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'];