From 468f30bcca18d0a5813c0710c1981397cbdc02df Mon Sep 17 00:00:00 2001 From: Avior Date: Sun, 26 Feb 2023 18:17:19 +0100 Subject: [PATCH] feat: add import/export functionality (#152) --- app/src/main/AndroidManifest.xml | 1 - .../com/dzeio/openhealth/data/AppDatabase.kt | 39 +++++++- .../ui/importexport/ImportExportFragment.kt | 98 +++++++++++++++++++ .../ui/importexport/ImportExportViewModel.kt | 11 +++ .../openhealth/ui/tests/TestsFragment.kt | 23 +---- .../com/dzeio/openhealth/utils/ZipFile.kt | 42 ++++++++ .../res/layout/fragment_import_export.xml | 47 +++++++++ app/src/main/res/menu/main.xml | 5 + .../main/res/navigation/mobile_navigation.xml | 5 + app/src/main/res/values-fr/strings.xml | 6 ++ app/src/main/res/values/strings.xml | 6 ++ 11 files changed, 259 insertions(+), 24 deletions(-) create mode 100644 app/src/main/java/com/dzeio/openhealth/ui/importexport/ImportExportFragment.kt create mode 100644 app/src/main/java/com/dzeio/openhealth/ui/importexport/ImportExportViewModel.kt create mode 100644 app/src/main/java/com/dzeio/openhealth/utils/ZipFile.kt create mode 100644 app/src/main/res/layout/fragment_import_export.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 0d268e5..ccac778 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -16,7 +16,6 @@ - ( + ImportExportViewModel::class.java +) { + override val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> FragmentImportExportBinding = + FragmentImportExportBinding::inflate + + /** + * Response to the import button event + */ + private val readResult = registerForActivityResult( + ActivityResultContracts.OpenDocument() + ) { + // stop if no document was chosen + if (it == null) { + return@registerForActivityResult + } + + // read the zipfile + val stream = requireActivity().contentResolver.openInputStream(it) + val zip = ZipInputStream(stream) + var entry = zip.nextEntry + + // copy each file to the database directory + while (entry != null) { + val direction = File(requireContext().getDatabasePath(AppDatabase.DATABASE_NAME).parent, entry.name) + Log.d("Pouet", entry.name) + + direction.writeBytes(zip.readBytes()) + entry = zip.nextEntry + } + + // Restart the app + val i = requireContext().packageManager.getLaunchIntentForPackage(requireContext().packageName) + i!!.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + requireContext().startActivity(i) + Toast.makeText(requireContext(), R.string.import_complete, Toast.LENGTH_LONG).show() + exitProcess(0) + } + + /** + * Response to the export button event + */ + private val writeResult = registerForActivityResult( + ActivityResultContracts.CreateDocument("application/zip") + ) { + + // stop if no location is given + if (it == null) { + return@registerForActivityResult + } + + // export the db to a zip + val bkp = viewModel.appDatabase.exportDatabase(requireContext()) + viewModel.appDatabase.checkpoint() + + // write file to location + requireActivity().contentResolver.openOutputStream(it)?.apply { + write(bkp) + close() + } + + Toast.makeText(requireContext(), R.string.export_complete, Toast.LENGTH_LONG).show() + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + binding.imports.setOnClickListener { + // ask user the location of the backup file + readResult.launch(arrayOf("application/zip")) + } + + binding.export.setOnClickListener { + // ask user the location to save the backup file + writeResult.launch("export.openhealth") + } + } +} diff --git a/app/src/main/java/com/dzeio/openhealth/ui/importexport/ImportExportViewModel.kt b/app/src/main/java/com/dzeio/openhealth/ui/importexport/ImportExportViewModel.kt new file mode 100644 index 0000000..66785c9 --- /dev/null +++ b/app/src/main/java/com/dzeio/openhealth/ui/importexport/ImportExportViewModel.kt @@ -0,0 +1,11 @@ +package com.dzeio.openhealth.ui.importexport + +import com.dzeio.openhealth.core.BaseViewModel +import com.dzeio.openhealth.data.AppDatabase +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject + +@HiltViewModel +class ImportExportViewModel @Inject internal constructor( + val appDatabase: AppDatabase +) : BaseViewModel() diff --git a/app/src/main/java/com/dzeio/openhealth/ui/tests/TestsFragment.kt b/app/src/main/java/com/dzeio/openhealth/ui/tests/TestsFragment.kt index e838924..f7ab5a8 100644 --- a/app/src/main/java/com/dzeio/openhealth/ui/tests/TestsFragment.kt +++ b/app/src/main/java/com/dzeio/openhealth/ui/tests/TestsFragment.kt @@ -5,7 +5,6 @@ import android.view.LayoutInflater import android.view.ViewGroup import com.dzeio.openhealth.core.BaseFragment import com.dzeio.openhealth.databinding.FragmentTestsBinding -import com.dzeio.openhealth.utils.Bluetooth import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint @@ -19,28 +18,8 @@ class TestsFragment : override fun onStart() { super.onStart() - // Bindings + // Test code below binding.button.setOnClickListener { - Bluetooth(requireContext()).apply { -// scanDevices(cb) - -// val device = DeviceMiSmartScale2(requireContext()) -// -// device.status.addObserver { -// Log.d("Device", "New device status $it") -// -// if (it == Device.ConnectionStatus.CONNECTED) { -// device.fetchWeights().addObserver { -// Log.i( -// "FetchStatus", -// "${(it.progress.toFloat() / it.progressMax.toFloat() * 100f).roundToInt()}% ${it.progress}/${it.progressMax}" -// ) -// } -// } -// } -// -// device.connect() - } } } } diff --git a/app/src/main/java/com/dzeio/openhealth/utils/ZipFile.kt b/app/src/main/java/com/dzeio/openhealth/utils/ZipFile.kt new file mode 100644 index 0000000..2555dd6 --- /dev/null +++ b/app/src/main/java/com/dzeio/openhealth/utils/ZipFile.kt @@ -0,0 +1,42 @@ +package com.dzeio.openhealth.utils + +import java.io.BufferedOutputStream +import java.io.ByteArrayOutputStream +import java.io.File +import java.io.IOException +import java.util.zip.ZipEntry +import java.util.zip.ZipOutputStream + +class ZipFile { + + private val stream = ByteArrayOutputStream() + private val output = ZipOutputStream(BufferedOutputStream(stream)) + + fun addFile(path: String, item: String) = addFile(path, item.toByteArray()) + fun addFile(path: String, item: File) { + // Read file + val data = item.inputStream() + val bytes = data.readBytes() + data.close() + + return addFile(path, bytes) + } + + fun addFile(path: String, item: ByteArray) { + val entry = ZipEntry(path) + try { + output.putNextEntry(entry) + + output.write(item) + } catch (e: IOException) { + e.printStackTrace() + } + + output.closeEntry() + } + + fun toByteArray(): ByteArray { + output.close() + return stream.toByteArray() + } +} diff --git a/app/src/main/res/layout/fragment_import_export.xml b/app/src/main/res/layout/fragment_import_export.xml new file mode 100644 index 0000000..fd5d28c --- /dev/null +++ b/app/src/main/res/layout/fragment_import_export.xml @@ -0,0 +1,47 @@ + + + + + + + + + +