diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,5 +1,6 @@ - + @@ -9,10 +10,11 @@ - - - + + @@ -41,6 +43,12 @@ android:networkSecurityConfig="@xml/networks_security_config" android:roundIcon="@mipmap/ic_launcher_round" android:theme="@style/AppTheme.NoActionBar"> + diff --git a/app/src/main/java/it/reyboz/bustorino/ActivityBackup.kt b/app/src/main/java/it/reyboz/bustorino/ActivityBackup.kt new file mode 100644 --- /dev/null +++ b/app/src/main/java/it/reyboz/bustorino/ActivityBackup.kt @@ -0,0 +1,26 @@ +package it.reyboz.bustorino + +import android.os.Bundle +import it.reyboz.bustorino.fragments.BackupImportFragment +import it.reyboz.bustorino.middleware.GeneralActivity + +class ActivityBackup : GeneralActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_container_fragment) + + val actionBar = supportActionBar + if (actionBar != null) { + actionBar.setDisplayHomeAsUpEnabled(true) + actionBar.setIcon(R.drawable.ic_launcher) + actionBar.show() + } + if (savedInstanceState == null) { + supportFragmentManager.beginTransaction() + .setReorderingAllowed(true) + .add(R.id.fragment_container_view, BackupImportFragment::class.java, null) + .commit() + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/it/reyboz/bustorino/ActivityExperiments.java b/app/src/main/java/it/reyboz/bustorino/ActivityExperiments.java --- a/app/src/main/java/it/reyboz/bustorino/ActivityExperiments.java +++ b/app/src/main/java/it/reyboz/bustorino/ActivityExperiments.java @@ -32,7 +32,7 @@ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.activity_experiments); + setContentView(R.layout.activity_container_fragment); ActionBar actionBar = getSupportActionBar(); if (actionBar != null) { @@ -54,7 +54,7 @@ //.add(R.id.fragment_container_view, LinesDetailFragment.class, // LinesDetailFragment.Companion.makeArgs("gtt:4U")) - .add(R.id.fragment_container_view, TestSavingFragment.class, null) + .add(R.id.fragment_container_view, BackupImportFragment.class, null) .commit(); } } diff --git a/app/src/main/java/it/reyboz/bustorino/backend/mato/MQTTMatoClient.kt b/app/src/main/java/it/reyboz/bustorino/backend/mato/MQTTMatoClient.kt --- a/app/src/main/java/it/reyboz/bustorino/backend/mato/MQTTMatoClient.kt +++ b/app/src/main/java/it/reyboz/bustorino/backend/mato/MQTTMatoClient.kt @@ -24,7 +24,7 @@ private var isStarted = false private var subscribedToAll = false - private lateinit var client: MqttAndroidClient + private var client: MqttAndroidClient? = null //private var clientID = "" private val respondersMap = HashMap>>() @@ -66,9 +66,9 @@ Log.d(DEBUG_TAG,"client name: $clientID") //actually connect - client.connect(options,null, iMqttActionListener) + client!!.connect(options,null, iMqttActionListener) isStarted = true - client.setCallback(this) + client!!.setCallback(this) if (this.context ==null) this.context = context.applicationContext @@ -92,8 +92,8 @@ disconnectedBufferOptions.bufferSize = 100 disconnectedBufferOptions.isPersistBuffer = false disconnectedBufferOptions.isDeleteOldestMessages = false - client.setBufferOpts(disconnectedBufferOptions) - client.subscribe(topic, QoS.AtMostOnce.value) + client!!.setBufferOpts(disconnectedBufferOptions) + client!!.subscribe(topic, QoS.AtMostOnce.value) isStarted = true } @@ -122,7 +122,7 @@ connectTopic(topic) //wait for connection } else { - client.subscribe(topic, QoS.AtMostOnce.value) + client!!.subscribe(topic, QoS.AtMostOnce.value) } } @@ -156,7 +156,7 @@ //if (done) break if (v.isEmpty()){ //actually unsubscribe - client.unsubscribe( mapTopic(line)) + client!!.unsubscribe( mapTopic(line)) } removed = done || removed } @@ -227,7 +227,7 @@ if(elms.isEmpty()) respondersMap.remove(line) else { - client.subscribe(topic, QoS.AtMostOnce.value, null, null) + client!!.subscribe(topic, QoS.AtMostOnce.value, null, null) Log.d(DEBUG_TAG, "Resubscribed with topic $topic") } } @@ -327,7 +327,7 @@ //try unsubscribing to all if(emptyResp) { Log.d(DEBUG_TAG, "Unsubscribe all") - client.unsubscribe(LINES_ALL) + client!!.unsubscribe(LINES_ALL) } } //Log.d(DEBUG_TAG, "We have update on line $lineId, vehicle $vehicleId") @@ -353,7 +353,7 @@ }*/ fun disconnect(){ - client.disconnect() + client?.disconnect() } diff --git a/app/src/main/java/it/reyboz/bustorino/data/PreferencesHolder.java b/app/src/main/java/it/reyboz/bustorino/data/PreferencesHolder.java --- a/app/src/main/java/it/reyboz/bustorino/data/PreferencesHolder.java +++ b/app/src/main/java/it/reyboz/bustorino/data/PreferencesHolder.java @@ -39,6 +39,7 @@ public static final String DB_LAST_UPDATE_KEY = "NextGenDB.LastDBUpdate"; public static final String PREF_FAVORITE_LINES = "pref_favorite_lines"; + public static final Set KEYS_MERGE_SET = Set.of(PREF_FAVORITE_LINES); public static final Set IGNORE_KEYS_LOAD_MAIN = Set.of(PREF_GTFS_DB_VERSION, PREF_INTRO_ACTIVITY_RUN, DB_GTT_VERSION_KEY, DB_LAST_UPDATE_KEY); public static SharedPreferences getMainSharedPreferences(Context context){ diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/TestSavingFragment.kt b/app/src/main/java/it/reyboz/bustorino/fragments/BackupImportFragment.kt rename from app/src/main/java/it/reyboz/bustorino/fragments/TestSavingFragment.kt rename to app/src/main/java/it/reyboz/bustorino/fragments/BackupImportFragment.kt --- a/app/src/main/java/it/reyboz/bustorino/fragments/TestSavingFragment.kt +++ b/app/src/main/java/it/reyboz/bustorino/fragments/BackupImportFragment.kt @@ -18,7 +18,7 @@ import it.reyboz.bustorino.R import it.reyboz.bustorino.data.PreferencesHolder import it.reyboz.bustorino.data.UserDB -import it.reyboz.bustorino.util.Saving +import it.reyboz.bustorino.util.ImportExport import java.io.* import java.text.DateFormat import java.text.SimpleDateFormat @@ -30,10 +30,10 @@ /** * A simple [Fragment] subclass. - * Use the [TestSavingFragment.newInstance] factory method to + * Use the [BackupImportFragment.newInstance] factory method to * create an instance of this fragment. */ -class TestSavingFragment : Fragment() { +class BackupImportFragment : Fragment() { private val saveFileLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> @@ -162,13 +162,13 @@ //write main preferences zipOutputStream.putNextEntry(ZipEntry(MAIN_PREF_NAME)) var sharedPrefs = PreferencesHolder.getMainSharedPreferences(context) - var jsonPref = Saving.writeSharedPreferencesIntoJSON(sharedPrefs) + var jsonPref = ImportExport.writeSharedPreferencesIntoJSON(sharedPrefs) writeStringToZipOS(jsonPref.toString(2), zipOutputStream) zipOutputStream.closeEntry() zipOutputStream.putNextEntry(ZipEntry(APP_PREF_NAME)) sharedPrefs = PreferencesHolder.getAppPreferences(context) - jsonPref = Saving.writeSharedPreferencesIntoJSON(sharedPrefs) + jsonPref = ImportExport.writeSharedPreferencesIntoJSON(sharedPrefs) writeStringToZipOS(jsonPref.toString(2), zipOutputStream) zipOutputStream.closeEntry() //add CSV @@ -210,7 +210,7 @@ val jsonString = readFileToString(zipstream) try { val pref = PreferencesHolder.getAppPreferences(context) - Saving.importJsonToSharedPreferences(pref, jsonString, null, Regex("osmdroid\\.")) + ImportExport.importJsonToSharedPreferences(pref, jsonString, null, Regex("osmdroid\\.")) } catch (e: Exception){ Log.e(DEBUG_TAG, "Cannot read app preferences from file") e.printStackTrace() @@ -223,7 +223,8 @@ val pref = PreferencesHolder.getMainSharedPreferences(context) //In the future, if we move the favorite lines to a different file, // We should check here if the key is in the jsonObject, and copy it to the other file - Saving.importJsonToSharedPreferences(pref, jsonString, PreferencesHolder.IGNORE_KEYS_LOAD_MAIN, null) + ImportExport.importJsonToSharedPreferences(pref, jsonString, PreferencesHolder.IGNORE_KEYS_LOAD_MAIN, null, + PreferencesHolder.KEYS_MERGE_SET) } catch (e: Exception){ Log.e(DEBUG_TAG, "Cannot read main preferences from file") e.printStackTrace() @@ -279,7 +280,7 @@ @JvmStatic fun newInstance() = - TestSavingFragment().apply { + BackupImportFragment().apply { arguments = Bundle() /*.apply { putString(ARG_PARAM1, param1) putString(ARG_PARAM2, param2) diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/SettingsFragment.java b/app/src/main/java/it/reyboz/bustorino/fragments/SettingsFragment.java --- a/app/src/main/java/it/reyboz/bustorino/fragments/SettingsFragment.java +++ b/app/src/main/java/it/reyboz/bustorino/fragments/SettingsFragment.java @@ -18,6 +18,7 @@ package it.reyboz.bustorino.fragments; import android.content.Context; +import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; import android.os.Handler; @@ -33,6 +34,7 @@ import androidx.work.OneTimeWorkRequest; import androidx.work.WorkInfo; import androidx.work.WorkManager; +import it.reyboz.bustorino.ActivityBackup; import it.reyboz.bustorino.R; import it.reyboz.bustorino.data.DatabaseUpdate; import it.reyboz.bustorino.data.GtfsMaintenanceWorker; @@ -89,15 +91,24 @@ Preference dbUpdateNow = findPreference("pref_db_update_now"); if (dbUpdateNow!=null) dbUpdateNow.setOnPreferenceClickListener( - new Preference.OnPreferenceClickListener() { - @Override - public boolean onPreferenceClick(@NonNull Preference preference) { - //trigger update - if(getContext()!=null) { - DatabaseUpdate.requestDBUpdateWithWork(getContext().getApplicationContext(), true, true); - Toast.makeText(getContext(),R.string.requesting_db_update,Toast.LENGTH_SHORT).show(); - return true; - } + preference -> { + //trigger update + if(getContext()!=null) { + DatabaseUpdate.requestDBUpdateWithWork(getContext().getApplicationContext(), true, true); + Toast.makeText(getContext(),R.string.requesting_db_update,Toast.LENGTH_SHORT).show(); + return true; + } + return false; + } + ); + //set click listener on backup item + final Preference backupPref = findPreference("pref_backup_open"); + if (backupPref!=null) backupPref.setOnPreferenceClickListener( + preference -> { + if(getActivity()!=null){ + startActivity( new Intent(getActivity().getApplicationContext(), ActivityBackup.class) ); + return true; + } else { return false; } } diff --git a/app/src/main/java/it/reyboz/bustorino/util/Saving.kt b/app/src/main/java/it/reyboz/bustorino/util/ImportExport.kt rename from app/src/main/java/it/reyboz/bustorino/util/Saving.kt rename to app/src/main/java/it/reyboz/bustorino/util/ImportExport.kt --- a/app/src/main/java/it/reyboz/bustorino/util/Saving.kt +++ b/app/src/main/java/it/reyboz/bustorino/util/ImportExport.kt @@ -8,7 +8,7 @@ import java.io.* -class Saving { +class ImportExport { companion object{ @@ -87,10 +87,20 @@ } return editor.commit() } - + fun importJsonToSharedPreferences(sharedPreferences: SharedPreferences, + allJsonAsString: String, + ignoreKeys: Set?, + ignoreKeyRegex: Regex?): Int{ + return importJsonToSharedPreferences(sharedPreferences, allJsonAsString, + ignoreKeys, ignoreKeyRegex, HashSet()) + } fun importJsonToSharedPreferences(sharedPreferences: SharedPreferences, - allJsonAsString: String, ignoreKeys: Set?, ignoreKeyRegex: Regex?): Int { + allJsonAsString: String, + ignoreKeys: Set?, + ignoreKeyRegex: Regex?, + mergeSetKeys: Set + ): Int { // Parse JSON val jsonObject = JSONObject(allJsonAsString) @@ -111,6 +121,8 @@ is String -> editor.putString(key, value) is JSONArray -> { // Handle arrays val set = mutableSetOf() + if (mergeSetKeys.contains(key)) + sharedPreferences.getStringSet(key, mutableSetOf())?.let { set.addAll(it) } for (i in 0 until value.length()) { set.add(value.optString(i)) } diff --git a/app/src/main/res/layout/activity_experiments.xml b/app/src/main/res/layout/activity_container_fragment.xml rename from app/src/main/res/layout/activity_experiments.xml rename to app/src/main/res/layout/activity_container_fragment.xml diff --git a/app/src/main/res/layout/fragment_test_saving.xml b/app/src/main/res/layout/fragment_test_saving.xml --- a/app/src/main/res/layout/fragment_test_saving.xml +++ b/app/src/main/res/layout/fragment_test_saving.xml @@ -3,7 +3,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context=".fragments.TestSavingFragment"> + tools:context=".fragments.BackupImportFragment"> diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -275,6 +275,9 @@ Abilita notifiche Notifiche abilitate + + Backup e ripristino + Importa / esporta dati Dati salvati Salva backup Importa i dati dal backup diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -304,7 +304,9 @@ Close the tutorial Enable notifications Notifications enabled - + + Backup and restore + Import/export preferences Data saved Backup to file Import data from backup diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -100,6 +100,13 @@ /> + + + +