diff --git a/android/.idea/codeStyles/Project.xml b/android/.idea/codeStyles/Project.xml
index 30aa626..ae78c11 100644
--- a/android/.idea/codeStyles/Project.xml
+++ b/android/.idea/codeStyles/Project.xml
@@ -1,29 +1,113 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+ xmlns:android
+
+ ^$
+
+
+
+
+
+
+
+
+ xmlns:.*
+
+ ^$
+
+
+ BY_NAME
+
+
+
+
+
+
+ .*:id
+
+ http://schemas.android.com/apk/res/android
+
+
+
+
+
+
+
+
+ .*:name
+
+ http://schemas.android.com/apk/res/android
+
+
+
+
+
+
+
+
+ name
+
+ ^$
+
+
+
+
+
+
+
+
+ style
+
+ ^$
+
+
+
+
+
+
+
+
+ .*
+
+ ^$
+
+
+ BY_NAME
+
+
+
+
+
+
+ .*
+
+ http://schemas.android.com/apk/res/android
+
+
+ ANDROID_ATTRIBUTE_ORDER
+
+
+
+
+
+
+ .*
+
+ .*
+
+
+ BY_NAME
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index e75e26e..4894d79 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -24,6 +24,15 @@
+
+
+
+
+
diff --git a/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/model/CMessageList.java b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/model/CMessageList.java
index e384282..55f27f9 100644
--- a/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/model/CMessageList.java
+++ b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/model/CMessageList.java
@@ -35,6 +35,11 @@ public class CMessageList
}
private CMessageList()
+ {
+ reloadPrefs();
+ }
+
+ public void reloadPrefs()
{
synchronized (msg_lock)
{
diff --git a/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/model/QueryLog.java b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/model/QueryLog.java
index b8cbb5a..a0932b3 100644
--- a/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/model/QueryLog.java
+++ b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/model/QueryLog.java
@@ -15,9 +15,9 @@ public class QueryLog
private final static int MAX_HISTORY_SIZE = 192;
private static QueryLog _instance;
- public static QueryLog instance() { if (_instance == null) synchronized (QueryLog.class) { if (_instance == null) _instance = new QueryLog(); } return _instance; }
+ public static QueryLog inst() { if (_instance == null) synchronized (QueryLog.class) { if (_instance == null) _instance = new QueryLog(); } return _instance; }
- private QueryLog(){ load(); }
+ private QueryLog(){ reloadPrefs(); }
private final List history = new ArrayList<>();
@@ -50,7 +50,7 @@ public class QueryLog
e.apply();
}
- public synchronized void load()
+ public synchronized void reloadPrefs()
{
try
{
diff --git a/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/model/SCNSettings.java b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/model/SCNSettings.java
index 03186fe..e4f6fd9 100644
--- a/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/model/SCNSettings.java
+++ b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/model/SCNSettings.java
@@ -62,6 +62,11 @@ public class SCNSettings
// ------------------------------------------------------------
public SCNSettings()
+ {
+ reloadPrefs();
+ }
+
+ public void reloadPrefs()
{
SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("Config", Context.MODE_PRIVATE);
diff --git a/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/model/ServerCommunication.java b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/model/ServerCommunication.java
index 860b381..d2e18be 100644
--- a/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/model/ServerCommunication.java
+++ b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/model/ServerCommunication.java
@@ -613,7 +613,7 @@ public class ServerCommunication
LogLevel l = LogLevel.INFO;
SingleQuery q = new SingleQuery(l, i, s, u, r, rc, "SUCCESS");
- QueryLog.instance().add(q);
+ QueryLog.inst().add(q);
}
catch (Exception e2)
{
@@ -644,7 +644,7 @@ public class ServerCommunication
LogLevel l = isio?LogLevel.WARN:LogLevel.ERROR;
SingleQuery q = new SingleQuery(l, i, s, u, r, rc, e.toString());
- QueryLog.instance().add(q);
+ QueryLog.inst().add(q);
}
catch (Exception e2)
{
diff --git a/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/service/FBMService.java b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/service/FBMService.java
index 748fc27..5db4154 100644
--- a/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/service/FBMService.java
+++ b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/service/FBMService.java
@@ -4,8 +4,6 @@ import android.util.Log;
import android.widget.Toast;
import com.blackforestbytes.simplecloudnotifier.SCNApp;
-import com.blackforestbytes.simplecloudnotifier.lib.datatypes.Tuple4;
-import com.blackforestbytes.simplecloudnotifier.lib.datatypes.Tuple5;
import com.blackforestbytes.simplecloudnotifier.lib.string.Str;
import com.blackforestbytes.simplecloudnotifier.model.CMessage;
import com.blackforestbytes.simplecloudnotifier.model.CMessageList;
@@ -15,7 +13,6 @@ import com.blackforestbytes.simplecloudnotifier.model.QueryLog;
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
import com.blackforestbytes.simplecloudnotifier.model.ServerCommunication;
import com.blackforestbytes.simplecloudnotifier.model.SingleQuery;
-import com.google.android.gms.common.util.JsonUtils;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
@@ -52,7 +49,7 @@ public class FBMService extends FirebaseMessagingService
SingleQuery q = new SingleQuery(LogLevel.INFO, Instant.now(), "FBM", Str.Empty, new JSONObject(remoteMessage.getData()).toString(), 0, "SUCCESS");
- QueryLog.instance().add(q);
+ QueryLog.inst().add(q);
if (trimmed)
{
diff --git a/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/service/IABService.java b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/service/IABService.java
index d23c437..206b787 100644
--- a/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/service/IABService.java
+++ b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/service/IABService.java
@@ -74,6 +74,11 @@ public class IABService implements PurchasesUpdatedListener
startServiceConnection(this::queryPurchases, false);
}
+ public void reloadPrefs()
+ {
+ loadCache();
+ }
+
private void loadCache()
{
_localCache.clear();
diff --git a/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/view/MainActivity.java b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/view/MainActivity.java
index 5314cd7..0ef5547 100644
--- a/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/view/MainActivity.java
+++ b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/view/MainActivity.java
@@ -1,11 +1,13 @@
package com.blackforestbytes.simplecloudnotifier.view;
+import android.content.Context;
import android.content.Intent;
-import android.icu.text.SymbolTable;
+import android.content.SharedPreferences;
+import android.net.Uri;
import android.os.Bundle;
+import android.util.Log;
import android.view.View;
import android.widget.RelativeLayout;
-import android.widget.TextView;
import android.widget.Toast;
import com.blackforestbytes.simplecloudnotifier.R;
@@ -23,6 +25,12 @@ import androidx.appcompat.widget.Toolbar;
import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.ObjectInputStream;
+import java.util.Map;
+import java.util.Set;
+
public class MainActivity extends AppCompatActivity
{
public TabAdapter adpTabs;
@@ -31,7 +39,7 @@ public class MainActivity extends AppCompatActivity
@Override
protected void onCreate(Bundle savedInstanceState)
{
- QueryLog.instance();
+ QueryLog.inst();
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
@@ -103,4 +111,114 @@ public class MainActivity extends AppCompatActivity
if (clickCount == 4) startActivity(new Intent(this, QueryLogActivity.class));
}
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if(requestCode == 1991 && resultCode == RESULT_OK)
+ {
+ Uri uri = data.getData(); //The uri with the location of the file
+
+ Context ctxt = this;
+
+ try
+ {
+ ObjectInputStream stream = new ObjectInputStream(getContentResolver().openInputStream(uri));
+
+ Map d1 = (Map)stream.readObject();
+ Map d2 = (Map)stream.readObject();
+ Map d3 = (Map)stream.readObject();
+ Map d4 = (Map)stream.readObject();
+
+ stream.close();
+
+ runOnUiThread(() ->
+ {
+
+ SharedPreferences.Editor e1 = ctxt.getSharedPreferences("Config", Context.MODE_PRIVATE).edit();
+ SharedPreferences.Editor e2 = ctxt.getSharedPreferences("IAB", Context.MODE_PRIVATE).edit();
+ SharedPreferences.Editor e3 = ctxt.getSharedPreferences("CMessageList", Context.MODE_PRIVATE).edit();
+ SharedPreferences.Editor e4 = ctxt.getSharedPreferences("QueryLog", Context.MODE_PRIVATE).edit();
+
+ e1.clear();
+ for (Map.Entry entry : d1.entrySet())
+ {
+ if (entry.getValue() instanceof String) e1.putString(entry.getKey(), (String)entry.getValue());
+ if (entry.getValue() instanceof Boolean) e1.putBoolean(entry.getKey(), (Boolean)entry.getValue());
+ if (entry.getValue() instanceof Float) e1.putFloat(entry.getKey(), (Float)entry.getValue());
+ if (entry.getValue() instanceof Integer) e1.putInt(entry.getKey(), (Integer)entry.getValue());
+ if (entry.getValue() instanceof Long) e1.putLong(entry.getKey(), (Long)entry.getValue());
+ if (entry.getValue() instanceof Set>) e1.putStringSet(entry.getKey(), (Set)entry.getValue());
+ }
+
+ e2.clear();
+ for (Map.Entry entry : d2.entrySet())
+ {
+ if (entry.getValue() instanceof String) e2.putString(entry.getKey(), (String)entry.getValue());
+ if (entry.getValue() instanceof Boolean) e2.putBoolean(entry.getKey(), (Boolean)entry.getValue());
+ if (entry.getValue() instanceof Float) e2.putFloat(entry.getKey(), (Float)entry.getValue());
+ if (entry.getValue() instanceof Integer) e2.putInt(entry.getKey(), (Integer)entry.getValue());
+ if (entry.getValue() instanceof Long) e2.putLong(entry.getKey(), (Long)entry.getValue());
+ if (entry.getValue() instanceof Set>) e2.putStringSet(entry.getKey(), (Set)entry.getValue());
+ }
+
+ e2.clear();
+ for (Map.Entry entry : d3.entrySet())
+ {
+ if (entry.getValue() instanceof String) e3.putString(entry.getKey(), (String)entry.getValue());
+ if (entry.getValue() instanceof Boolean) e3.putBoolean(entry.getKey(), (Boolean)entry.getValue());
+ if (entry.getValue() instanceof Float) e3.putFloat(entry.getKey(), (Float)entry.getValue());
+ if (entry.getValue() instanceof Integer) e3.putInt(entry.getKey(), (Integer)entry.getValue());
+ if (entry.getValue() instanceof Long) e3.putLong(entry.getKey(), (Long)entry.getValue());
+ if (entry.getValue() instanceof Set>) e3.putStringSet(entry.getKey(), (Set)entry.getValue());
+ }
+
+ e4.clear();
+ for (Map.Entry entry : d4.entrySet())
+ {
+ if (entry.getValue() instanceof String) e4.putString(entry.getKey(), (String)entry.getValue());
+ if (entry.getValue() instanceof Boolean) e4.putBoolean(entry.getKey(), (Boolean)entry.getValue());
+ if (entry.getValue() instanceof Float) e4.putFloat(entry.getKey(), (Float)entry.getValue());
+ if (entry.getValue() instanceof Integer) e4.putInt(entry.getKey(), (Integer)entry.getValue());
+ if (entry.getValue() instanceof Long) e4.putLong(entry.getKey(), (Long)entry.getValue());
+ if (entry.getValue() instanceof Set>) e4.putStringSet(entry.getKey(), (Set)entry.getValue());
+ }
+
+ e1.apply();
+ e2.apply();
+ e3.apply();
+ e4.apply();
+
+
+ SCNSettings.inst().reloadPrefs();
+ IABService.inst().reloadPrefs();
+ CMessageList.inst().reloadPrefs();
+ QueryLog.inst().reloadPrefs();
+
+
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ ViewPager viewPager = findViewById(R.id.pager);
+ PagerAdapter adapter = adpTabs = new TabAdapter(getSupportFragmentManager());
+ viewPager.setAdapter(adapter);
+
+ TabLayout tabLayout = findViewById(R.id.tab_layout);
+ tabLayout.setupWithViewPager(viewPager);
+
+
+ SCNSettings.inst().work(this);
+
+ SCNApp.showToast("Backup imported", Toast.LENGTH_LONG);
+
+ finish();
+ });
+ }
+ catch (Exception e)
+ {
+ Log.e("Import:Err", e.toString());
+ SCNApp.showToast("Import failed", Toast.LENGTH_LONG);
+ }
+ }
+ }
}
diff --git a/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/view/SettingsFragment.java b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/view/SettingsFragment.java
index 71037a5..43dbb75 100644
--- a/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/view/SettingsFragment.java
+++ b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/view/SettingsFragment.java
@@ -2,6 +2,8 @@ package com.blackforestbytes.simplecloudnotifier.view;
import android.annotation.SuppressLint;
import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
import android.graphics.Color;
import android.media.AudioAttributes;
import android.media.AudioManager;
@@ -11,6 +13,7 @@ import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
+import android.os.Environment;
import android.text.Editable;
import android.util.Log;
import android.view.LayoutInflater;
@@ -28,6 +31,7 @@ import android.widget.TextView;
import android.widget.Toast;
import com.android.billingclient.api.Purchase;
+import com.blackforestbytes.simplecloudnotifier.BuildConfig;
import com.blackforestbytes.simplecloudnotifier.R;
import com.blackforestbytes.simplecloudnotifier.SCNApp;
import com.blackforestbytes.simplecloudnotifier.lib.android.ThreadUtils;
@@ -39,9 +43,15 @@ import com.blackforestbytes.simplecloudnotifier.util.TextChangedListener;
import org.jetbrains.annotations.NotNull;
+import java.io.File;
+import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.util.Date;
+import java.util.Map;
import androidx.annotation.NonNull;
+import androidx.core.content.FileProvider;
import androidx.fragment.app.Fragment;
import top.defaults.colorpicker.ColorPickerPopup;
import xyz.aprildown.ultimatemusicpicker.MusicPickerListener;
@@ -93,6 +103,9 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
private SeekBar prefMsgHighVolume;
private ImageView prefMsgHighVolumeTest;
+ private Button prefBtnImport;
+ private Button prefBtnExport;
+
private int musicPickerSwitch = -1;
private MediaPlayer[] mPlayers = new MediaPlayer[3];
@@ -160,6 +173,9 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
prefMsgHighVolume = v.findViewById(R.id.prefMsgHighVolume);
prefMsgHighVolumeTest = v.findViewById(R.id.btnHighVolumeTest);
+ prefBtnExport = v.findViewById(R.id.prefExport);
+ prefBtnImport = v.findViewById(R.id.prefImport);
+
ArrayAdapter plcsa = new ArrayAdapter<>(v.getContext(), android.R.layout.simple_spinner_item, SCNSettings.CHOOSABLE_CACHE_SIZES);
plcsa.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
prefLocalCacheSize.setAdapter(plcsa);
@@ -246,6 +262,9 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
prefUpgradeAccount.setOnClickListener(a -> onUpgradeAccount());
+ prefBtnExport.setOnClickListener(a -> onExport());
+ prefBtnImport.setOnClickListener(a -> onImport());
+
prefMsgLowEnableSound.setOnCheckedChangeListener((a,b) -> { s.PriorityLow.EnableSound=b; saveAndUpdate(); });
prefMsgLowRingtone_container.setOnClickListener(a -> chooseRingtoneLow());
prefMsgLowRepeatSound.setOnCheckedChangeListener((a,b) -> { s.PriorityLow.RepeatSound=b; saveAndUpdate(); });
@@ -277,6 +296,55 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
prefMsgHighVolumeTest.setOnClickListener((v) -> { if (s.PriorityHigh.ForceVolume) playTestSound(2, prefMsgHighVolumeTest, s.PriorityHigh.SoundSource, s.PriorityHigh.ForceVolumeValue); });
}
+ private void onExport()
+ {
+ Context ctxt = getContext();
+ if (ctxt == null) return;
+
+ try
+ {
+ File outputDir = ctxt.getCacheDir(); // context being the Activity pointer
+ File outputFile = File.createTempFile("scn_export_", ".dat", outputDir);
+
+ ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream(outputFile));
+
+ Map d1 = ctxt.getSharedPreferences("Config", Context.MODE_PRIVATE).getAll();
+ Map d2 = ctxt.getSharedPreferences("IAB", Context.MODE_PRIVATE).getAll();
+ Map d3 = ctxt.getSharedPreferences("CMessageList", Context.MODE_PRIVATE).getAll();
+ Map d4 = ctxt.getSharedPreferences("QueryLog", Context.MODE_PRIVATE).getAll();
+
+ output.writeObject(d1);
+ output.writeObject(d2);
+ output.writeObject(d3);
+ output.writeObject(d4);
+
+ Intent intent = new Intent(Intent.ACTION_SEND);
+
+ Uri uri = FileProvider.getUriForFile(ctxt, "com.blackforestbytes.simplecloudnotifier.fileprovider", outputFile);
+ intent.putExtra(Intent.EXTRA_STREAM, uri);
+ intent.setType("*/*");
+ intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+ startActivity(Intent.createChooser(intent, "Export"));
+ }
+ catch (IOException e)
+ {
+ Log.e("Export:Err", e.toString());
+ SCNApp.showToast("Export failed", Toast.LENGTH_LONG);
+ }
+ }
+
+ private void onImport()
+ {
+ SCNApp.getMainActivity().setContentView(R.layout.activity_main);
+
+ Intent intent = new Intent()
+ .setType("*/*")
+ .setAction(Intent.ACTION_GET_CONTENT);
+
+ ((MainActivity)getActivity()).startActivityForResult(Intent.createChooser(intent, "Select a file"), 1991);
+ }
+
private void updateEnabled(boolean prev, boolean now)
{
if (!prev && now)
diff --git a/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/view/debug/QueryLogActivity.java b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/view/debug/QueryLogActivity.java
index 782e112..609eac4 100644
--- a/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/view/debug/QueryLogActivity.java
+++ b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/view/debug/QueryLogActivity.java
@@ -22,7 +22,7 @@ public class QueryLogActivity extends AppCompatActivity
setContentView(R.layout.activity_querylog);
ListView lvMain = findViewById(R.id.lvQueryList);
- SingleQuery[] arr = QueryLog.instance().get().toArray(new SingleQuery[0]);
+ SingleQuery[] arr = QueryLog.inst().get().toArray(new SingleQuery[0]);
QueryLogAdapter a = new QueryLogAdapter(this, arr);
lvMain.setAdapter(a);
diff --git a/android/app/src/main/res/layout/fragment_settings.xml b/android/app/src/main/res/layout/fragment_settings.xml
index 96c913b..86ec0d5 100644
--- a/android/app/src/main/res/layout/fragment_settings.xml
+++ b/android/app/src/main/res/layout/fragment_settings.xml
@@ -805,6 +805,24 @@
+
+
+
+
diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml
index 1a5a526..4a78a71 100644
--- a/android/app/src/main/res/values/strings.xml
+++ b/android/app/src/main/res/values/strings.xml
@@ -37,4 +37,6 @@
Play test sound
DELETE
QueryLogActivity
+ Import settings
+ Export settings
diff --git a/android/app/src/main/res/xml/filepaths.xml b/android/app/src/main/res/xml/filepaths.xml
new file mode 100644
index 0000000..6e3dcec
--- /dev/null
+++ b/android/app/src/main/res/xml/filepaths.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/web/api/model.php b/web/api/model.php
index 7cb9825..f1bbf54 100644
--- a/web/api/model.php
+++ b/web/api/model.php
@@ -166,6 +166,12 @@ function verifyOrderToken($tok)
{
// https://developers.google.com/android-publisher/api-ref/purchases/products/get
+ // if this does no longer work, you probably have to go through the initial OAuth process again
+ // 1. go to Postman do the [ https://accounts.google.com/o/oauth2/auth ] request (in browser) to get a new "code"
+ // 2. go to Postman do the [ Get Tokens ] request to get a new "access_token" and "access_token"
+ // 3. update these tokens in the server config.php
+
+
try
{
$package = getConfig()['verify_api']['package_name'];