Preferences Encryption - export as JSON
This commit is contained in:
parent
f4b7f642c9
commit
fde84207ab
|
@ -36,6 +36,7 @@ import info.nightscout.androidaps.plugins.general.automation.actions.*
|
|||
import info.nightscout.androidaps.plugins.general.automation.elements.*
|
||||
import info.nightscout.androidaps.plugins.general.automation.triggers.*
|
||||
import info.nightscout.androidaps.plugins.general.overview.graphData.GraphData
|
||||
import info.nightscout.androidaps.plugins.general.maintenance.ImportExportPrefs
|
||||
import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationWithAction
|
||||
import info.nightscout.androidaps.plugins.general.smsCommunicator.AuthRequest
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensData
|
||||
|
@ -249,6 +250,8 @@ open class AppModule {
|
|||
|
||||
@ContributesAndroidInjector fun graphDataInjector(): GraphData
|
||||
|
||||
@ContributesAndroidInjector fun importExportPrefsInjector(): ImportExportPrefs
|
||||
|
||||
@Binds fun bindContext(mainApp: MainApp): Context
|
||||
@Binds fun bindInjector(mainApp: MainApp): HasAndroidInjector
|
||||
|
||||
|
|
|
@ -1,129 +0,0 @@
|
|||
package info.nightscout.androidaps.plugins.general.maintenance;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Environment;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Map;
|
||||
|
||||
import info.nightscout.androidaps.MainApp;
|
||||
import info.nightscout.androidaps.R;
|
||||
import info.nightscout.androidaps.events.EventAppExit;
|
||||
import info.nightscout.androidaps.logging.L;
|
||||
import info.nightscout.androidaps.logging.StacktraceLoggerWrapper;
|
||||
import info.nightscout.androidaps.plugins.bus.RxBus;
|
||||
import info.nightscout.androidaps.utils.OKDialog;
|
||||
import info.nightscout.androidaps.utils.SP;
|
||||
import info.nightscout.androidaps.utils.ToastUtils;
|
||||
|
||||
/**
|
||||
* Created by mike on 03.07.2016.
|
||||
*/
|
||||
|
||||
public class ImportExportPrefs {
|
||||
private static Logger log = StacktraceLoggerWrapper.getLogger(L.CORE);
|
||||
private static File path = new File(Environment.getExternalStorageDirectory().toString());
|
||||
static public final File file = new File(path, MainApp.gs(R.string.app_name) + "Preferences");
|
||||
|
||||
private static final int REQUEST_EXTERNAL_STORAGE = 1;
|
||||
private static String[] PERMISSIONS_STORAGE = {
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
};
|
||||
|
||||
static void verifyStoragePermissions(Fragment fragment) {
|
||||
int permission = ContextCompat.checkSelfPermission(fragment.getContext(),
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE);
|
||||
|
||||
if (permission != PackageManager.PERMISSION_GRANTED) {
|
||||
// We don't have permission so prompt the user
|
||||
fragment.requestPermissions(PERMISSIONS_STORAGE, REQUEST_EXTERNAL_STORAGE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void exportSharedPreferences(final Fragment f) {
|
||||
exportSharedPreferences(f.getContext());
|
||||
}
|
||||
|
||||
private static void exportSharedPreferences(final Context context) {
|
||||
OKDialog.showConfirmation(context, MainApp.gs(R.string.maintenance), MainApp.gs(R.string.export_to) + " " + file + " ?", () -> {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
try {
|
||||
FileWriter fw = new FileWriter(file);
|
||||
PrintWriter pw = new PrintWriter(fw);
|
||||
Map<String, ?> prefsMap = prefs.getAll();
|
||||
for (Map.Entry<String, ?> entry : prefsMap.entrySet()) {
|
||||
pw.println(entry.getKey() + "::" + entry.getValue().toString());
|
||||
}
|
||||
pw.close();
|
||||
fw.close();
|
||||
ToastUtils.showToastInUiThread(context, MainApp.gs(R.string.exported));
|
||||
} catch (FileNotFoundException e) {
|
||||
ToastUtils.showToastInUiThread(context, MainApp.gs(R.string.filenotfound) + " " + file);
|
||||
log.error("Unhandled exception", e);
|
||||
} catch (IOException e) {
|
||||
log.error("Unhandled exception", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void importSharedPreferences(final Fragment fragment) {
|
||||
importSharedPreferences(fragment.getContext());
|
||||
}
|
||||
|
||||
public static void importSharedPreferences(final Context context) {
|
||||
OKDialog.showConfirmation(context, MainApp.gs(R.string.maintenance), MainApp.gs(R.string.import_from) + " " + file + " ?", () -> {
|
||||
String line;
|
||||
String[] lineParts;
|
||||
try {
|
||||
SP.clear();
|
||||
|
||||
BufferedReader reader = new BufferedReader(new FileReader(file));
|
||||
while ((line = reader.readLine()) != null) {
|
||||
lineParts = line.split("::");
|
||||
if (lineParts.length == 2) {
|
||||
if (lineParts[1].equals("true") || lineParts[1].equals("false")) {
|
||||
SP.putBoolean(lineParts[0], Boolean.parseBoolean(lineParts[1]));
|
||||
} else {
|
||||
SP.putString(lineParts[0], lineParts[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
reader.close();
|
||||
SP.putBoolean(R.string.key_setupwizard_processed, true);
|
||||
OKDialog.show(context, MainApp.gs(R.string.setting_imported), MainApp.gs(R.string.restartingapp), () -> {
|
||||
log.debug("Exiting");
|
||||
RxBus.Companion.getINSTANCE().send(new EventAppExit());
|
||||
if (context instanceof Activity) {
|
||||
((Activity) context).finish();
|
||||
}
|
||||
System.runFinalization();
|
||||
System.exit(0);
|
||||
});
|
||||
} catch (FileNotFoundException e) {
|
||||
ToastUtils.showToastInUiThread(context, MainApp.gs(R.string.filenotfound) + " " + file);
|
||||
log.error("Unhandled exception", e);
|
||||
} catch (IOException e) {
|
||||
log.error("Unhandled exception", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,142 @@
|
|||
package info.nightscout.androidaps.plugins.general.maintenance
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Environment
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.events.EventAppExit
|
||||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
|
||||
import info.nightscout.androidaps.plugins.general.maintenance.formats.*
|
||||
import info.nightscout.androidaps.utils.OKDialog.show
|
||||
import info.nightscout.androidaps.utils.OKDialog.showConfirmation
|
||||
import info.nightscout.androidaps.utils.ToastUtils
|
||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||
import java.io.File
|
||||
import java.io.FileNotFoundException
|
||||
import java.io.IOException
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
* Created by mike on 03.07.2016.
|
||||
*/
|
||||
|
||||
private const val REQUEST_EXTERNAL_STORAGE = 1
|
||||
private val PERMISSIONS_STORAGE = arrayOf(
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
)
|
||||
|
||||
@Singleton
|
||||
class ImportExportPrefs @Inject constructor (
|
||||
private var log: AAPSLogger,
|
||||
private val resourceHelper: ResourceHelper,
|
||||
private val sp : SP,
|
||||
private val rxBus: RxBusWrapper
|
||||
)
|
||||
{
|
||||
|
||||
val TAG = LTag.CORE
|
||||
|
||||
private val path = File(Environment.getExternalStorageDirectory().toString())
|
||||
|
||||
private val file = File(path, resourceHelper.gs(R.string.app_name) + "Preferences")
|
||||
private val encFile = File(path, resourceHelper.gs(R.string.app_name) + "Preferences.json")
|
||||
|
||||
fun prefsImportFile() : File {
|
||||
return if (encFile.exists()) encFile else file
|
||||
}
|
||||
|
||||
fun prefsFileExists() : Boolean {
|
||||
return encFile.exists() || file.exists()
|
||||
}
|
||||
|
||||
fun exportSharedPreferences(f: Fragment) {
|
||||
exportSharedPreferences(f.context)
|
||||
}
|
||||
|
||||
fun verifyStoragePermissions(fragment: Fragment) {
|
||||
val permission = ContextCompat.checkSelfPermission(fragment.context!!,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
if (permission != PackageManager.PERMISSION_GRANTED) {
|
||||
// We don't have permission so prompt the user
|
||||
fragment.requestPermissions(PERMISSIONS_STORAGE, REQUEST_EXTERNAL_STORAGE)
|
||||
}
|
||||
}
|
||||
|
||||
private fun exportSharedPreferences(context: Context?) {
|
||||
showConfirmation(context!!, resourceHelper.gs(R.string.maintenance), resourceHelper.gs(R.string.export_to) + " " + encFile + " ?", Runnable {
|
||||
try {
|
||||
val entries: MutableMap<String, String> = mutableMapOf()
|
||||
for ((key, value) in sp.getAll()) {
|
||||
entries[key] = value.toString()
|
||||
}
|
||||
|
||||
val prefs = Prefs(entries, mapOf())
|
||||
|
||||
ClassicPrefsFormat.savePreferences(file, prefs)
|
||||
EncryptedPrefsFormat.savePreferences(encFile, prefs)
|
||||
|
||||
ToastUtils.showToastInUiThread(context, resourceHelper.gs(R.string.exported))
|
||||
} catch (e: FileNotFoundException) {
|
||||
ToastUtils.showToastInUiThread(context, resourceHelper.gs(R.string.filenotfound) + " " + encFile)
|
||||
log.error(TAG,"Unhandled exception", e)
|
||||
} catch (e: IOException) {
|
||||
log.error(TAG,"Unhandled exception", e)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun importSharedPreferences(fragment: Fragment) {
|
||||
importSharedPreferences(fragment.context)
|
||||
}
|
||||
|
||||
fun importSharedPreferences(context: Context?) {
|
||||
|
||||
val importFile = prefsImportFile()
|
||||
|
||||
showConfirmation(context!!, resourceHelper.gs(R.string.maintenance), resourceHelper.gs(R.string.import_from) + " " + importFile + " ?", Runnable {
|
||||
|
||||
val format : PrefsFormat = if (encFile.exists()) EncryptedPrefsFormat else ClassicPrefsFormat
|
||||
|
||||
try {
|
||||
val prefs = format.loadPreferences(importFile)
|
||||
|
||||
sp.clear()
|
||||
for ((key, value) in prefs.values) {
|
||||
if (value == "true" || value == "false") {
|
||||
sp.putBoolean(key, value.toBoolean())
|
||||
} else {
|
||||
sp.putString(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
sp.putBoolean(R.string.key_setupwizard_processed, true)
|
||||
show(context, resourceHelper.gs(R.string.setting_imported), resourceHelper.gs(R.string.restartingapp), Runnable {
|
||||
log.debug(TAG,"Exiting")
|
||||
rxBus.send(EventAppExit())
|
||||
if (context is Activity) {
|
||||
context.finish()
|
||||
}
|
||||
System.runFinalization()
|
||||
System.exit(0)
|
||||
})
|
||||
|
||||
|
||||
} catch (e: PrefFileNotFoundError) {
|
||||
ToastUtils.showToastInUiThread(context, resourceHelper.gs(R.string.filenotfound) + " " + importFile)
|
||||
log.error(TAG,"Unhandled exception", e)
|
||||
} catch (e: PrefIOError) {
|
||||
log.error(TAG,"Unhandled exception", e)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
|
@ -23,6 +23,7 @@ class MaintenanceFragment : DaggerFragment() {
|
|||
@Inject lateinit var resourceHelper: ResourceHelper
|
||||
@Inject lateinit var treatmentsPlugin: TreatmentsPlugin
|
||||
@Inject lateinit var foodPlugin: FoodPlugin
|
||||
@Inject lateinit var importExportPrefs: ImportExportPrefs
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.maintenance_fragment, container, false)
|
||||
|
@ -45,13 +46,13 @@ class MaintenanceFragment : DaggerFragment() {
|
|||
}
|
||||
nav_export.setOnClickListener {
|
||||
// start activity for checking permissions...
|
||||
ImportExportPrefs.verifyStoragePermissions(this)
|
||||
ImportExportPrefs.exportSharedPreferences(this)
|
||||
importExportPrefs.verifyStoragePermissions(this)
|
||||
importExportPrefs.exportSharedPreferences(this)
|
||||
}
|
||||
nav_import.setOnClickListener {
|
||||
// start activity for checking permissions...
|
||||
ImportExportPrefs.verifyStoragePermissions(this)
|
||||
ImportExportPrefs.importSharedPreferences(this)
|
||||
importExportPrefs.verifyStoragePermissions(this)
|
||||
importExportPrefs.importSharedPreferences(this)
|
||||
}
|
||||
nav_logsettings.setOnClickListener { startActivity(Intent(activity, LogSettingActivity::class.java)) }
|
||||
}
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
package info.nightscout.androidaps.plugins.general.maintenance.formats
|
||||
|
||||
import info.nightscout.androidaps.plugins.general.maintenance.ImportExportPrefs
|
||||
import java.io.*
|
||||
|
||||
|
||||
object ClassicPrefsFormat : PrefsFormat {
|
||||
|
||||
const val FORMAT_KEY = "old"
|
||||
|
||||
override fun savePreferences(file:File, prefs: Prefs) {
|
||||
try {
|
||||
val fw = FileWriter(file)
|
||||
val pw = PrintWriter(fw)
|
||||
for ((key, value) in prefs.values) {
|
||||
pw.println(key + "::" + value)
|
||||
}
|
||||
pw.close()
|
||||
fw.close()
|
||||
} catch (e: FileNotFoundException) {
|
||||
throw PrefFileNotFoundError(file.absolutePath)
|
||||
} catch (e: IOException) {
|
||||
throw PrefIOError(file.absolutePath)
|
||||
}
|
||||
}
|
||||
|
||||
override fun loadPreferences(file:File): Prefs {
|
||||
var line: String
|
||||
var lineParts: Array<String>
|
||||
val entries: MutableMap<String, String> = mutableMapOf()
|
||||
val metadata: MutableMap<PrefsMetadataKey, PrefMetadata> = mutableMapOf()
|
||||
try {
|
||||
val reader = BufferedReader(FileReader(file))
|
||||
while (reader.readLine().also { line = it } != null) {
|
||||
lineParts = line.split("::").toTypedArray()
|
||||
if (lineParts.size == 2) {
|
||||
entries[lineParts[0]] = lineParts[1]
|
||||
}
|
||||
}
|
||||
reader.close()
|
||||
|
||||
metadata[PrefsMetadataKey.FILE_FORMAT] = PrefMetadata(FORMAT_KEY, PrefsStatus.WARN)
|
||||
|
||||
return Prefs(entries, metadata)
|
||||
|
||||
} catch (e: FileNotFoundException) {
|
||||
throw PrefFileNotFoundError(file.absolutePath)
|
||||
} catch (e: IOException) {
|
||||
throw PrefIOError(file.absolutePath)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package info.nightscout.androidaps.plugins.general.maintenance.formats
|
||||
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
import java.io.File
|
||||
import java.io.FileNotFoundException
|
||||
import java.io.IOException
|
||||
|
||||
object EncryptedPrefsFormat : PrefsFormat {
|
||||
|
||||
const val FORMAT_KEY = "new_v1"
|
||||
|
||||
override fun savePreferences(file:File, prefs: Prefs) {
|
||||
|
||||
val container = JSONObject()
|
||||
|
||||
try {
|
||||
for ((key, value) in prefs.values) {
|
||||
container.put(key, value)
|
||||
}
|
||||
|
||||
file.writeText(container.toString(2));
|
||||
|
||||
} catch (e: FileNotFoundException) {
|
||||
throw PrefFileNotFoundError(file.absolutePath)
|
||||
} catch (e: IOException) {
|
||||
throw PrefIOError(file.absolutePath)
|
||||
}
|
||||
}
|
||||
|
||||
override fun loadPreferences(file:File): Prefs {
|
||||
|
||||
val entries: MutableMap<String, String> = mutableMapOf()
|
||||
val metadata: MutableMap<PrefsMetadataKey, PrefMetadata> = mutableMapOf()
|
||||
try {
|
||||
|
||||
val jsonBody = file.readText()
|
||||
val container = JSONObject(jsonBody)
|
||||
|
||||
for (key in container.keys()) {
|
||||
entries.put(key, container[key].toString())
|
||||
}
|
||||
|
||||
metadata[PrefsMetadataKey.FILE_FORMAT] = PrefMetadata(FORMAT_KEY, PrefsStatus.OK)
|
||||
|
||||
return Prefs(entries, metadata)
|
||||
|
||||
} catch (e: FileNotFoundException) {
|
||||
throw PrefFileNotFoundError(file.absolutePath)
|
||||
} catch (e: IOException) {
|
||||
throw PrefIOError(file.absolutePath)
|
||||
} catch (e: JSONException){
|
||||
throw PrefFormatError("Mallformed preferences JSON file: "+e)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package info.nightscout.androidaps.plugins.general.maintenance.formats
|
||||
|
||||
import java.io.File
|
||||
|
||||
enum class PrefsMetadataKey(val key: String) {
|
||||
FILE_FORMAT("fileFormat")
|
||||
}
|
||||
|
||||
data class PrefMetadata(var value : String, var status : PrefsStatus)
|
||||
|
||||
data class Prefs(val values : Map<String, String>, val metadata : Map<PrefsMetadataKey, PrefMetadata>)
|
||||
|
||||
interface PrefsFormat {
|
||||
fun savePreferences(file: File, prefs: Prefs)
|
||||
fun loadPreferences(file: File) : Prefs
|
||||
}
|
||||
|
||||
enum class PrefsStatus {
|
||||
OK,
|
||||
WARN,
|
||||
ERROR
|
||||
}
|
||||
|
||||
class PrefFileNotFoundError(message: String) : Exception(message)
|
||||
class PrefIOError(message: String) : Exception(message)
|
||||
class PrefFormatError(message: String) : Exception(message)
|
|
@ -59,6 +59,7 @@ class SWDefinition @Inject constructor(
|
|||
private val nsClientPlugin: NSClientPlugin,
|
||||
private val nsProfilePlugin: NSProfilePlugin,
|
||||
private val protectionCheck: ProtectionCheck,
|
||||
private val importExportPrefs: ImportExportPrefs,
|
||||
private val androidPermission: AndroidPermission
|
||||
) {
|
||||
|
||||
|
@ -160,8 +161,8 @@ class SWDefinition @Inject constructor(
|
|||
.add(SWBreak(injector))
|
||||
.add(SWButton(injector)
|
||||
.text(R.string.nav_import)
|
||||
.action(Runnable { ImportExportPrefs.importSharedPreferences(activity) }))
|
||||
.visibility(SWValidator { ImportExportPrefs.file.exists() && !androidPermission.permissionNotGranted(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) })
|
||||
.action(Runnable { importExportPrefs.importSharedPreferences(activity) }))
|
||||
.visibility(SWValidator { importExportPrefs.prefsFileExists() && !androidPermission.permissionNotGranted(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) })
|
||||
private val screenNsClient = SWScreen(injector, R.string.nsclientinternal_title)
|
||||
.skippable(true)
|
||||
.add(SWInfotext(injector)
|
||||
|
|
Loading…
Reference in a new issue