Skip to content

Commit

Permalink
[ANDROAPP-2474] Import/Export database (#3503)
Browse files Browse the repository at this point in the history
Signed-off-by: Pablo <[email protected]>
  • Loading branch information
Balcan authored Mar 8, 2024
1 parent 9744ae7 commit 89c5875
Show file tree
Hide file tree
Showing 31 changed files with 1,150 additions and 206 deletions.
2 changes: 1 addition & 1 deletion app/src/dhis/java/org/dhis2/bindings/ContextExtensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ fun Context.buildInfo(): String {
return if (BuildConfig.BUILD_TYPE == "release") {
"v${BuildConfig.VERSION_NAME}"
} else {
"v${BuildConfig.VERSION_NAME} : ${BuildConfig.BUILD_DATE} : ${BuildConfig.GIT_SHA} "
"v${BuildConfig.VERSION_NAME} : ${BuildConfig.GIT_SHA} "
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ fun Context.buildInfo(): String {
return if (BuildConfig.BUILD_TYPE == "release") {
"v${BuildConfig.VERSION_NAME}"
} else {
"v${BuildConfig.VERSION_NAME} : ${BuildConfig.BUILD_DATE} : ${BuildConfig.GIT_SHA} "
"v${BuildConfig.VERSION_NAME} : ${BuildConfig.GIT_SHA} "
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ fun Context.buildInfo(): String {
return if (BuildConfig.BUILD_TYPE == "release") {
"v${BuildConfig.VERSION_NAME}"
} else {
"v${BuildConfig.VERSION_NAME} : ${BuildConfig.BUILD_DATE} : ${BuildConfig.GIT_SHA} "
"v${BuildConfig.VERSION_NAME} : ${BuildConfig.GIT_SHA} "
}
}

Expand Down
4 changes: 2 additions & 2 deletions app/src/main/java/org/dhis2/App.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import android.content.Context;
import android.os.Looper;
import android.os.StrictMode;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
Expand Down Expand Up @@ -108,15 +109,14 @@ public class App extends MultiDexApplication implements Components, LifecycleObs
public void onCreate() {
super.onCreate();


ProcessLifecycleOwner.get().getLifecycle().addObserver(this);

appInspector = new AppInspector(this).init();

MapController.Companion.init(this);

setUpAppComponent();
if(BuildConfig.DEBUG){
if (BuildConfig.DEBUG) {
Timber.plant(new DebugTree());
}

Expand Down
71 changes: 64 additions & 7 deletions app/src/main/java/org/dhis2/usescases/login/LoginActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,15 @@ import android.text.TextWatcher
import android.util.Patterns
import android.view.View
import android.view.WindowManager
import android.webkit.MimeTypeMap
import android.webkit.URLUtil
import android.widget.ArrayAdapter
import androidx.activity.result.ActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.databinding.DataBindingUtil
import com.google.android.material.composethemeadapter.MdcTheme
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
Expand Down Expand Up @@ -42,6 +46,7 @@ import org.dhis2.usescases.general.ActivityGlobalAbstract
import org.dhis2.usescases.login.accounts.AccountsActivity
import org.dhis2.usescases.login.auth.AuthServiceModel
import org.dhis2.usescases.login.auth.OpenIdProviders
import org.dhis2.usescases.login.ui.LoginTopBar
import org.dhis2.usescases.main.MainActivity
import org.dhis2.usescases.qrScanner.ScanActivity
import org.dhis2.usescases.sync.SyncActivity
Expand All @@ -56,6 +61,9 @@ import org.dhis2.utils.session.PinDialog
import org.hisp.dhis.android.core.user.openid.IntentWithRequestCode
import timber.log.Timber
import java.io.BufferedReader
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.io.InputStreamReader
import java.io.StringWriter
import javax.inject.Inject
Expand Down Expand Up @@ -89,6 +97,38 @@ class LoginActivity : ActivityGlobalAbstract(), LoginContracts.View {
private var skipSync = false
private var openIDRequestCode = -1

private val filePickerLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
result.data?.data?.let { uri ->
val fileType = with(contentResolver) {
MimeTypeMap.getSingleton().getExtensionFromMimeType(getType(uri))
}
val file = File.createTempFile("importedDb", fileType)
val inputStream = contentResolver.openInputStream(uri)!!
try {
FileOutputStream(file, false).use { outputStream ->
var read: Int
val bytes = ByteArray(DEFAULT_BUFFER_SIZE)
while (inputStream.read(bytes).also { read = it } != -1) {
outputStream.write(bytes, 0, read)
}
}
} catch (e: IOException) {
Timber.e("Failed to load file: ", e.message.toString())
}
if (file.exists()) {
presenter.onImportDataBase(file)
}
}
}

override fun onDbImportFinished(isSuccess: Boolean) {
showLoginProgress(false)
if (isSuccess) {
blockLoginInfo()
}
}

companion object {
fun bundle(
skipSync: Boolean = false,
Expand All @@ -106,6 +146,7 @@ class LoginActivity : ActivityGlobalAbstract(), LoginContracts.View {
EXTRA_ACCOUNT_DISABLED,
true,
)

null -> {
// Nothing to do in this case
}
Expand Down Expand Up @@ -149,6 +190,23 @@ class LoginActivity : ActivityGlobalAbstract(), LoginContracts.View {

binding = DataBindingUtil.setContentView(this, R.layout.activity_login)

binding.topbar.setContent {
val displayMoreActions by presenter.displayMoreActions().observeAsState(true)
MdcTheme {
LoginTopBar(
version = buildInfo(),
displayMoreActions = displayMoreActions,
onImportDatabase = {
showLoginProgress(false, getString(R.string.importing_database))
val intent = Intent()
intent.type = "*/*"
intent.action = Intent.ACTION_GET_CONTENT
filePickerLauncher.launch(intent)
},
)
}
}

binding.presenter = presenter
setLoginVisibility(false)

Expand Down Expand Up @@ -194,12 +252,11 @@ class LoginActivity : ActivityGlobalAbstract(), LoginContracts.View {
binding.clearUrl.setOnClickListener { binding.serverUrlEdit.text = null }

presenter.loginProgressVisible.observe(this) { show ->
showLoginProgress(show)
showLoginProgress(show, getString(R.string.authenticating))
}

setTestingCredentials()
setAutocompleteAdapters()
setUpLoginInfo()
checkMessage()
presenter.apply {
checkServerInfoAndShowBiometricButton()
Expand Down Expand Up @@ -294,7 +351,7 @@ class LoginActivity : ActivityGlobalAbstract(), LoginContracts.View {
binding.login.isEnabled = isVisible
}

private fun showLoginProgress(showLogin: Boolean) {
private fun showLoginProgress(showLogin: Boolean, message: String? = null) {
if (showLogin) {
window.setFlags(
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
Expand All @@ -304,6 +361,7 @@ class LoginActivity : ActivityGlobalAbstract(), LoginContracts.View {
binding.progressLayout.visibility = View.VISIBLE
} else {
window.clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
binding.progressMessage.text = message
binding.credentialLayout.visibility = View.VISIBLE
binding.progressLayout.visibility = View.GONE
}
Expand Down Expand Up @@ -388,6 +446,7 @@ class LoginActivity : ActivityGlobalAbstract(), LoginContracts.View {
handleFingerPrint()
goToNextScreen()
}

else -> goToNextScreen()
}
}
Expand Down Expand Up @@ -490,6 +549,7 @@ class LoginActivity : ActivityGlobalAbstract(), LoginContracts.View {
binding.userNameEdit.isEnabled = true
binding.clearUrl.visibility = View.VISIBLE
binding.clearUserNameButton.visibility = View.VISIBLE
presenter.setDisplayMoreActions(true)
}

private fun blockLoginInfo() {
Expand All @@ -499,6 +559,7 @@ class LoginActivity : ActivityGlobalAbstract(), LoginContracts.View {
binding.userNameEdit.isEnabled = false
binding.clearUrl.visibility = View.GONE
binding.clearUserNameButton.visibility = View.GONE
presenter.setDisplayMoreActions(false)
}

/*
Expand Down Expand Up @@ -544,10 +605,6 @@ class LoginActivity : ActivityGlobalAbstract(), LoginContracts.View {
requestQRScanner.launch(Intent(context, ScanActivity::class.java))
}

private fun setUpLoginInfo() {
binding.appBuildInfo.text = buildInfo()
}

override fun getDefaultServerProtocol(): String = getString(R.string.login_https)

private fun showLoginOptions(authServiceModel: AuthServiceModel?) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,6 @@ class LoginContracts {
fun openAccountsActivity()
fun showNoConnectionDialog()
fun initLogin(): UserManager?
fun onDbImportFinished(isSuccess: Boolean)
}
}
6 changes: 6 additions & 0 deletions app/src/main/java/org/dhis2/usescases/login/LoginModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import org.dhis2.commons.di.dagger.PerActivity
import org.dhis2.commons.network.NetworkUtils
import org.dhis2.commons.prefs.PreferenceProvider
import org.dhis2.commons.reporting.CrashReportController
import org.dhis2.commons.resources.ResourceManager
import org.dhis2.commons.schedulers.SchedulerProvider
import org.dhis2.commons.viewmodel.DispatcherProvider
import org.dhis2.data.fingerprint.FingerPrintController
import org.dhis2.data.server.UserManager
import org.dhis2.usescases.login.auth.OpenIdProviders
Expand All @@ -26,7 +28,9 @@ class LoginModule(
@PerActivity
fun providePresenter(
preferenceProvider: PreferenceProvider,
resourceManager: ResourceManager,
schedulerProvider: SchedulerProvider,
dispatcherProvider: DispatcherProvider,
fingerPrintController: FingerPrintController,
analyticsHelper: AnalyticsHelper,
crashReportController: CrashReportController,
Expand All @@ -37,7 +41,9 @@ class LoginModule(
LoginViewModelFactory(
view,
preferenceProvider,
resourceManager,
schedulerProvider,
dispatcherProvider,
fingerPrintController,
analyticsHelper,
crashReportController,
Expand Down
56 changes: 52 additions & 4 deletions app/src/main/java/org/dhis2/usescases/login/LoginViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@ import androidx.annotation.VisibleForTesting
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import io.reactivex.Observable
import io.reactivex.disposables.CompositeDisposable
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import org.dhis2.commons.Constants.PREFS_URLS
import org.dhis2.commons.Constants.PREFS_USERS
import org.dhis2.commons.Constants.USER_TEST_ANDROID
Expand All @@ -22,7 +25,9 @@ import org.dhis2.commons.prefs.SECURE_PASS
import org.dhis2.commons.prefs.SECURE_SERVER_URL
import org.dhis2.commons.prefs.SECURE_USER_NAME
import org.dhis2.commons.reporting.CrashReportController
import org.dhis2.commons.resources.ResourceManager
import org.dhis2.commons.schedulers.SchedulerProvider
import org.dhis2.commons.viewmodel.DispatcherProvider
import org.dhis2.data.fingerprint.FingerPrintController
import org.dhis2.data.fingerprint.Type
import org.dhis2.data.server.UserManager
Expand All @@ -41,13 +46,16 @@ import org.hisp.dhis.android.core.systeminfo.SystemInfo
import org.hisp.dhis.android.core.user.openid.OpenIDConnectConfig
import retrofit2.Response
import timber.log.Timber
import java.io.File

const val VERSION = "version"

class LoginViewModel(
private val view: LoginContracts.View,
private val preferenceProvider: PreferenceProvider,
private val resourceManager: ResourceManager,
private val schedulers: SchedulerProvider,
private val dispatchers: DispatcherProvider,
private val fingerPrintController: FingerPrintController,
private val analyticsHelper: AnalyticsHelper,
private val crashReportController: CrashReportController,
Expand All @@ -69,6 +77,12 @@ class LoginViewModel(
private val _loginProgressVisible = MutableLiveData(false)
val loginProgressVisible: LiveData<Boolean> = _loginProgressVisible

private val _hasAccounts = MutableLiveData<Boolean>()
val hasAccounts: LiveData<Boolean> = _hasAccounts

private val _displayMoreActions = MutableLiveData<Boolean>(true)
val displayMoreActions: LiveData<Boolean> = _displayMoreActions

init {
this.userManager?.let {
disposable.add(
Expand Down Expand Up @@ -103,6 +117,7 @@ class LoginViewModel(
),
)
} ?: view.setUrl(view.getDefaultServerProtocol())
displayManageAccount()
}

private fun trackServerVersion() {
Expand Down Expand Up @@ -462,9 +477,9 @@ class LoginViewModel(
return Pair(urls, users)
}

fun displayManageAccount(): Boolean {
fun displayManageAccount() {
val users = userManager?.d2?.userModule()?.accountManager()?.getAccounts()?.count() ?: 0
return users >= 1
_hasAccounts.value = (users >= 1)
}

fun onManageAccountClicked() {
Expand Down Expand Up @@ -548,7 +563,40 @@ class LoginViewModel(
this.userName.value = userName
}

fun testCoverage() {
view.setUser("Coverage test")
fun onImportDataBase(file: File) {
userManager?.let {
viewModelScope.launch {
val resultJob = async {
try {
val importedMetadata =
it.d2.maintenanceModule().databaseImportExport().importDatabase(file)
Result.success(importedMetadata)
} catch (e: Exception) {
Result.failure(e)
}
}

val result = resultJob.await()

result.fold(
onSuccess = {
setAccountInfo(it.serverUrl, it.username)
view.setUrl(it.serverUrl)
view.setUser(it.username)
displayManageAccount()
},
onFailure = {
view.displayMessage(resourceManager.parseD2Error(it))
},
)

view.onDbImportFinished(result.isSuccess)
}
}
}

fun displayMoreActions() = displayMoreActions
fun setDisplayMoreActions(shouldDisplayMoreActions: Boolean) {
_displayMoreActions.postValue(shouldDisplayMoreActions)
}
}
Loading

0 comments on commit 89c5875

Please sign in to comment.