Page MenuHomeGitPull.it

D158.1781895239.diff
No OneTemporary

Authored By
Unknown
Size
33 KB
Referenced Files
None
Subscribers
None

D158.1781895239.diff

diff --git a/app/build.gradle b/app/build.gradle
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -123,7 +123,7 @@
//multidex - we need this to build the app
implementation "androidx.multidex:multidex:$multidex_version"
- implementation 'de.siegmar:fastcsv:2.0.0'
+ implementation 'de.siegmar:fastcsv:2.2.2'
testImplementation 'junit:junit:4.12'
implementation 'junit:junit:4.12'
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
@@ -27,7 +27,7 @@
public class ActivityExperiments extends GeneralActivity implements CommonFragmentListener {
- final static String DEBUG_TAG = "ExperimentsGTFS";
+ final static String DEBUG_TAG = "ExperimentsActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -54,7 +54,7 @@
//.add(R.id.fragment_container_view, LinesDetailFragment.class,
// LinesDetailFragment.Companion.makeArgs("gtt:4U"))
- .add(R.id.fragment_container_view, TestRealtimeGtfsFragment.class, null)
+ .add(R.id.fragment_container_view, TestSavingFragment.class, null)
.commit();
}
}
diff --git a/app/src/main/java/it/reyboz/bustorino/data/DBUpdateWorker.java b/app/src/main/java/it/reyboz/bustorino/data/DBUpdateWorker.java
--- a/app/src/main/java/it/reyboz/bustorino/data/DBUpdateWorker.java
+++ b/app/src/main/java/it/reyboz/bustorino/data/DBUpdateWorker.java
@@ -76,13 +76,13 @@
final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(getApplicationContext());
final int notification_ID = 32198;
final SharedPreferences shPr = con.getSharedPreferences(con.getString(R.string.mainSharedPreferences),MODE_PRIVATE);
- final int current_DB_version = shPr.getInt(DatabaseUpdate.DB_VERSION_KEY,-10);
+ final int current_DB_version = shPr.getInt(PreferencesHolder.DB_GTT_VERSION_KEY,-10);
final int new_DB_version = DatabaseUpdate.getNewVersion();
final boolean isUpdateCompulsory = getInputData().getBoolean(FORCED_UPDATE,false);
- final long lastDBUpdateTime = shPr.getLong(DatabaseUpdate.DB_LAST_UPDATE_KEY, 0);
+ final long lastDBUpdateTime = shPr.getLong(PreferencesHolder.DB_LAST_UPDATE_KEY, 0);
long currentTime = System.currentTimeMillis()/1000;
//showNotification(notificationManager, notification_ID);
@@ -143,9 +143,9 @@
Log.d(DEBUG_TAG, "Update finished successfully!");
//update the version in the shared preference
final SharedPreferences.Editor editor = shPr.edit();
- editor.putInt(DatabaseUpdate.DB_VERSION_KEY, new_DB_version);
+ editor.putInt(PreferencesHolder.DB_GTT_VERSION_KEY, new_DB_version);
currentTime = System.currentTimeMillis()/1000;
- editor.putLong(DatabaseUpdate.DB_LAST_UPDATE_KEY, currentTime);
+ editor.putLong(PreferencesHolder.DB_LAST_UPDATE_KEY, currentTime);
editor.apply();
cancelNotification(notification_ID);
diff --git a/app/src/main/java/it/reyboz/bustorino/data/DatabaseUpdate.java b/app/src/main/java/it/reyboz/bustorino/data/DatabaseUpdate.java
--- a/app/src/main/java/it/reyboz/bustorino/data/DatabaseUpdate.java
+++ b/app/src/main/java/it/reyboz/bustorino/data/DatabaseUpdate.java
@@ -57,8 +57,7 @@
public static final int VERSION_UNAVAILABLE = -2;
public static final int JSON_PARSING_ERROR = -4;
- public static final String DB_VERSION_KEY = "NextGenDB.GTTVersion";
- public static final String DB_LAST_UPDATE_KEY = "NextGenDB.LastDBUpdate";
+
enum Result {
@@ -305,8 +304,8 @@
.build())
.setInputData(reqData)
.build();
- final int version = theShPr.getInt(DatabaseUpdate.DB_VERSION_KEY, -10);
- final long lastDBUpdateTime = theShPr.getLong(DatabaseUpdate.DB_LAST_UPDATE_KEY, -10);
+ final int version = theShPr.getInt(PreferencesHolder.DB_GTT_VERSION_KEY, -10);
+ final long lastDBUpdateTime = theShPr.getLong(PreferencesHolder.DB_LAST_UPDATE_KEY, -10);
if ((version >= 0 || lastDBUpdateTime >=0) && !restart)
workManager.enqueueUniquePeriodicWork(DBUpdateWorker.DEBUG_TAG,
ExistingPeriodicWorkPolicy.KEEP, wr);
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
@@ -19,7 +19,6 @@
import android.content.Context;
import android.content.SharedPreferences;
-import android.util.Log;
import it.reyboz.bustorino.R;
import static android.content.Context.MODE_PRIVATE;
@@ -36,9 +35,12 @@
public static final String PREF_GTFS_DB_VERSION = "gtfs_db_version";
public static final String PREF_INTRO_ACTIVITY_RUN ="pref_intro_activity_run";
-
+ public static final String DB_GTT_VERSION_KEY = "NextGenDB.GTTVersion";
+ public static final String DB_LAST_UPDATE_KEY = "NextGenDB.LastDBUpdate";
public static final String PREF_FAVORITE_LINES = "pref_favorite_lines";
+ public static final Set<String> 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){
return context.getSharedPreferences(context.getString(R.string.mainSharedPreferences), MODE_PRIVATE);
}
diff --git a/app/src/main/java/it/reyboz/bustorino/data/UserDB.java b/app/src/main/java/it/reyboz/bustorino/data/UserDB.java
--- a/app/src/main/java/it/reyboz/bustorino/data/UserDB.java
+++ b/app/src/main/java/it/reyboz/bustorino/data/UserDB.java
@@ -27,11 +27,13 @@
import android.net.Uri;
import android.util.Log;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
+import java.io.IOException;
+import java.util.*;
+import de.siegmar.fastcsv.reader.CloseableIterator;
+import de.siegmar.fastcsv.reader.CsvReader;
+import de.siegmar.fastcsv.reader.CsvRow;
+import de.siegmar.fastcsv.writer.CsvWriter;
import it.reyboz.bustorino.backend.Stop;
import it.reyboz.bustorino.backend.StopsDBInterface;
@@ -40,8 +42,12 @@
private static final String DATABASE_NAME = "user.db";
static final String TABLE_NAME = "favorites";
private final Context c; // needed during upgrade
+ public final static String COL_ID = "ID";
+ public final static String COL_USERNAME="username";
+
+ public static final int FILE_INVALID=-10;
private final static String[] usernameColumnNameAsArray = {"username"};
- public final static String[] getFavoritesColumnNamesAsArray = {"ID", "username"};
+ public final static String[] getFavoritesColumnNamesAsArray = {COL_ID, COL_USERNAME};
private static final Uri FAVORITES_URI = AppDataProvider.getUriBuilderToComplete().appendPath(
AppDataProvider.FAVORITES).build();
@@ -323,4 +329,66 @@
return found;
}
+
+ //extract rows into CSV
+ public boolean writeFavoritesToCsv(CsvWriter writer){
+ SQLiteDatabase db = this.getReadableDatabase();
+
+ String sortOrder =
+ COL_ID + " DESC";
+ Cursor cursor = db.query(TABLE_NAME, getFavoritesColumnNamesAsArray,null,null,null,null, sortOrder);
+
+ final int nCols = 2;//cursor.getColumnCount();
+ writer.writeRow(cursor.getColumnNames());
+ while (cursor.moveToNext()){
+ String[] arr = {cursor.getString(0), cursor.getString(1)};
+ writer.writeRow(arr);
+ }
+ cursor.close();
+ return true;
+ }
+
+ public int insertRowsFromCSV(CsvReader reader){
+ SQLiteDatabase db = this.getWritableDatabase();
+
+ boolean firstrow = true;
+ final HashMap<String,Integer> colIndexByRows = new HashMap<>();
+
+ final CloseableIterator<CsvRow> rowsIter = reader.iterator();
+ if (!rowsIter.hasNext()){
+ //nothing to do, it's an empty file
+ return -1;
+ }
+ final CsvRow firstRow = rowsIter.next();
+ // close if there isn't another rows
+ if(!rowsIter.hasNext()) return -2;
+ for (int i =0; i<firstRow.getFieldCount(); i++){
+ colIndexByRows.put(firstRow.getField(i),i);
+ }
+ if (!colIndexByRows.containsKey(COL_ID) || !colIndexByRows.containsKey(COL_USERNAME)){
+ //Cannot accept the file
+ return FILE_INVALID;
+ }
+ //begin
+ db.beginTransaction();
+ int updated = 0;
+ final int col_id = colIndexByRows.get(COL_ID);
+ final int col_username = colIndexByRows.get(COL_USERNAME);
+ while (rowsIter.hasNext()){
+ final CsvRow row = rowsIter.next();
+ final ContentValues cv = new ContentValues();
+ cv.put(COL_ID, row.getField(col_id));
+ cv.put(COL_USERNAME, row.getField(col_username));
+
+ long rowid = db.insertWithOnConflict(TABLE_NAME, null, cv, SQLiteDatabase.CONFLICT_REPLACE);
+ if (rowid >= 0)
+ updated +=1;
+ }
+ db.setTransactionSuccessful();
+ db.endTransaction();
+
+ db.close();
+
+ return updated;
+ }
}
diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/TestSavingFragment.kt b/app/src/main/java/it/reyboz/bustorino/fragments/TestSavingFragment.kt
new file mode 100644
--- /dev/null
+++ b/app/src/main/java/it/reyboz/bustorino/fragments/TestSavingFragment.kt
@@ -0,0 +1,297 @@
+package it.reyboz.bustorino.fragments
+
+import android.app.Activity
+import android.content.Intent
+import android.net.Uri
+import android.os.Bundle
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.Button
+import android.widget.CheckBox
+import android.widget.Toast
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.fragment.app.Fragment
+import de.siegmar.fastcsv.reader.CsvReader
+import de.siegmar.fastcsv.writer.CsvWriter
+import it.reyboz.bustorino.R
+import it.reyboz.bustorino.data.PreferencesHolder
+import it.reyboz.bustorino.data.UserDB
+import it.reyboz.bustorino.util.Saving
+import java.io.*
+import java.text.DateFormat
+import java.text.SimpleDateFormat
+import java.util.*
+import java.util.zip.ZipEntry
+import java.util.zip.ZipInputStream
+import java.util.zip.ZipOutputStream
+
+
+/**
+ * A simple [Fragment] subclass.
+ * Use the [TestSavingFragment.newInstance] factory method to
+ * create an instance of this fragment.
+ */
+class TestSavingFragment : Fragment() {
+
+ private val saveFileLauncher =
+ registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
+ if (result.resultCode == Activity.RESULT_OK) {
+ result.data?.data?.also { uri ->
+ writeDataZip(uri)
+ }
+ }
+ }
+
+ private val openFileLauncher =
+ registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
+ if (!(loadFavorites|| loadPreferences)){
+ Toast.makeText(context, R.string.message_check_at_least_one, Toast.LENGTH_SHORT).show()
+ }
+ else if (result.resultCode == Activity.RESULT_OK) {
+
+ result.data?.data?.also { uri ->
+
+ loadZipData(uri,loadFavorites, loadPreferences)
+ }
+ }
+ }
+
+
+ private lateinit var saveButton: Button
+ private var loadFavorites = true
+ private var loadPreferences = true
+ private lateinit var checkFavorites: CheckBox
+ private lateinit var checkPreferences: CheckBox
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ /*arguments?.let {
+ param1 = it.getString(ARG_PARAM1)
+ param2 = it.getString(ARG_PARAM2)
+ }*/
+ }
+
+
+
+ override fun onCreateView(
+ inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ // Inflate the layout for this fragment
+ val rootview= inflater.inflate(R.layout.fragment_test_saving, container, false)
+
+ saveButton = rootview.findViewById(R.id.saveButton)
+ saveButton.setOnClickListener {
+ startFileSaveIntent()
+ }
+ checkFavorites = rootview.findViewById(R.id.favoritesCheckBox)
+ checkFavorites.setOnCheckedChangeListener { _, isChecked ->
+ loadFavorites = isChecked
+
+ }
+ checkPreferences = rootview.findViewById(R.id.preferencesCheckBox)
+ checkPreferences.setOnCheckedChangeListener { _, isChecked ->
+ loadPreferences = isChecked
+
+ }
+ val readFavoritesButton = rootview.findViewById<Button>(R.id.loadDataButton)
+ readFavoritesButton.setOnClickListener {
+ startOpenCSVIntent()
+ }
+
+
+ return rootview
+ }
+ private fun startFileSaveIntent() {
+ val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
+ addCategory(Intent.CATEGORY_OPENABLE)
+ val day_string = getCurrentDateString()
+ type = "application/zip" //"text/csv" // Set MIME type to CSV
+ putExtra(Intent.EXTRA_TITLE, "busto_data_${day_string}.zip") // Default file name
+ // Optionally, specify a URI for the directory that should be opened in
+ // the system file picker before your app creates the document.
+ //putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri)
+ }
+ saveFileLauncher.launch(intent)
+ }
+
+ private fun writeCsv(uri: Uri){
+ val context = context ?: return
+ val contentResolver = context.contentResolver
+ contentResolver.openOutputStream(uri)?.use {
+ OutputStreamWriter(it).use {wr->
+ val csvWriter = CsvWriter.builder().build(wr)
+ val userDB = UserDB(context)
+ userDB.writeFavoritesToCsv(csvWriter)
+ csvWriter.close()
+ Toast.makeText(context, R.string.saved_data, Toast.LENGTH_SHORT).show()
+ }
+
+ }
+ }
+ private fun writeStringToZipOS(string: String, zipOutputStream: ZipOutputStream){
+ val bais = ByteArrayInputStream(string.toByteArray())
+ val bytes = ByteArray(1024)
+ var length: Int
+ while ((bais.read(bytes).also { length = it }) >= 0) {
+ zipOutputStream.write(bytes, 0, length)
+ }
+ }
+
+ private fun readFileToString(zipInputStream: ZipInputStream): String{
+ val buffer = ByteArrayOutputStream()
+ val data = ByteArray(1024)
+ var count: Int
+ // Read the content of the specific entry into the buffer
+ while (zipInputStream.read(data, 0, data.size).also { count = it } != -1) {
+ buffer.write(data, 0, count)
+ }
+ // Convert the buffer's content to a string
+ return buffer.toString()
+ }
+
+ private fun writeDataZip(uri: Uri){
+ val context = context ?: return
+ val contentResolver = context.contentResolver
+ contentResolver.openOutputStream(uri)?.use {outs ->
+ val bof = BufferedOutputStream(outs)
+ val zipOutputStream = ZipOutputStream(bof)
+
+ //write main preferences
+ zipOutputStream.putNextEntry(ZipEntry(MAIN_PREF_NAME))
+ var sharedPrefs = PreferencesHolder.getMainSharedPreferences(context)
+ var jsonPref = Saving.writeSharedPreferencesIntoJSON(sharedPrefs)
+ writeStringToZipOS(jsonPref.toString(2), zipOutputStream)
+ zipOutputStream.closeEntry()
+
+ zipOutputStream.putNextEntry(ZipEntry(APP_PREF_NAME))
+ sharedPrefs = PreferencesHolder.getAppPreferences(context)
+ jsonPref = Saving.writeSharedPreferencesIntoJSON(sharedPrefs)
+ writeStringToZipOS(jsonPref.toString(2), zipOutputStream)
+ zipOutputStream.closeEntry()
+ //add CSV
+ zipOutputStream.putNextEntry(ZipEntry(FAVORITES_NAME))
+ val outWriter = OutputStreamWriter(zipOutputStream)
+ val csvWriter = CsvWriter.builder().build(outWriter)
+ val userDB = UserDB(context)
+ userDB.writeFavoritesToCsv(csvWriter)
+ outWriter.flush()
+ zipOutputStream.closeEntry()
+
+ zipOutputStream.close()
+
+ Toast.makeText(context, R.string.saved_data, Toast.LENGTH_SHORT).show()
+ }
+ }
+ private fun loadZipData(uri: Uri, loadFavorites: Boolean, loadPreferences: Boolean){
+ val context = context ?: return
+ val contentResolver = context.contentResolver
+ contentResolver.openInputStream(uri)?.use {ins->
+ ZipInputStream(ins).use {zipstream->
+ var entry: ZipEntry? = zipstream.nextEntry
+
+ while (entry != null) {
+ Log.d("testSavingFragment", "read file: ${entry.name}")
+ when (entry.name){
+ FAVORITES_NAME -> if (loadFavorites) {
+
+ val reader = InputStreamReader(zipstream)
+ val csvReader = CsvReader.builder().build(reader)
+
+ val userDB = UserDB(context)
+ val updated = userDB.insertRowsFromCSV(csvReader)
+
+ userDB.close()
+ //csvReader.close()
+ }
+ APP_PREF_NAME -> if(loadPreferences){
+ val jsonString = readFileToString(zipstream)
+ try {
+ val pref = PreferencesHolder.getAppPreferences(context)
+ Saving.importJsonToSharedPreferences(pref, jsonString, null, Regex("osmdroid\\."))
+ } catch (e: Exception){
+ Log.e(DEBUG_TAG, "Cannot read app preferences from file")
+ e.printStackTrace()
+ }
+ }
+ //Main preferences contains the lines favorites
+ MAIN_PREF_NAME -> if(loadFavorites){
+ val jsonString = readFileToString(zipstream)
+ try {
+ 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)
+ } catch (e: Exception){
+ Log.e(DEBUG_TAG, "Cannot read main preferences from file")
+ e.printStackTrace()
+ }
+ }
+ }
+ //load new entry
+ entry = zipstream.nextEntry
+ }
+
+ }
+ Toast.makeText(context, R.string.data_imported_backup, Toast.LENGTH_SHORT).show()
+ }
+
+ }
+
+ ///OPEN CSV
+ private fun startOpenCSVIntent(){
+ val intent= Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
+ addCategory(Intent.CATEGORY_OPENABLE)
+ type = "application/zip" // Set MIME type
+ // Optionally, specify a URI for the directory that should be opened in
+ // the system file picker before your app creates the document.
+ //putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri)
+ }
+ openFileLauncher.launch(intent)
+ }
+
+ private fun importCSVIntoFavorites(uri: Uri){
+ val context = context ?: return
+ val contentResolver = context.contentResolver
+ contentResolver.openInputStream(uri)?.use {
+ InputStreamReader(it).use { stream ->
+ val csvReader = CsvReader.builder().build(stream)
+
+ val userDB = UserDB(context)
+ val updated = userDB.insertRowsFromCSV(csvReader)
+ Toast.makeText(context, "Read $updated favorites", Toast.LENGTH_SHORT).show()
+ userDB.close()
+ csvReader.close()
+ }
+ }
+ }
+
+ companion object {
+
+ const val FILE_SAVE = "favorites.csv"
+ const val DEBUG_TAG ="BusTO-TestSave"
+
+ const val FAVORITES_NAME = "favorites.csv"
+ const val MAIN_PREF_NAME = "preferences_main.json"
+ const val APP_PREF_NAME = "preferences_app.json"
+
+ @JvmStatic
+ fun newInstance() =
+ TestSavingFragment().apply {
+ arguments = Bundle() /*.apply {
+ putString(ARG_PARAM1, param1)
+ putString(ARG_PARAM2, param2)
+ }
+ */
+ }
+
+ fun getCurrentDateString(): String{
+
+ val dateFormat: DateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.ITALY)
+ val date = Date()
+ return dateFormat.format(date)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/it/reyboz/bustorino/util/Saving.kt b/app/src/main/java/it/reyboz/bustorino/util/Saving.kt
new file mode 100644
--- /dev/null
+++ b/app/src/main/java/it/reyboz/bustorino/util/Saving.kt
@@ -0,0 +1,153 @@
+package it.reyboz.bustorino.util
+
+import android.content.SharedPreferences
+import android.util.Log
+import it.reyboz.bustorino.BuildConfig
+import org.json.JSONArray
+import org.json.JSONObject
+import java.io.*
+
+
+class Saving {
+
+ companion object{
+
+ const val TAG = "BusTO-Saving";
+
+ /**
+ * Serialize all preferences into an output stream
+ * @param os OutputStream to write to
+ * @return True iff successful
+ */
+ fun serialize(os: OutputStream, sharedPreferences: SharedPreferences): Boolean {
+ var oos: ObjectOutputStream? = null
+ try {
+ oos = ObjectOutputStream(os)
+ oos.writeObject(sharedPreferences.all)
+ oos.close()
+ } catch (e: IOException) {
+ Log.e(TAG, "Error serializing preferences", if (BuildConfig.DEBUG) e else null)
+ return false
+ } finally {
+ //Utils.closeQuietly(oos, os)
+ oos?.close()
+ }
+ return true
+ }
+
+ /**
+ * Read all preferences from an input stream.
+ * then deserializes the options present in the given stream.
+ * If the given object contains an unknown class, the deserialization is aborted and the underlying
+ * preferences are not changed by this method
+ * @param `is` Input stream to load the preferences from
+ * @return True iff the new values were successfully written to persistent storage
+ *
+ * @throws IllegalArgumentException
+ */
+ fun deserialize(inputs: InputStream, preferences: SharedPreferences): Boolean {
+ var ois: ObjectInputStream? = null
+ var map: Map<String?, Any>? = null
+ try {
+ ois = ObjectInputStream(inputs)
+ map = ois.readObject() as Map<String?,Any>?
+ } catch (e: IOException) {
+ Log.e(TAG, "Error deserializing preferences", if (BuildConfig.DEBUG) e else null)
+ return false
+ } catch (e: ClassNotFoundException) {
+ Log.e(TAG, "Error deserializing preferences", if (BuildConfig.DEBUG) e else null)
+ return false
+ } finally {
+ //Utils.closeQuietly(ois, inputs)
+ ois?.close()
+ }
+
+ val editor: SharedPreferences.Editor = preferences.edit()
+ //editor.clear()
+
+ for ((key, value) in map!!) {
+ // Unfortunately, the editor only provides typed setters
+ if (value is Boolean) {
+ editor.putBoolean(key, (value as Boolean))
+ } else if (value is String) {
+ editor.putString(key, value as String)
+ } else if (value is Int) {
+ editor.putInt(key, value as Int)
+ } else if (value is Float) {
+ editor.putFloat(key, value as Float)
+ } else if (value is Long) {
+ editor.putLong(key, (value as Long))
+ } else if (value is Set<*>) {
+ val setvalue = value as Set<*>
+ if (setvalue.iterator().next() is String)
+ editor.putStringSet(key, value as Set<String?>)
+ } else {
+ throw IllegalArgumentException("Type " + value.javaClass.name + " is unknown")
+ }
+ }
+ return editor.commit()
+ }
+
+
+ fun importJsonToSharedPreferences(sharedPreferences: SharedPreferences,
+ allJsonAsString: String, ignoreKeys: Set<String>?, ignoreKeyRegex: Regex?): Int {
+ // Parse JSON
+ val jsonObject = JSONObject(allJsonAsString)
+
+
+ try {
+ // Write to SharedPreferences
+ val editor = sharedPreferences.edit()
+ jsonObject.keys().forEach { key ->
+ if (ignoreKeys?.contains(key)==true || ignoreKeyRegex?.containsMatchIn(key)==true)
+ //do nothing
+ return@forEach
+ val value = jsonObject.opt(key)
+ when (value) {
+ is Boolean -> editor.putBoolean(key, value)
+ is Int -> editor.putInt(key, value)
+ is Long -> editor.putLong(key, value)
+ is Float -> editor.putFloat(key, value)
+ is String -> editor.putString(key, value)
+ is JSONArray -> { // Handle arrays
+ val set = mutableSetOf<String>()
+ for (i in 0 until value.length()) {
+ set.add(value.optString(i))
+ }
+ editor.putStringSet(key, set)
+ }
+ // Handle other types as needed
+ }
+ }
+ editor.apply()
+ } catch (e: Exception){
+ Log.e(TAG, "Cannot write sharedPreferences")
+ e.printStackTrace()
+ return -1
+ }
+ return 0
+ }
+
+ fun writeSharedPreferencesIntoJSON(sharedPreferences: SharedPreferences): JSONObject{
+ val allEntries: Map<String?, *> = sharedPreferences.all
+
+
+ // Convert to JSON
+ val json = JSONObject()
+ for ((key, value1) in allEntries) {
+ val value = value1!!
+ if (value is Set<*>) {
+ // Convert StringSet to JSONArray
+ val jsonArray = JSONArray(value)
+ if (key != null) {
+ json.put(key, jsonArray)
+ }
+ } else if (key != null) {
+ json.put(key, value)
+
+ }
+ }
+ return json
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_test_saving.xml b/app/src/main/res/layout/fragment_test_saving.xml
new file mode 100644
--- /dev/null
+++ b/app/src/main/res/layout/fragment_test_saving.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ 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">
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <Button
+ android:text="@string/btn_backup_message"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" android:id="@+id/saveButton"
+ app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toTopOf="@id/favoritesCheckBox"
+ android:layout_marginTop="50dp"
+ android:layout_marginBottom="50dp"
+ app:layout_constraintVertical_bias="0.0"
+ app:layout_constraintVertical_chainStyle="packed"
+
+ />
+
+
+ <CheckBox
+ android:text="@string/load_file_favorites"
+ android:layout_width="wrap_content"
+ android:checked="true"
+ app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/saveButton"
+ app:layout_constraintBottom_toTopOf="@id/preferencesCheckBox"
+ app:layout_constraintVertical_bias="0.0"
+ android:minHeight="48dp"
+
+ android:layout_height="wrap_content" android:id="@+id/favoritesCheckBox"/>
+ <CheckBox
+ android:text="@string/load_preferences"
+ android:layout_width="wrap_content"
+ android:checked="true"
+ app:layout_constraintTop_toBottomOf="@id/favoritesCheckBox"
+ app:layout_constraintBottom_toTopOf="@id/loadDataButton"
+ app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintVertical_bias="0.0"
+ android:minHeight="48dp"
+ android:layout_marginTop="8dp"
+
+ android:layout_height="wrap_content" android:id="@+id/preferencesCheckBox"/>
+ <Button
+ android:text="@string/btn_load_backup_message"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" android:id="@+id/loadDataButton"
+ app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/preferencesCheckBox"
+ app:layout_constraintBottom_toBottomOf="parent"
+ android:layout_marginTop="18dp"
+ app:layout_constraintVertical_bias="0.0"
+
+
+ />
+
+ </androidx.constraintlayout.widget.ConstraintLayout>
+</FrameLayout>
\ No newline at end of file
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,7 +275,14 @@
<string name="grant_notification_permission">Abilita notifiche</string>
<string name="notification_permission_granted">Notifiche abilitate</string>
-
+ <string name="saved_data">Dati salvati</string>
+ <string name="btn_backup_message">Salva backup</string>
+ <string name="btn_load_backup_message">Importa i dati dal backup</string>
+ <string name="data_imported_backup">Backup importato</string>
+
+ <string name="message_check_at_least_one">Seleziona almeno un elemento da importare!</string>
+ <string name="load_file_favorites">Importa preferiti dal backup</string>
+ <string name="load_preferences">Importa preferenze dal backup</string>
</resources>
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
@@ -164,7 +164,7 @@
<!--
Mixed button strings
!-->
- <string name="open_telegram"> Join Telegram channel</string>
+ <string name="open_telegram">Join Telegram channel</string>
<!--
Map view buttons strings
!-->
@@ -283,7 +283,7 @@
</string>
<string name="tutorial_map">
- <![CDATA[...or you can find them in the map, along with the real-time positions of the buses running in the city (in <font color="#2F59CC">blue</font>)]]>
+ <![CDATA[...or you can find them in the map, along with the real-time positions of the buses running in the city (in <font color="#2F59CC">blue</font>)]]>
</string>
<string name="tutorial_line">
<![CDATA[You can also look at the line you want and see where the stops are, together with the vehicles serving the line at the moment. Remember to choose the direction you\'re interested in!]]>
@@ -305,5 +305,12 @@
<string name="grant_notification_permission">Enable notifications</string>
<string name="notification_permission_granted">Notifications enabled</string>
+ <string name="saved_data">Data saved</string>
+ <string name="btn_backup_message">Backup to file</string>
+ <string name="btn_load_backup_message">Import data from backup</string>
+ <string name="data_imported_backup">Backup has been imported</string>
+ <string name="message_check_at_least_one">Check at least one item to import!</string>
+ <string name="load_file_favorites">Import favorites from backup</string>
+ <string name="load_preferences">Import preferences from backup</string>
</resources>

File Metadata

Mime Type
text/plain
Expires
Fri, Jun 19, 20:53 (21 h, 5 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1967957
Default Alt Text
D158.1781895239.diff (33 KB)

Event Timeline