Compare commits
90 Commits
Author | SHA1 | Date | |
---|---|---|---|
741c09b1e4
|
|||
0c0d7d181f
|
|||
9304da9422
|
|||
![]() |
d7afdd00f2 | ||
b780ccea1c
|
|||
![]() |
5d8e871871 | ||
4655d688c9
|
|||
8263c0ad95
|
|||
a701afd09b
|
|||
![]() |
1e02d8c01f | ||
e4651375aa
|
|||
![]() |
afce4c6391 | ||
e95d0cb010
|
|||
f717355519
|
|||
3368e514ca
|
|||
9dca27177f
|
|||
c63274f7a9
|
|||
6c2d2d2345
|
|||
a6fbaa192f
|
|||
![]() |
a01f156535 | ||
3b021c09dc
|
|||
287176c881
|
|||
84eeb5a002
|
|||
b892532023
|
|||
![]() |
56cdc52bb4 | ||
9ef6b1dd91
|
|||
6f7585323b
|
|||
92135e64a3
|
|||
28efeea30b
|
|||
a8a074907b
|
|||
6c29ec9820
|
|||
![]() |
8ec23144ca | ||
b703853b28
|
|||
6f7529fc9b
|
|||
27b45098f0
|
|||
21f66d99cd
|
|||
1745051e6e
|
|||
![]() |
b395e054e6 | ||
90f4270b74
|
|||
f68d75c226
|
|||
9cf5133469
|
|||
75929aad48
|
|||
dca149ec4e
|
|||
75a7b97d24
|
|||
eb62873fb6
|
|||
f8effe7d1e
|
|||
87a3d34315
|
|||
0f38ad6e5c
|
|||
0ec6ab71b2
|
|||
b42204c87d
|
|||
53cb259ec1
|
|||
0150cc9e8e
|
|||
20214473f1
|
|||
![]() |
586ac07ef3 | ||
66f82f26d5
|
|||
6cb2aa00fb
|
|||
96a236803e
|
|||
9d24044eb3
|
|||
90248bcb54
|
|||
a3b2a1b14f
|
|||
b21b159b95
|
|||
93ec261dc0
|
|||
ae246e9219
|
|||
3f18fdd35a
|
|||
faf5207478
|
|||
71f003dd66
|
|||
3f85ab514e
|
|||
9eb5a6b1b9
|
|||
8e26cd6078
|
|||
36b9263730
|
|||
3d29fecaec
|
|||
![]() |
92ac05f1e3 | ||
cae6ff6271
|
|||
2163ae4dbf
|
|||
083945852b
|
|||
![]() |
e69b1232d7 | ||
ac681065fa
|
|||
90d8b46179
|
|||
![]() |
280b5ca4aa | ||
54a171bd5e
|
|||
c043367810
|
|||
5494df1c56
|
|||
f9d04e38a0
|
|||
6dfd7e016b
|
|||
96a3cbc40a
|
|||
7261795c99
|
|||
d6becd15c1
|
|||
0b22a18088
|
|||
39fcddfaf0
|
|||
![]() |
38eaa0c132 |
22
README.md
Normal file
@@ -0,0 +1,22 @@
|
||||
SimpleCloudNotifier [](https://play.google.com/store/apps/details?id=com.blackforestbytes.simplecloudnotifier)
|
||||
===================
|
||||
|
||||
> SimpleCloudNotifier is an app to display messages that you can send to your phone with simple POST requests.
|
||||
>
|
||||
> After you start the app it generates a UserID and a UserSecret.
|
||||
> Now you can send your message to https://simplecloudnotifier.blackforestbytes.com/send.php and a notification will be pushed to your phone.
|
||||
> (see https://simplecloudnotifier.blackforestbytes.com/ for an example with curl)
|
||||
>
|
||||
>
|
||||
> Use it to
|
||||
> - send yourself automated messages from cron jobs
|
||||
> - notify yourself when long-running scripts finish
|
||||
> - send server error messages directly to your phone
|
||||
> - integrate with other online services
|
||||
>
|
||||
> The possibilities are endless*
|
||||
>
|
||||
> \* Disclaimer: Developer does not actually guarantee endless possibilities
|
||||
|
||||
|
||||
  
|
57
android/.idea/assetWizardSettings.xml
generated
@@ -3,6 +3,59 @@
|
||||
<component name="WizardSettings">
|
||||
<option name="children">
|
||||
<map>
|
||||
<entry key="imageWizard">
|
||||
<value>
|
||||
<PersistentState>
|
||||
<option name="children">
|
||||
<map>
|
||||
<entry key="imageAssetPanel">
|
||||
<value>
|
||||
<PersistentState>
|
||||
<option name="children">
|
||||
<map>
|
||||
<entry key="launcherLegacy">
|
||||
<value>
|
||||
<PersistentState>
|
||||
<option name="values">
|
||||
<map>
|
||||
<entry key="assetType" value="IMAGE" />
|
||||
<entry key="cropped" value="true" />
|
||||
<entry key="iconShape" value="NONE" />
|
||||
<entry key="imageAsset" value="F:\Eigene Dateien\Dropbox\Programming\Java\AndroidStudioProjects\SimpleCloudNotifier\data\icon_512_nobox.png" />
|
||||
<entry key="outputName" value="ic_notification_full" />
|
||||
</map>
|
||||
</option>
|
||||
</PersistentState>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="notification">
|
||||
<value>
|
||||
<PersistentState>
|
||||
<option name="values">
|
||||
<map>
|
||||
<entry key="assetType" value="IMAGE" />
|
||||
<entry key="imageAsset" value="F:\Eigene Dateien\Dropbox\Programming\Java\AndroidStudioProjects\SimpleCloudNotifier\data\icon_512_transparent.png" />
|
||||
<entry key="outputName" value="ic_notification_white" />
|
||||
</map>
|
||||
</option>
|
||||
</PersistentState>
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
<option name="values">
|
||||
<map>
|
||||
<entry key="outputIconType" value="LAUNCHER_LEGACY" />
|
||||
</map>
|
||||
</option>
|
||||
</PersistentState>
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</PersistentState>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="vectorWizard">
|
||||
<value>
|
||||
<PersistentState>
|
||||
@@ -14,8 +67,8 @@
|
||||
<option name="values">
|
||||
<map>
|
||||
<entry key="assetSourceType" value="FILE" />
|
||||
<entry key="outputName" value="priority_low" />
|
||||
<entry key="sourceFile" value="C:\Users\Mike\Downloads\Low Priority-595b40b75ba036ed117d9842.svg" />
|
||||
<entry key="outputName" value="ic_garbage" />
|
||||
<entry key="sourceFile" value="C:\Users\Mike\Downloads\garbage.svg" />
|
||||
</map>
|
||||
</option>
|
||||
</PersistentState>
|
||||
|
@@ -35,23 +35,23 @@ android {
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
|
||||
implementation 'com.android.support:support-v4:28.0.0'
|
||||
implementation 'com.android.support:appcompat-v7:28.0.0'
|
||||
implementation 'com.android.support:cardview-v7:28.0.0'
|
||||
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
|
||||
implementation 'com.android.support:recyclerview-v7:28.0.0'
|
||||
implementation 'com.android.support:design:28.0.0'
|
||||
|
||||
implementation 'com.takisoft.fix:preference-v7:28.0.0.0'
|
||||
implementation 'com.takisoft.fix:preference-v7-extras:28.0.0.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.0.2'
|
||||
implementation 'androidx.cardview:cardview:1.0.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.0.0'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.0.0'
|
||||
implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'
|
||||
|
||||
implementation 'com.google.android.material:material:1.0.0'
|
||||
implementation 'com.google.firebase:firebase-core:16.0.4'
|
||||
implementation 'com.google.firebase:firebase-messaging:17.3.3'
|
||||
|
||||
implementation "android.arch.lifecycle:extensions:1.1.1"
|
||||
implementation 'com.google.firebase:firebase-messaging:17.3.4'
|
||||
implementation 'com.google.android.gms:play-services-ads:17.1.0'
|
||||
implementation 'com.android.billingclient:billing:1.2'
|
||||
|
||||
implementation 'com.squareup.okhttp3:okhttp:3.10.0'
|
||||
implementation 'com.github.kenglxn.QRGen:android:2.5.0'
|
||||
implementation "com.github.DeweyReed:UltimateMusicPicker:2.0.0"
|
||||
implementation 'com.github.duanhong169:colorpicker:1.1.5'
|
||||
}
|
||||
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
|
@@ -1,15 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<manifest
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.blackforestbytes.simplecloudnotifier">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="com.android.vending.BILLING" />
|
||||
|
||||
<application
|
||||
android:allowBackup="false"
|
||||
android:name="SCNApp"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:icon="@drawable/icon"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
android:theme="@style/AppTheme"
|
||||
tools:ignore="GoogleAppIndexingWarning">
|
||||
|
||||
<activity android:name=".view.MainActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
@@ -19,13 +27,17 @@
|
||||
|
||||
<meta-data android:name="com.google.firebase.messaging.default_notification_icon" android:resource="@drawable/icon" />
|
||||
<meta-data android:name="com.google.firebase.messaging.default_notification_color" android:resource="@color/colorAccent" />
|
||||
<meta-data android:name="com.google.android.gms.ads.AD_MANAGER_APP" android:value="true"/>
|
||||
<meta-data android:name="com.google.android.gms.ads.APPLICATION_ID" android:value="ca-app-pub-3320562328966175~7579972005"/>
|
||||
|
||||
<service android:name=".service.FBMService">
|
||||
<service android:name=".service.FBMService" android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="com.google.firebase.MESSAGING_EVENT" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<receiver android:name=".service.BroadcastReceiverService" android:exported="false" />
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
BIN
android/app/src/main/ic_bfb_raster-web.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
android/app/src/main/ic_notification_full-web.png
Normal file
After Width: | Height: | Size: 67 KiB |
@@ -1,26 +1,27 @@
|
||||
package com.blackforestbytes.simplecloudnotifier;
|
||||
|
||||
import android.app.Application;
|
||||
import android.arch.lifecycle.Lifecycle;
|
||||
import android.arch.lifecycle.LifecycleObserver;
|
||||
import android.arch.lifecycle.OnLifecycleEvent;
|
||||
import android.arch.lifecycle.ProcessLifecycleOwner;
|
||||
import android.content.Context;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.blackforestbytes.simplecloudnotifier.model.CMessageList;
|
||||
import com.blackforestbytes.simplecloudnotifier.service.NotificationService;
|
||||
import com.android.billingclient.api.BillingClient;
|
||||
import com.blackforestbytes.simplecloudnotifier.view.AccountFragment;
|
||||
import com.blackforestbytes.simplecloudnotifier.view.MainActivity;
|
||||
import com.blackforestbytes.simplecloudnotifier.view.TabAdapter;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import androidx.lifecycle.Lifecycle;
|
||||
import androidx.lifecycle.LifecycleObserver;
|
||||
import androidx.lifecycle.OnLifecycleEvent;
|
||||
import androidx.lifecycle.ProcessLifecycleOwner;
|
||||
|
||||
public class SCNApp extends Application implements LifecycleObserver
|
||||
{
|
||||
private static SCNApp instance;
|
||||
private static WeakReference<MainActivity> mainActivity;
|
||||
private static WeakReference<MainActivity> mainActivity = new WeakReference<>(null);
|
||||
|
||||
public static final boolean LOCAL_DEBUG = BuildConfig.DEBUG;
|
||||
public static final boolean DEBUG = BuildConfig.DEBUG || !BuildConfig.VERSION_NAME.endsWith(".0");
|
||||
public static final boolean RELEASE = !DEBUG;
|
||||
|
||||
@@ -37,6 +38,11 @@ public class SCNApp extends Application implements LifecycleObserver
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static MainActivity getMainActivity()
|
||||
{
|
||||
return mainActivity.get();
|
||||
}
|
||||
|
||||
public static boolean isBackground()
|
||||
{
|
||||
return isBackground;
|
||||
|
@@ -0,0 +1,23 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.lib.android;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
public final class ThreadUtils
|
||||
{
|
||||
public static void safeSleep(int millisMin, int millisMax)
|
||||
{
|
||||
safeSleep(millisMin + (int)(Math.random()*(millisMax-millisMin)));
|
||||
}
|
||||
|
||||
public static void safeSleep(int millis)
|
||||
{
|
||||
try
|
||||
{
|
||||
Thread.sleep(millis);
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
Log.d("ThreadUtils", e.toString());
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,43 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.lib.collections;
|
||||
|
||||
import com.blackforestbytes.simplecloudnotifier.lib.lambda.Func1to1;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public final class CollectionHelper
|
||||
{
|
||||
public static <T, C> List<T> unique(List<T> input, Func1to1<T, C> mapping)
|
||||
{
|
||||
List<T> output = new ArrayList<>(input.size());
|
||||
|
||||
HashSet<C> seen = new HashSet<>();
|
||||
|
||||
for (T v : input) if (seen.add(mapping.invoke(v))) output.add(v);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
public static <T> List<T> sort(List<T> input, Comparator<T> comparator)
|
||||
{
|
||||
List<T> output = new ArrayList<>(input);
|
||||
Collections.sort(output, comparator);
|
||||
return output;
|
||||
}
|
||||
|
||||
public static <T> void sort_inplace(List<T> input, Comparator<T> comparator)
|
||||
{
|
||||
Collections.sort(input, comparator);
|
||||
}
|
||||
|
||||
public static <T, U extends Comparable<U>> List<T> sort(List<T> input, Func1to1<T, U> mapper)
|
||||
{
|
||||
return sort(input, mapper, 1);
|
||||
}
|
||||
|
||||
public static <T, U extends Comparable<U>> List<T> sort(List<T> input, Func1to1<T, U> mapper, int sortMod)
|
||||
{
|
||||
List<T> output = new ArrayList<>(input);
|
||||
Collections.sort(output, (o1, o2) -> sortMod * mapper.invoke(o1).compareTo(mapper.invoke(o2)));
|
||||
return output;
|
||||
}
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.lib.datatypes;
|
||||
|
||||
public class IntRange
|
||||
{
|
||||
private int Start;
|
||||
public int Start() { return Start; }
|
||||
|
||||
private int End;
|
||||
public int End() { return End; }
|
||||
|
||||
public IntRange(int s, int e) { Start = s; End = e; }
|
||||
|
||||
private IntRange() { }
|
||||
}
|
@@ -0,0 +1,10 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.lib.datatypes;
|
||||
|
||||
public class NInt
|
||||
{
|
||||
public int Value;
|
||||
|
||||
public NInt(int v) { Value = v; }
|
||||
|
||||
private NInt() { }
|
||||
}
|
@@ -0,0 +1,6 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.lib.datatypes;
|
||||
|
||||
public final class Nothing
|
||||
{
|
||||
public final static Nothing Inst = new Nothing();
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.lib.datatypes;
|
||||
|
||||
public class Tuple1<T1>
|
||||
{
|
||||
public final T1 Item1;
|
||||
|
||||
public Tuple1(T1 i1)
|
||||
{
|
||||
Item1 = i1;
|
||||
}
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.lib.datatypes;
|
||||
|
||||
public class Tuple2<T1, T2>
|
||||
{
|
||||
public final T1 Item1;
|
||||
public final T2 Item2;
|
||||
|
||||
public Tuple2(T1 i1, T2 i2)
|
||||
{
|
||||
Item1 = i1;
|
||||
Item2 = i2;
|
||||
}
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.lib.datatypes;
|
||||
|
||||
public class Tuple3<T1, T2, T3>
|
||||
{
|
||||
public final T1 Item1;
|
||||
public final T2 Item2;
|
||||
public final T3 Item3;
|
||||
|
||||
public Tuple3(T1 i1, T2 i2, T3 i3)
|
||||
{
|
||||
Item1 = i1;
|
||||
Item2 = i2;
|
||||
Item3 = i3;
|
||||
}
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.lib.datatypes;
|
||||
|
||||
public class Tuple4<T1, T2, T3, T4>
|
||||
{
|
||||
public final T1 Item1;
|
||||
public final T2 Item2;
|
||||
public final T3 Item3;
|
||||
public final T4 Item4;
|
||||
|
||||
public Tuple4(T1 i1, T2 i2, T3 i3, T4 i4)
|
||||
{
|
||||
Item1 = i1;
|
||||
Item2 = i2;
|
||||
Item3 = i3;
|
||||
Item4 = i4;
|
||||
}
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.lib.datatypes;
|
||||
|
||||
public class Tuple5<T1, T2, T3, T4, T5>
|
||||
{
|
||||
public final T1 Item1;
|
||||
public final T2 Item2;
|
||||
public final T3 Item3;
|
||||
public final T4 Item4;
|
||||
public final T5 Item5;
|
||||
|
||||
public Tuple5(T1 i1, T2 i2, T3 i3, T4 i4, T5 i5)
|
||||
{
|
||||
Item1 = i1;
|
||||
Item2 = i2;
|
||||
Item3 = i3;
|
||||
Item4 = i4;
|
||||
Item5 = i5;
|
||||
}
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.lib.lambda;
|
||||
|
||||
import android.widget.SeekBar;
|
||||
|
||||
public final class FI
|
||||
{
|
||||
private FI() throws InstantiationException { throw new InstantiationException(); }
|
||||
|
||||
public static SeekBar.OnSeekBarChangeListener SeekBarChanged(Func3to0<SeekBar, Integer, Boolean> action)
|
||||
{
|
||||
return new SeekBar.OnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
action.invoke(seekBar, progress, fromUser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.lib.lambda;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface Func0to0 {
|
||||
|
||||
Func0to0 EMPTY = ()->{};
|
||||
|
||||
void invoke();
|
||||
}
|
@@ -0,0 +1,6 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.lib.lambda;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface Func0to1<TResult> {
|
||||
TResult invoke();
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.lib.lambda;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface Func0to1WithIOException<TResult> {
|
||||
TResult invoke() throws IOException;
|
||||
}
|
@@ -0,0 +1,6 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.lib.lambda;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface Func1to0<TInput1> {
|
||||
void invoke(TInput1 value);
|
||||
}
|
@@ -0,0 +1,6 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.lib.lambda;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface Func1to1<TInput1, TResult> {
|
||||
TResult invoke(TInput1 value);
|
||||
}
|
@@ -0,0 +1,6 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.lib.lambda;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface Func2to0<TInput1, TInput2> {
|
||||
void invoke(TInput1 value1, TInput2 value2);
|
||||
}
|
@@ -0,0 +1,6 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.lib.lambda;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface Func2to1<TInput1, TInput2, TResult> {
|
||||
TResult invoke(TInput1 value1, TInput2 value2);
|
||||
}
|
@@ -0,0 +1,6 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.lib.lambda;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface Func3to0<TInput1, TInput2, TInput3> {
|
||||
void invoke(TInput1 value1, TInput2 value2, TInput3 value3);
|
||||
}
|
@@ -0,0 +1,6 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.lib.lambda;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface Func4to0<TInput1, TInput2, TInput3, TInput4> {
|
||||
void invoke(TInput1 value1, TInput2 value2, TInput3 value3, TInput4 value4);
|
||||
}
|
@@ -0,0 +1,6 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.lib.lambda;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface Func5to0<TInput1, TInput2, TInput3, TInput4, TInput5> {
|
||||
void invoke(TInput1 value1, TInput2 value2, TInput3 value3, TInput4 value4, TInput5 value5);
|
||||
}
|
@@ -0,0 +1,231 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.lib.string;
|
||||
|
||||
// from MonoSAMFramework.Portable.DebugTools.CompactJsonFormatter
|
||||
public class CompactJsonFormatter
|
||||
{
|
||||
private static final String INDENT_STRING = " ";
|
||||
|
||||
public static String formatJSON(String str, int maxIndent)
|
||||
{
|
||||
int indent = 0;
|
||||
boolean quoted = false;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
char last = ' ';
|
||||
for (int i = 0; i < str.length(); i++)
|
||||
{
|
||||
char ch = str.charAt(i);
|
||||
switch (ch)
|
||||
{
|
||||
case '\r':
|
||||
case '\n':
|
||||
break;
|
||||
case '{':
|
||||
case '[':
|
||||
sb.append(ch);
|
||||
last = ch;
|
||||
if (!quoted)
|
||||
{
|
||||
indent++;
|
||||
if (indent >= maxIndent) break;
|
||||
sb.append("\n");
|
||||
for (int ix = 0; ix < indent; ix++) sb.append(INDENT_STRING);
|
||||
last = ' ';
|
||||
}
|
||||
break;
|
||||
case '}':
|
||||
case ']':
|
||||
if (!quoted)
|
||||
{
|
||||
indent--;
|
||||
if (indent + 1 >= maxIndent) { sb.append(ch); break; }
|
||||
sb.append("\n");
|
||||
for (int ix = 0; ix < indent; ix++) sb.append(INDENT_STRING);
|
||||
}
|
||||
sb.append(ch);
|
||||
last = ch;
|
||||
break;
|
||||
case '"':
|
||||
sb.append(ch);
|
||||
last = ch;
|
||||
boolean escaped = false;
|
||||
int index = i;
|
||||
while (index > 0 && str.charAt(--index) == '\\')
|
||||
escaped = !escaped;
|
||||
if (!escaped)
|
||||
quoted = !quoted;
|
||||
break;
|
||||
case ',':
|
||||
sb.append(ch);
|
||||
last = ch;
|
||||
if (!quoted)
|
||||
{
|
||||
if (indent >= maxIndent) { sb.append(' '); last = ' '; break; }
|
||||
sb.append("\n");
|
||||
for (int ix = 0; ix < indent; ix++) sb.append(INDENT_STRING);
|
||||
}
|
||||
break;
|
||||
case ':':
|
||||
sb.append(ch);
|
||||
last = ch;
|
||||
if (!quoted) { sb.append(" "); last = ' '; }
|
||||
break;
|
||||
case ' ':
|
||||
case '\t':
|
||||
if (quoted)
|
||||
{
|
||||
sb.append(ch);
|
||||
last = ch;
|
||||
}
|
||||
else if (last != ' ')
|
||||
{
|
||||
sb.append(' ');
|
||||
last = ' ';
|
||||
}
|
||||
break;
|
||||
default:
|
||||
sb.append(ch);
|
||||
last = ch;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String compressJson(String str, int compressionLevel)
|
||||
{
|
||||
int indent = 0;
|
||||
boolean quoted = false;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
char last = ' ';
|
||||
int compress = 0;
|
||||
for (int i = 0; i < str.length(); i++)
|
||||
{
|
||||
char ch = str.charAt(i);
|
||||
switch (ch)
|
||||
{
|
||||
case '\r':
|
||||
case '\n':
|
||||
break;
|
||||
case '{':
|
||||
case '[':
|
||||
|
||||
sb.append(ch);
|
||||
last = ch;
|
||||
if (!quoted)
|
||||
{
|
||||
if (compress == 0 && getJsonDepth(str, i) <= compressionLevel)
|
||||
compress = 1;
|
||||
else if (compress > 0)
|
||||
compress++;
|
||||
|
||||
indent++;
|
||||
if (compress > 0) break;
|
||||
sb.append("\n");
|
||||
for (int ix = 0; ix < indent; ix++) sb.append(INDENT_STRING);
|
||||
last = ' ';
|
||||
}
|
||||
break;
|
||||
case '}':
|
||||
case ']':
|
||||
if (!quoted)
|
||||
{
|
||||
indent--;
|
||||
if (compress > 0) { compress--; sb.append(ch); break; }
|
||||
compress--;
|
||||
sb.append("\n");
|
||||
for (int ix = 0; ix < indent; ix++) sb.append(INDENT_STRING);
|
||||
}
|
||||
sb.append(ch);
|
||||
last = ch;
|
||||
break;
|
||||
case '"':
|
||||
sb.append(ch);
|
||||
last = ch;
|
||||
boolean escaped = false;
|
||||
int index = i;
|
||||
while (index > 0 && str.charAt(--index) == '\\')
|
||||
escaped = !escaped;
|
||||
if (!escaped)
|
||||
quoted = !quoted;
|
||||
break;
|
||||
case ',':
|
||||
sb.append(ch);
|
||||
last = ch;
|
||||
if (!quoted)
|
||||
{
|
||||
if (compress > 0) { sb.append(' '); last = ' '; break; }
|
||||
sb.append("\n");
|
||||
for (int ix = 0; ix < indent; ix++) sb.append(INDENT_STRING);
|
||||
}
|
||||
break;
|
||||
case ':':
|
||||
sb.append(ch);
|
||||
last = ch;
|
||||
if (!quoted) { sb.append(" "); last = ' '; }
|
||||
break;
|
||||
case ' ':
|
||||
case '\t':
|
||||
if (quoted)
|
||||
{
|
||||
sb.append(ch);
|
||||
last = ch;
|
||||
}
|
||||
else if (last != ' ')
|
||||
{
|
||||
sb.append(' ');
|
||||
last = ' ';
|
||||
}
|
||||
break;
|
||||
default:
|
||||
sb.append(ch);
|
||||
last = ch;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static int getJsonDepth(String str, int i)
|
||||
{
|
||||
int maxindent = 0;
|
||||
int indent = 0;
|
||||
boolean quoted = false;
|
||||
for (; i < str.length(); i++)
|
||||
{
|
||||
char ch = str.charAt(i);
|
||||
switch (ch)
|
||||
{
|
||||
case '{':
|
||||
case '[':
|
||||
if (!quoted)
|
||||
{
|
||||
indent++;
|
||||
maxindent = Math.max(indent, maxindent);
|
||||
}
|
||||
break;
|
||||
|
||||
case '}':
|
||||
case ']':
|
||||
if (!quoted)
|
||||
{
|
||||
indent--;
|
||||
if (indent <= 0) return maxindent;
|
||||
}
|
||||
break;
|
||||
|
||||
case '"':
|
||||
boolean escaped = false;
|
||||
int index = i;
|
||||
while (index > 0 && str.charAt(--index) == '\\')
|
||||
escaped = !escaped;
|
||||
if (!escaped)
|
||||
quoted = !quoted;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return maxindent;
|
||||
}
|
||||
}
|
@@ -0,0 +1,98 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.lib.string;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import com.blackforestbytes.simplecloudnotifier.SCNApp;
|
||||
import com.blackforestbytes.simplecloudnotifier.lib.lambda.Func1to1;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.List;
|
||||
|
||||
public class Str
|
||||
{
|
||||
public final static String Empty = "";
|
||||
|
||||
public static String format(String fmt, Object... data)
|
||||
{
|
||||
return MessageFormat.format(fmt, data);
|
||||
}
|
||||
|
||||
public static String rformat(int fmtResId, Object... data)
|
||||
{
|
||||
Context inst = SCNApp.getContext();
|
||||
if (inst == null)
|
||||
{
|
||||
Log.e("StringFormat", "rformat::NoInstance --> inst==null for" + fmtResId);
|
||||
return "?ERR?";
|
||||
}
|
||||
|
||||
return MessageFormat.format(inst.getResources().getString(fmtResId), data);
|
||||
}
|
||||
|
||||
public static String firstLine(String content)
|
||||
{
|
||||
int idx = content.indexOf('\n');
|
||||
if (idx == -1) return content;
|
||||
|
||||
if (idx == 0) return Str.Empty;
|
||||
|
||||
if (content.charAt(idx-1) == '\r') return content.substring(0, idx-1);
|
||||
|
||||
return content.substring(0, idx);
|
||||
}
|
||||
|
||||
public static boolean isNullOrWhitespace(String str)
|
||||
{
|
||||
return str == null || str.length() == 0 || str.trim().length() == 0;
|
||||
}
|
||||
|
||||
public static boolean isNullOrEmpty(String str)
|
||||
{
|
||||
return str == null || str.length() == 0;
|
||||
}
|
||||
|
||||
public static boolean equals(String a, String b)
|
||||
{
|
||||
if (a == null) return (b == null);
|
||||
return a.equals(b);
|
||||
}
|
||||
|
||||
public static String join(String sep, List<String> list)
|
||||
{
|
||||
StringBuilder b = new StringBuilder();
|
||||
boolean first = true;
|
||||
for (String v : list)
|
||||
{
|
||||
if (!first) b.append(sep);
|
||||
b.append(v);
|
||||
first = false;
|
||||
}
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
public static <T> String join(String sep, List<T> list, Func1to1<T, String> map)
|
||||
{
|
||||
StringBuilder b = new StringBuilder();
|
||||
boolean first = true;
|
||||
for (T v : list)
|
||||
{
|
||||
if (!first) b.append(sep);
|
||||
b.append(map.invoke(v));
|
||||
first = false;
|
||||
}
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
public static Integer tryParseToInt(String s)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Integer.parseInt(s);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@@ -9,7 +9,8 @@ import java.util.TimeZone;
|
||||
|
||||
public class CMessage
|
||||
{
|
||||
public final long Timestamp ;
|
||||
public final long SCN_ID;
|
||||
public final long Timestamp;
|
||||
public final String Title;
|
||||
public final String Content;
|
||||
public final PriorityEnum Priority;
|
||||
@@ -21,8 +22,9 @@ public class CMessage
|
||||
_format.setTimeZone(TimeZone.getDefault());
|
||||
}
|
||||
|
||||
public CMessage(long t, String mt, String mc, PriorityEnum p)
|
||||
public CMessage(long id, long t, String mt, String mc, PriorityEnum p)
|
||||
{
|
||||
SCN_ID = id;
|
||||
Timestamp = t;
|
||||
Title = mt;
|
||||
Content = mc;
|
||||
|
@@ -3,15 +3,23 @@ package com.blackforestbytes.simplecloudnotifier.model;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import com.blackforestbytes.simplecloudnotifier.lib.collections.CollectionHelper;
|
||||
import com.blackforestbytes.simplecloudnotifier.lib.string.Str;
|
||||
import com.blackforestbytes.simplecloudnotifier.view.MessageAdapter;
|
||||
import com.blackforestbytes.simplecloudnotifier.SCNApp;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class CMessageList
|
||||
{
|
||||
private final Object msg_lock = new Object();
|
||||
|
||||
public ArrayList<CMessage> Messages;
|
||||
public Set<String> AllAcks;
|
||||
|
||||
private ArrayList<WeakReference<MessageAdapter>> _listener = new ArrayList<>();
|
||||
|
||||
@@ -27,8 +35,11 @@ public class CMessageList
|
||||
}
|
||||
|
||||
private CMessageList()
|
||||
{
|
||||
synchronized (msg_lock)
|
||||
{
|
||||
Messages = new ArrayList<>();
|
||||
AllAcks = new HashSet<>();
|
||||
|
||||
SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("CMessageList", Context.MODE_PRIVATE);
|
||||
int count = sharedPref.getInt("message_count", 0);
|
||||
@@ -38,44 +49,75 @@ public class CMessageList
|
||||
String title = sharedPref.getString("message["+i+"].title", "");
|
||||
String content = sharedPref.getString("message["+i+"].content", "");
|
||||
PriorityEnum prio = PriorityEnum.parseAPI(sharedPref.getInt("message["+i+"].priority", 1));
|
||||
long scnid = sharedPref.getLong("message["+i+"].scnid", 0);
|
||||
|
||||
Messages.add(new CMessage(time, title, content, prio));
|
||||
Messages.add(new CMessage(scnid, time, title, content, prio));
|
||||
}
|
||||
|
||||
AllAcks = sharedPref.getStringSet("acks", new HashSet<>());
|
||||
}
|
||||
}
|
||||
|
||||
public CMessage add(final long time, final String title, final String content, final PriorityEnum pe)
|
||||
public CMessage add(final long scnid, final long time, final String title, final String content, final PriorityEnum pe)
|
||||
{
|
||||
CMessage msg = new CMessage(time, title, content, pe);
|
||||
CMessage msg = new CMessage(scnid, time, title, content, pe);
|
||||
|
||||
boolean run = SCNApp.runOnUiThread(() ->
|
||||
{
|
||||
SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("CMessageList", Context.MODE_PRIVATE);
|
||||
int count = sharedPref.getInt("message_count", 0);
|
||||
|
||||
synchronized (msg_lock)
|
||||
{
|
||||
Messages.add(msg);
|
||||
AllAcks.add(Long.toHexString(msg.SCN_ID));
|
||||
|
||||
while (Messages.size()>SCNSettings.inst().LocalCacheSize) Messages.remove(0);
|
||||
}
|
||||
|
||||
if (Messages.size()>1 && Messages.get(Messages.size()-2).Timestamp < msg.Timestamp)
|
||||
{
|
||||
// quick save
|
||||
|
||||
SharedPreferences.Editor e = sharedPref.edit();
|
||||
|
||||
Messages.add(msg);
|
||||
e.putInt("message_count", count+1);
|
||||
e.putLong("message["+count+"].timestamp", time);
|
||||
e.putInt( "message_count", count+1);
|
||||
e.putLong( "message["+count+"].timestamp", time);
|
||||
e.putString("message["+count+"].title", title);
|
||||
e.putString("message["+count+"].content", content);
|
||||
e.putInt("message["+count+"].priority", pe.ID);
|
||||
e.putInt( "message["+count+"].priority", pe.ID);
|
||||
e.putLong( "message["+count+"].scnid", scnid);
|
||||
|
||||
e.putStringSet("acks", AllAcks);
|
||||
|
||||
e.apply();
|
||||
}
|
||||
else
|
||||
{
|
||||
// full save
|
||||
|
||||
fullSave(); // does sort in here
|
||||
}
|
||||
|
||||
|
||||
for (WeakReference<MessageAdapter> ref : _listener)
|
||||
{
|
||||
MessageAdapter a = ref.get();
|
||||
if (a == null) continue;
|
||||
a.customNotifyItemInserted(count);
|
||||
a.customNotifyDataSetChanged();
|
||||
a.scrollToTop();
|
||||
}
|
||||
CleanUpListener();
|
||||
});
|
||||
|
||||
if (!run)
|
||||
{
|
||||
Messages.add(new CMessage(time, title, content, pe));
|
||||
fullSave();
|
||||
synchronized (msg_lock)
|
||||
{
|
||||
Messages.add(new CMessage(scnid, time, title, content, pe));
|
||||
AllAcks.add(Long.toHexString(msg.SCN_ID));
|
||||
}
|
||||
fullSave(); // does sort in here
|
||||
}
|
||||
|
||||
return msg;
|
||||
@@ -97,6 +139,10 @@ public class CMessageList
|
||||
|
||||
public void fullSave()
|
||||
{
|
||||
synchronized (msg_lock)
|
||||
{
|
||||
CollectionHelper.sort_inplace(Messages, (a,b) -> Long.compare(a.Timestamp, b.Timestamp));
|
||||
|
||||
SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("CMessageList", Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor e = sharedPref.edit();
|
||||
|
||||
@@ -106,25 +152,40 @@ public class CMessageList
|
||||
|
||||
for (int i = 0; i < Messages.size(); i++)
|
||||
{
|
||||
e.putLong("message["+i+"].timestamp", Messages.get(i).Timestamp);
|
||||
e.putLong( "message["+i+"].timestamp", Messages.get(i).Timestamp);
|
||||
e.putString("message["+i+"].title", Messages.get(i).Title);
|
||||
e.putString("message["+i+"].content", Messages.get(i).Content);
|
||||
e.putInt("message["+i+"].priority", Messages.get(i).Priority.ID);
|
||||
e.putInt( "message["+i+"].priority", Messages.get(i).Priority.ID);
|
||||
e.putLong( "message["+i+"].scnid", Messages.get(i).SCN_ID);
|
||||
}
|
||||
|
||||
e.putStringSet("acks", AllAcks);
|
||||
|
||||
e.apply();
|
||||
}
|
||||
}
|
||||
|
||||
public CMessage tryGet(int pos)
|
||||
{
|
||||
synchronized (msg_lock)
|
||||
{
|
||||
if (pos < 0 || pos >= Messages.size()) return null;
|
||||
return Messages.get(pos);
|
||||
}
|
||||
}
|
||||
|
||||
public CMessage tryGetFromBack(int pos)
|
||||
{
|
||||
return tryGet(Messages.size() - pos - 1);
|
||||
}
|
||||
|
||||
public int size()
|
||||
{
|
||||
synchronized (msg_lock)
|
||||
{
|
||||
return Messages.size();
|
||||
}
|
||||
}
|
||||
|
||||
public void register(MessageAdapter adp)
|
||||
{
|
||||
@@ -139,4 +200,33 @@ public class CMessageList
|
||||
if (_listener.get(i).get() == null) _listener.remove(i);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isAck(long id)
|
||||
{
|
||||
synchronized (msg_lock)
|
||||
{
|
||||
return AllAcks.contains(Long.toHexString(id));
|
||||
}
|
||||
}
|
||||
|
||||
public CMessage removeFromBack(int pos)
|
||||
{
|
||||
CMessage r;
|
||||
synchronized (msg_lock)
|
||||
{
|
||||
int index = Messages.size() - pos - 1;
|
||||
r = Messages.remove(index);
|
||||
}
|
||||
fullSave(); // does sort in here
|
||||
return r;
|
||||
}
|
||||
|
||||
public void insert(int index, CMessage item)
|
||||
{
|
||||
synchronized (msg_lock)
|
||||
{
|
||||
Messages.add(index, item);
|
||||
}
|
||||
fullSave(); // does sort in here
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,34 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.model;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.media.RingtoneManager;
|
||||
import android.net.Uri;
|
||||
|
||||
public class NotificationSettings
|
||||
{
|
||||
public boolean EnableSound;
|
||||
public String SoundName;
|
||||
public String SoundSource;
|
||||
public boolean RepeatSound;
|
||||
|
||||
public boolean ForceVolume;
|
||||
public int ForceVolumeValue;
|
||||
|
||||
public boolean EnableLED;
|
||||
public int LEDColor;
|
||||
|
||||
public boolean EnableVibration;
|
||||
|
||||
public NotificationSettings(PriorityEnum p)
|
||||
{
|
||||
EnableSound = (p == PriorityEnum.HIGH);
|
||||
SoundName = "Default";
|
||||
SoundSource = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION).toString();
|
||||
RepeatSound = false;
|
||||
EnableLED = (p == PriorityEnum.HIGH) || (p == PriorityEnum.NORMAL);
|
||||
LEDColor = Color.BLUE;
|
||||
EnableVibration = (p == PriorityEnum.HIGH) || (p == PriorityEnum.NORMAL);
|
||||
ForceVolume = false;
|
||||
ForceVolumeValue = 50;
|
||||
}
|
||||
}
|
@@ -6,21 +6,35 @@ import android.content.SharedPreferences;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.billingclient.api.Purchase;
|
||||
import com.blackforestbytes.simplecloudnotifier.SCNApp;
|
||||
import com.blackforestbytes.simplecloudnotifier.lib.string.Str;
|
||||
import com.blackforestbytes.simplecloudnotifier.service.IABService;
|
||||
import com.google.firebase.iid.FirebaseInstanceId;
|
||||
|
||||
public class SCNSettings
|
||||
{
|
||||
private final static Object _lock = new Object();
|
||||
private static SCNSettings _inst = null;
|
||||
private static volatile SCNSettings _inst = null;
|
||||
public static SCNSettings inst()
|
||||
{
|
||||
SCNSettings local = _inst;
|
||||
if (local == null)
|
||||
{
|
||||
synchronized (_lock)
|
||||
{
|
||||
if (_inst != null) return _inst;
|
||||
return _inst = new SCNSettings();
|
||||
local = _inst;
|
||||
if (local == null) _inst = local = new SCNSettings();
|
||||
}
|
||||
}
|
||||
return local;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------
|
||||
|
||||
public final static Integer[] CHOOSABLE_CACHE_SIZES = new Integer[]{20, 50, 100, 200, 500, 1000, 2000, 5000};
|
||||
|
||||
// ------------------------------------------------------------
|
||||
|
||||
public int quota_curr;
|
||||
public int quota_max;
|
||||
@@ -30,16 +44,69 @@ public class SCNSettings
|
||||
public String fcm_token_local;
|
||||
public String fcm_token_server;
|
||||
|
||||
public String promode_token;
|
||||
public boolean promode_local;
|
||||
public boolean promode_server;
|
||||
|
||||
// ------------------------------------------------------------
|
||||
|
||||
public boolean Enabled = true;
|
||||
public int LocalCacheSize = 500;
|
||||
public boolean EnableDeleteSwipe = true;
|
||||
|
||||
public final NotificationSettings PriorityLow = new NotificationSettings(PriorityEnum.LOW);
|
||||
public final NotificationSettings PriorityNorm = new NotificationSettings(PriorityEnum.NORMAL);
|
||||
public final NotificationSettings PriorityHigh = new NotificationSettings(PriorityEnum.HIGH);
|
||||
|
||||
// ------------------------------------------------------------
|
||||
|
||||
public SCNSettings()
|
||||
{
|
||||
SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("Config", Context.MODE_PRIVATE);
|
||||
|
||||
quota_curr = sharedPref.getInt("quota_curr", 0);
|
||||
quota_max = sharedPref.getInt("quota_max", 0);
|
||||
user_id = sharedPref.getInt("user_id", -1);
|
||||
quota_curr = sharedPref.getInt( "quota_curr", 0);
|
||||
quota_max = sharedPref.getInt( "quota_max", 0);
|
||||
user_id = sharedPref.getInt( "user_id", -1);
|
||||
user_key = sharedPref.getString("user_key", "");
|
||||
fcm_token_local = sharedPref.getString("fcm_token_local", "");
|
||||
fcm_token_server = sharedPref.getString("fcm_token_server", "");
|
||||
promode_local = sharedPref.getBoolean("promode_local", false);
|
||||
promode_server = sharedPref.getBoolean("promode_server", false);
|
||||
promode_token = sharedPref.getString("promode_token", "");
|
||||
|
||||
Enabled = sharedPref.getBoolean("app_enabled", Enabled);
|
||||
LocalCacheSize = sharedPref.getInt("local_cache_size", LocalCacheSize);
|
||||
EnableDeleteSwipe = sharedPref.getBoolean("do_del_swipe", EnableDeleteSwipe);
|
||||
|
||||
PriorityLow.EnableLED = sharedPref.getBoolean("priority_low:enabled_led", PriorityLow.EnableLED);
|
||||
PriorityLow.EnableSound = sharedPref.getBoolean("priority_low:enabled_sound", PriorityLow.EnableSound);
|
||||
PriorityLow.EnableVibration = sharedPref.getBoolean("priority_low:enabled_vibration", PriorityLow.EnableVibration);
|
||||
PriorityLow.RepeatSound = sharedPref.getBoolean("priority_low:repeat_sound", PriorityLow.RepeatSound);
|
||||
PriorityLow.SoundName = sharedPref.getString( "priority_low:sound_name", PriorityLow.SoundName);
|
||||
PriorityLow.SoundSource = sharedPref.getString( "priority_low:sound_source", PriorityLow.SoundSource);
|
||||
PriorityLow.LEDColor = sharedPref.getInt( "priority_low:led_color", PriorityLow.LEDColor);
|
||||
PriorityLow.ForceVolume = sharedPref.getBoolean("priority_low:force_volume", PriorityLow.ForceVolume);
|
||||
PriorityLow.ForceVolumeValue = sharedPref.getInt( "priority_low:force_volume_value", PriorityLow.ForceVolumeValue);
|
||||
|
||||
PriorityNorm.EnableLED = sharedPref.getBoolean("priority_norm:enabled_led", PriorityNorm.EnableLED);
|
||||
PriorityNorm.EnableSound = sharedPref.getBoolean("priority_norm:enabled_sound", PriorityNorm.EnableSound);
|
||||
PriorityNorm.EnableVibration = sharedPref.getBoolean("priority_norm:enabled_vibration", PriorityNorm.EnableVibration);
|
||||
PriorityNorm.RepeatSound = sharedPref.getBoolean("priority_norm:repeat_sound", PriorityNorm.RepeatSound);
|
||||
PriorityNorm.SoundName = sharedPref.getString( "priority_norm:sound_name", PriorityNorm.SoundName);
|
||||
PriorityNorm.SoundSource = sharedPref.getString( "priority_norm:sound_source", PriorityNorm.SoundSource);
|
||||
PriorityNorm.LEDColor = sharedPref.getInt( "priority_norm:led_color", PriorityNorm.LEDColor);
|
||||
PriorityNorm.ForceVolume = sharedPref.getBoolean("priority_norm:force_volume", PriorityNorm.ForceVolume);
|
||||
PriorityNorm.ForceVolumeValue = sharedPref.getInt( "priority_norm:force_volume_value", PriorityNorm.ForceVolumeValue);
|
||||
|
||||
PriorityHigh.EnableLED = sharedPref.getBoolean("priority_high:enabled_led", PriorityHigh.EnableLED);
|
||||
PriorityHigh.EnableSound = sharedPref.getBoolean("priority_high:enabled_sound", PriorityHigh.EnableSound);
|
||||
PriorityHigh.EnableVibration = sharedPref.getBoolean("priority_high:enabled_vibration", PriorityHigh.EnableVibration);
|
||||
PriorityHigh.RepeatSound = sharedPref.getBoolean("priority_high:repeat_sound", PriorityHigh.RepeatSound);
|
||||
PriorityHigh.SoundName = sharedPref.getString( "priority_high:sound_name", PriorityHigh.SoundName);
|
||||
PriorityHigh.SoundSource = sharedPref.getString( "priority_high:sound_source", PriorityHigh.SoundSource);
|
||||
PriorityHigh.LEDColor = sharedPref.getInt( "priority_high:led_color", PriorityHigh.LEDColor);
|
||||
PriorityHigh.ForceVolume = sharedPref.getBoolean("priority_high:force_volume", PriorityHigh.ForceVolume);
|
||||
PriorityHigh.ForceVolumeValue = sharedPref.getInt( "priority_high:force_volume_value", PriorityHigh.ForceVolumeValue);
|
||||
}
|
||||
|
||||
public void save()
|
||||
@@ -47,12 +114,46 @@ public class SCNSettings
|
||||
SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("Config", Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor e = sharedPref.edit();
|
||||
|
||||
e.putInt("quota_curr", quota_curr);
|
||||
e.putInt("quota_max", quota_max);
|
||||
e.putInt("user_id", user_id);
|
||||
e.putString("user_key", user_key);
|
||||
e.putString("fcm_token_local", fcm_token_local);
|
||||
e.putString("fcm_token_server", fcm_token_server);
|
||||
e.putInt( "quota_curr", quota_curr);
|
||||
e.putInt( "quota_max", quota_max);
|
||||
e.putInt( "user_id", user_id);
|
||||
e.putString( "user_key", user_key);
|
||||
e.putString( "fcm_token_local", fcm_token_local);
|
||||
e.putString( "fcm_token_server", fcm_token_server);
|
||||
|
||||
e.putBoolean("app_enabled", Enabled);
|
||||
e.putInt( "local_cache_size", LocalCacheSize);
|
||||
e.putBoolean("do_del_swipe", EnableDeleteSwipe);
|
||||
|
||||
e.putBoolean("priority_low:enabled_led", PriorityLow.EnableLED);
|
||||
e.putBoolean("priority_low:enabled_sound", PriorityLow.EnableSound);
|
||||
e.putBoolean("priority_low:enabled_vibration", PriorityLow.EnableVibration);
|
||||
e.putBoolean("priority_low:repeat_sound", PriorityLow.RepeatSound);
|
||||
e.putString( "priority_low:sound_name", PriorityLow.SoundName);
|
||||
e.putString( "priority_low:sound_source", PriorityLow.SoundSource);
|
||||
e.putInt( "priority_low:led_color", PriorityLow.LEDColor);
|
||||
e.putBoolean("priority_low:force_volume", PriorityLow.ForceVolume);
|
||||
e.putInt( "priority_low:force_volume_value", PriorityLow.ForceVolumeValue);
|
||||
|
||||
e.putBoolean("priority_norm:enabled_led", PriorityNorm.EnableLED);
|
||||
e.putBoolean("priority_norm:enabled_sound", PriorityNorm.EnableSound);
|
||||
e.putBoolean("priority_norm:enabled_vibration", PriorityNorm.EnableVibration);
|
||||
e.putBoolean("priority_norm:repeat_sound", PriorityNorm.RepeatSound);
|
||||
e.putString( "priority_norm:sound_name", PriorityNorm.SoundName);
|
||||
e.putString( "priority_norm:sound_source", PriorityNorm.SoundSource);
|
||||
e.putInt( "priority_norm:led_color", PriorityNorm.LEDColor);
|
||||
e.putBoolean("priority_norm:force_volume", PriorityNorm.ForceVolume);
|
||||
e.putInt( "priority_norm:force_volume_value", PriorityNorm.ForceVolumeValue);
|
||||
|
||||
e.putBoolean("priority_high:enabled_led", PriorityHigh.EnableLED);
|
||||
e.putBoolean("priority_high:enabled_sound", PriorityHigh.EnableSound);
|
||||
e.putBoolean("priority_high:enabled_vibration", PriorityHigh.EnableVibration);
|
||||
e.putBoolean("priority_high:repeat_sound", PriorityHigh.RepeatSound);
|
||||
e.putString( "priority_high:sound_name", PriorityHigh.SoundName);
|
||||
e.putString( "priority_high:sound_source", PriorityHigh.SoundSource);
|
||||
e.putInt( "priority_high:led_color", PriorityHigh.LEDColor);
|
||||
e.putBoolean("priority_high:force_volume", PriorityHigh.ForceVolume);
|
||||
e.putInt( "priority_high:force_volume_value", PriorityHigh.ForceVolumeValue);
|
||||
|
||||
e.apply();
|
||||
}
|
||||
@@ -62,10 +163,12 @@ public class SCNSettings
|
||||
return user_id>=0 && user_key != null && !user_key.isEmpty();
|
||||
}
|
||||
|
||||
public String createOnlineURL()
|
||||
public String createOnlineURL(boolean longurl)
|
||||
{
|
||||
if (!isConnected()) return ServerCommunication.BASE_URL + "index.php";
|
||||
return ServerCommunication.BASE_URL + "index.php?preset_user_id="+user_id+"&preset_user_key="+user_key;
|
||||
String base = longurl ? ServerCommunication.PAGE_URL_LONG : ServerCommunication.PAGE_URL_SHORT;
|
||||
|
||||
if (!isConnected()) return base;
|
||||
return base + "index.php?preset_user_id="+user_id+"&preset_user_key="+user_key;
|
||||
}
|
||||
|
||||
public void setServerToken(String token, View loader)
|
||||
@@ -74,53 +177,85 @@ public class SCNSettings
|
||||
{
|
||||
fcm_token_local = token;
|
||||
save();
|
||||
if (!fcm_token_local.equals(fcm_token_server)) ServerCommunication.update(user_id, user_key, fcm_token_local, loader);
|
||||
if (!fcm_token_local.equals(fcm_token_server)) ServerCommunication.updateFCMToken(user_id, user_key, fcm_token_local, loader);
|
||||
}
|
||||
else
|
||||
{
|
||||
fcm_token_local = token;
|
||||
save();
|
||||
ServerCommunication.register(fcm_token_local, loader);
|
||||
ServerCommunication.register(fcm_token_local, loader, promode_local, promode_token);
|
||||
updateProState(loader);
|
||||
}
|
||||
}
|
||||
|
||||
// called at app start
|
||||
public void work(Activity a)
|
||||
{
|
||||
FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(a, instanceIdResult ->
|
||||
{
|
||||
String newToken = instanceIdResult.getToken();
|
||||
Log.e("FB::GetInstanceId", newToken);
|
||||
Log.d("FB::GetInstanceId", newToken);
|
||||
SCNSettings.inst().setServerToken(newToken, null);
|
||||
}).addOnCompleteListener(r ->
|
||||
{
|
||||
if (isConnected()) ServerCommunication.info(user_id, user_key, null);
|
||||
});
|
||||
|
||||
updateProState(null);
|
||||
}
|
||||
|
||||
// reset account key
|
||||
public void reset(View loader)
|
||||
{
|
||||
if (!isConnected()) return;
|
||||
|
||||
ServerCommunication.update(user_id, user_key, loader);
|
||||
ServerCommunication.resetSecret(user_id, user_key, loader);
|
||||
}
|
||||
|
||||
// refresh account data
|
||||
public void refresh(View loader, Activity a)
|
||||
{
|
||||
if (isConnected())
|
||||
{
|
||||
ServerCommunication.info(user_id, user_key, loader);
|
||||
|
||||
if (promode_server != promode_local) updateProState(loader);
|
||||
|
||||
if (!Str.equals(fcm_token_local, fcm_token_server)) work(a);
|
||||
}
|
||||
else
|
||||
{
|
||||
// get token then register
|
||||
FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(a, instanceIdResult ->
|
||||
{
|
||||
String newToken = instanceIdResult.getToken();
|
||||
Log.e("FB::GetInstanceId", newToken);
|
||||
SCNSettings.inst().setServerToken(newToken, loader);
|
||||
Log.d("FB::GetInstanceId", newToken);
|
||||
SCNSettings.inst().setServerToken(newToken, loader); // does register in here
|
||||
}).addOnCompleteListener(r ->
|
||||
{
|
||||
if (isConnected()) ServerCommunication.info(user_id, user_key, null);
|
||||
if (isConnected()) ServerCommunication.info(user_id, user_key, null); // info again for safety
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void updateProState(View loader)
|
||||
{
|
||||
Purchase purch = IABService.inst().getPurchaseCached(IABService.IAB_PRO_MODE);
|
||||
boolean promode_real = (purch != null);
|
||||
|
||||
if (promode_real != promode_local || promode_real != promode_server)
|
||||
{
|
||||
promode_local = promode_real;
|
||||
|
||||
promode_token = promode_real ? purch.getPurchaseToken() : "";
|
||||
updateProStateOnServer(loader);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateProStateOnServer(View loader)
|
||||
{
|
||||
if (!isConnected()) return;
|
||||
|
||||
ServerCommunication.upgrade(user_id, user_key, loader, promode_local, promode_token);
|
||||
}
|
||||
}
|
||||
|
@@ -4,11 +4,19 @@ import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import com.blackforestbytes.simplecloudnotifier.SCNApp;
|
||||
import com.blackforestbytes.simplecloudnotifier.lib.datatypes.Tuple5;
|
||||
import com.blackforestbytes.simplecloudnotifier.lib.lambda.Func1to0;
|
||||
import com.blackforestbytes.simplecloudnotifier.lib.lambda.Func5to0;
|
||||
import com.blackforestbytes.simplecloudnotifier.lib.string.Str;
|
||||
import com.blackforestbytes.simplecloudnotifier.service.FBMService;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONTokener;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
|
||||
import okhttp3.Call;
|
||||
import okhttp3.Callback;
|
||||
@@ -19,18 +27,20 @@ import okhttp3.ResponseBody;
|
||||
|
||||
public class ServerCommunication
|
||||
{
|
||||
public static final String BASE_URL = "https://scn.blackforestbytes.com/";
|
||||
public static final String PAGE_URL_LONG = "https://simplecloudnotifier.blackforestbytes.com/";
|
||||
public static final String PAGE_URL_SHORT = "https://scn.blackforestbytes.com/";
|
||||
public static final String BASE_URL = "https://scn.blackforestbytes.com/api/";
|
||||
|
||||
private static final OkHttpClient client = new OkHttpClient();
|
||||
|
||||
private ServerCommunication(){ throw new Error("no."); }
|
||||
|
||||
public static void register(String token, View loader)
|
||||
public static void register(String token, View loader, boolean pro, String pro_token)
|
||||
{
|
||||
try
|
||||
{
|
||||
Request request = new Request.Builder()
|
||||
.url(BASE_URL + "register.php?fcm_token="+token)
|
||||
.url(BASE_URL + "register.php?fcm_token=" + token + "&pro=" + pro + "&pro_token=" + URLEncoder.encode(pro_token, "utf-8"))
|
||||
.build();
|
||||
|
||||
client.newCall(request).enqueue(new Callback()
|
||||
@@ -38,7 +48,7 @@ public class ServerCommunication
|
||||
@Override
|
||||
public void onFailure(Call call, IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
Log.e("SC:register", e.toString());
|
||||
SCNApp.showToast("Communication with server failed", 4000);
|
||||
SCNApp.runOnUiThread(() -> { if (loader!=null)loader.setVisibility(View.GONE); });
|
||||
}
|
||||
@@ -52,28 +62,29 @@ public class ServerCommunication
|
||||
if (responseBody == null) throw new IOException("No response");
|
||||
|
||||
String r = responseBody.string();
|
||||
Log.d("Server::Response", r);
|
||||
Log.d("Server::Response", request.url().toString()+"\n"+r);
|
||||
|
||||
JSONObject json = (JSONObject) new JSONTokener(r).nextValue();
|
||||
|
||||
if (!json.getBoolean("success"))
|
||||
if (!json_bool(json, "success"))
|
||||
{
|
||||
SCNApp.showToast(json.getString("message"), 4000);
|
||||
SCNApp.showToast(json_str(json, "message"), 4000);
|
||||
return;
|
||||
}
|
||||
|
||||
SCNSettings.inst().user_id = json.getInt("user_id");
|
||||
SCNSettings.inst().user_key = json.getString("user_key");
|
||||
SCNSettings.inst().user_id = json_int(json, "user_id");
|
||||
SCNSettings.inst().user_key = json_str(json, "user_key");
|
||||
SCNSettings.inst().fcm_token_server = token;
|
||||
SCNSettings.inst().quota_curr = json.getInt("quota");
|
||||
SCNSettings.inst().quota_max = json.getInt("quota_max");
|
||||
SCNSettings.inst().quota_curr = json_int(json, "quota");
|
||||
SCNSettings.inst().quota_max = json_int(json, "quota_max");
|
||||
SCNSettings.inst().promode_server = json_bool(json, "is_pro");
|
||||
SCNSettings.inst().save();
|
||||
|
||||
SCNApp.refreshAccountTab();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
Log.e("SC:register", e.toString());
|
||||
SCNApp.showToast("Communication with server failed", 4000);
|
||||
}
|
||||
finally
|
||||
@@ -85,17 +96,17 @@ public class ServerCommunication
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
Log.e("SC:register", e.toString());
|
||||
SCNApp.showToast("Communication with server failed", 4000);
|
||||
}
|
||||
}
|
||||
|
||||
public static void update(int id, String key, String token, View loader)
|
||||
public static void updateFCMToken(int id, String key, String token, View loader)
|
||||
{
|
||||
try
|
||||
{
|
||||
Request request = new Request.Builder()
|
||||
.url(BASE_URL + "update.php?user_id="+id+"&user_key="+key+"&fcm_token="+token)
|
||||
.url(BASE_URL + "updateFCMToken.php?user_id="+id+"&user_key="+key+"&fcm_token="+token)
|
||||
.build();
|
||||
|
||||
client.newCall(request).enqueue(new Callback()
|
||||
@@ -103,7 +114,7 @@ public class ServerCommunication
|
||||
@Override
|
||||
public void onFailure(Call call, IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
Log.e("SC:update_1", e.toString());
|
||||
SCNApp.showToast("Communication with server failed", 4000);
|
||||
SCNApp.runOnUiThread(() -> { if (loader!=null)loader.setVisibility(View.GONE); });
|
||||
}
|
||||
@@ -117,28 +128,29 @@ public class ServerCommunication
|
||||
if (responseBody == null) throw new IOException("No response");
|
||||
|
||||
String r = responseBody.string();
|
||||
Log.d("Server::Response", r);
|
||||
Log.d("Server::Response", request.url().toString()+"\n"+r);
|
||||
|
||||
JSONObject json = (JSONObject) new JSONTokener(r).nextValue();
|
||||
|
||||
if (!json.getBoolean("success"))
|
||||
if (!json_bool(json, "success"))
|
||||
{
|
||||
SCNApp.showToast(json.getString("message"), 4000);
|
||||
SCNApp.showToast(json_str(json, "message"), 4000);
|
||||
return;
|
||||
}
|
||||
|
||||
SCNSettings.inst().user_id = json.getInt("user_id");
|
||||
SCNSettings.inst().user_key = json.getString("user_key");
|
||||
SCNSettings.inst().user_id = json_int(json, "user_id");
|
||||
SCNSettings.inst().user_key = json_str(json, "user_key");
|
||||
SCNSettings.inst().fcm_token_server = token;
|
||||
SCNSettings.inst().quota_curr = json.getInt("quota");
|
||||
SCNSettings.inst().quota_max = json.getInt("quota_max");
|
||||
SCNSettings.inst().quota_curr = json_int(json, "quota");
|
||||
SCNSettings.inst().quota_max = json_int(json, "quota_max");
|
||||
SCNSettings.inst().promode_server = json_bool(json, "is_pro");
|
||||
SCNSettings.inst().save();
|
||||
|
||||
SCNApp.refreshAccountTab();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
Log.e("SC:update_1", e.toString());
|
||||
SCNApp.showToast("Communication with server failed", 4000);
|
||||
}
|
||||
finally
|
||||
@@ -150,23 +162,23 @@ public class ServerCommunication
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
Log.e("SC:update_1", e.toString());
|
||||
SCNApp.showToast("Communication with server failed", 4000);
|
||||
}
|
||||
}
|
||||
|
||||
public static void update(int id, String key, View loader)
|
||||
public static void resetSecret(int id, String key, View loader)
|
||||
{
|
||||
try
|
||||
{
|
||||
Request request = new Request.Builder()
|
||||
.url(BASE_URL + "update.php?user_id=" + id + "&user_key=" + key)
|
||||
.url(BASE_URL + "updateFCMToken.php?user_id=" + id + "&user_key=" + key)
|
||||
.build();
|
||||
|
||||
client.newCall(request).enqueue(new Callback() {
|
||||
@Override
|
||||
public void onFailure(Call call, IOException e) {
|
||||
e.printStackTrace();
|
||||
Log.e("SC:update_2", e.toString());
|
||||
SCNApp.showToast("Communication with server failed", 4000);
|
||||
}
|
||||
|
||||
@@ -178,24 +190,25 @@ public class ServerCommunication
|
||||
if (responseBody == null) throw new IOException("No response");
|
||||
|
||||
String r = responseBody.string();
|
||||
Log.d("Server::Response", r);
|
||||
Log.d("Server::Response", request.url().toString()+"\n"+r);
|
||||
|
||||
JSONObject json = (JSONObject) new JSONTokener(r).nextValue();
|
||||
|
||||
if (!json.getBoolean("success")) {
|
||||
SCNApp.showToast(json.getString("message"), 4000);
|
||||
if (!json_bool(json, "success")) {
|
||||
SCNApp.showToast(json_str(json, "message"), 4000);
|
||||
return;
|
||||
}
|
||||
|
||||
SCNSettings.inst().user_id = json.getInt("user_id");
|
||||
SCNSettings.inst().user_key = json.getString("user_key");
|
||||
SCNSettings.inst().quota_curr = json.getInt("quota");
|
||||
SCNSettings.inst().quota_max = json.getInt("quota_max");
|
||||
SCNSettings.inst().user_id = json_int(json, "user_id");
|
||||
SCNSettings.inst().user_key = json_str(json, "user_key");
|
||||
SCNSettings.inst().quota_curr = json_int(json, "quota");
|
||||
SCNSettings.inst().quota_max = json_int(json, "quota_max");
|
||||
SCNSettings.inst().promode_server = json_bool(json, "is_pro");
|
||||
SCNSettings.inst().save();
|
||||
|
||||
SCNApp.refreshAccountTab();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e("SC:update_2", e.toString());
|
||||
SCNApp.showToast("Communication with server failed", 4000);
|
||||
} finally {
|
||||
SCNApp.runOnUiThread(() -> {
|
||||
@@ -207,7 +220,7 @@ public class ServerCommunication
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
Log.e("SC:update_2", e.toString());
|
||||
SCNApp.showToast("Communication with server failed", 4000);
|
||||
}
|
||||
}
|
||||
@@ -223,7 +236,7 @@ public class ServerCommunication
|
||||
client.newCall(request).enqueue(new Callback() {
|
||||
@Override
|
||||
public void onFailure(Call call, IOException e) {
|
||||
e.printStackTrace();
|
||||
Log.e("SC:info", e.toString());
|
||||
SCNApp.showToast("Communication with server failed", 4000);
|
||||
SCNApp.runOnUiThread(() -> {
|
||||
if (loader != null) loader.setVisibility(View.GONE);
|
||||
@@ -238,23 +251,47 @@ public class ServerCommunication
|
||||
if (responseBody == null) throw new IOException("No response");
|
||||
|
||||
String r = responseBody.string();
|
||||
Log.d("Server::Response", r);
|
||||
Log.d("Server::Response", request.url().toString()+"\n"+r);
|
||||
|
||||
JSONObject json = (JSONObject) new JSONTokener(r).nextValue();
|
||||
|
||||
if (!json.getBoolean("success")) {
|
||||
SCNApp.showToast(json.getString("message"), 4000);
|
||||
return;
|
||||
}
|
||||
if (!json_bool(json, "success"))
|
||||
{
|
||||
SCNApp.showToast(json_str(json, "message"), 4000);
|
||||
|
||||
SCNSettings.inst().user_id = json.getInt("user_id");
|
||||
SCNSettings.inst().quota_curr = json.getInt("quota");
|
||||
SCNSettings.inst().quota_max = json.getInt("quota_max");
|
||||
int errid = json.optInt("errid", 0);
|
||||
|
||||
if (errid == 201 || errid == 202 || errid == 203 || errid == 204)
|
||||
{
|
||||
// user not found or auth failed
|
||||
|
||||
SCNSettings.inst().user_id = -1;
|
||||
SCNSettings.inst().user_key = "";
|
||||
SCNSettings.inst().quota_curr = 0;
|
||||
SCNSettings.inst().quota_max = 0;
|
||||
SCNSettings.inst().promode_server = false;
|
||||
SCNSettings.inst().fcm_token_server = "";
|
||||
SCNSettings.inst().save();
|
||||
|
||||
SCNApp.refreshAccountTab();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
SCNSettings.inst().user_id = json_int(json, "user_id");
|
||||
SCNSettings.inst().quota_curr = json_int(json, "quota");
|
||||
SCNSettings.inst().quota_max = json_int(json, "quota_max");
|
||||
SCNSettings.inst().promode_server = json_bool(json, "is_pro");
|
||||
if (!json_bool(json, "fcm_token_set")) SCNSettings.inst().fcm_token_server = "";
|
||||
SCNSettings.inst().save();
|
||||
|
||||
SCNApp.refreshAccountTab();
|
||||
|
||||
if (json_int(json, "unack_count")>0) ServerCommunication.requery(id, key, loader);
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e("SC:info", e.toString());
|
||||
SCNApp.showToast("Communication with server failed", 4000);
|
||||
} finally {
|
||||
SCNApp.runOnUiThread(() -> {
|
||||
@@ -265,9 +302,266 @@ public class ServerCommunication
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.e("SC:info", e.toString());
|
||||
SCNApp.showToast("Communication with server failed", 4000);
|
||||
}
|
||||
}
|
||||
|
||||
public static void requery(int id, String key, View loader)
|
||||
{
|
||||
try
|
||||
{
|
||||
Request request = new Request.Builder()
|
||||
.url(BASE_URL + "requery.php?user_id=" + id + "&user_key=" + key)
|
||||
.build();
|
||||
|
||||
client.newCall(request).enqueue(new Callback() {
|
||||
@Override
|
||||
public void onFailure(Call call, IOException e) {
|
||||
Log.e("SC:requery", e.toString());
|
||||
SCNApp.showToast("Communication with server failed", 4000);
|
||||
SCNApp.runOnUiThread(() -> {
|
||||
if (loader != null) loader.setVisibility(View.GONE);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(Call call, Response response) {
|
||||
try (ResponseBody responseBody = response.body()) {
|
||||
if (!response.isSuccessful())
|
||||
throw new IOException("Unexpected code " + response);
|
||||
if (responseBody == null) throw new IOException("No response");
|
||||
|
||||
String r = responseBody.string();
|
||||
Log.d("Server::Response", request.url().toString()+"\n"+r);
|
||||
|
||||
JSONObject json = (JSONObject) new JSONTokener(r).nextValue();
|
||||
|
||||
if (!json_bool(json, "success"))
|
||||
{
|
||||
SCNApp.showToast(json_str(json, "message"), 4000);
|
||||
return;
|
||||
}
|
||||
|
||||
int count = json_int(json, "count");
|
||||
JSONArray arr = json.getJSONArray("data");
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
JSONObject o = arr.getJSONObject(i);
|
||||
|
||||
long time = json_lng(o, "timestamp");
|
||||
String title = json_str(o, "title");
|
||||
String content = json_str(o, "body");
|
||||
PriorityEnum prio = PriorityEnum.parseAPI(json_int(o, "priority"));
|
||||
long scn_id = json_lng(o, "scn_msg_id");
|
||||
|
||||
FBMService.recieveData(time, title, content, prio, scn_id, true);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e("SC:info", e.toString());
|
||||
SCNApp.showToast("Communication with server failed", 4000);
|
||||
} finally {
|
||||
SCNApp.runOnUiThread(() -> {
|
||||
if (loader != null) loader.setVisibility(View.GONE);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.e("SC:requery", e.toString());
|
||||
SCNApp.showToast("Communication with server failed", 4000);
|
||||
}
|
||||
}
|
||||
|
||||
public static void upgrade(int id, String key, View loader, boolean pro, String pro_token)
|
||||
{
|
||||
try
|
||||
{
|
||||
SCNApp.runOnUiThread(() -> { if (loader != null) loader.setVisibility(View.GONE); });
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(BASE_URL + "upgrade.php?user_id=" + id + "&user_key=" + key + "&pro=" + pro + "&pro_token=" + URLEncoder.encode(pro_token, "utf-8"))
|
||||
.build();
|
||||
|
||||
client.newCall(request).enqueue(new Callback() {
|
||||
@Override
|
||||
public void onFailure(Call call, IOException e) {
|
||||
Log.e("SC:upgrade", e.toString());
|
||||
SCNApp.showToast("Communication with server failed", 4000);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(Call call, Response response) {
|
||||
try (ResponseBody responseBody = response.body()) {
|
||||
if (!response.isSuccessful())
|
||||
throw new IOException("Unexpected code " + response);
|
||||
if (responseBody == null) throw new IOException("No response");
|
||||
|
||||
String r = responseBody.string();
|
||||
Log.d("Server::Response", request.url().toString()+"\n"+r);
|
||||
|
||||
JSONObject json = (JSONObject) new JSONTokener(r).nextValue();
|
||||
|
||||
if (!json_bool(json, "success")) {
|
||||
SCNApp.showToast(json_str(json, "message"), 4000);
|
||||
return;
|
||||
}
|
||||
|
||||
SCNSettings.inst().user_id = json_int(json, "user_id");
|
||||
SCNSettings.inst().quota_curr = json_int(json, "quota");
|
||||
SCNSettings.inst().quota_max = json_int(json, "quota_max");
|
||||
SCNSettings.inst().promode_server = json_bool(json, "is_pro");
|
||||
SCNSettings.inst().save();
|
||||
|
||||
SCNApp.refreshAccountTab();
|
||||
} catch (Exception e) {
|
||||
Log.e("SC:upgrade", e.toString());
|
||||
SCNApp.showToast("Communication with server failed", 4000);
|
||||
} finally {
|
||||
SCNApp.runOnUiThread(() -> { if (loader != null) loader.setVisibility(View.GONE); });
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
SCNApp.showToast("Communication with server failed", 4000);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ack(int id, String key, long msg_scn_id)
|
||||
{
|
||||
try
|
||||
{
|
||||
Request request = new Request.Builder()
|
||||
.url(BASE_URL + "ack.php?user_id=" + id + "&user_key=" + key + "&scn_msg_id=" + msg_scn_id)
|
||||
.build();
|
||||
|
||||
client.newCall(request).enqueue(new Callback() {
|
||||
@Override
|
||||
public void onFailure(Call call, IOException e) {
|
||||
Log.e("SC:ack", e.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(Call call, Response response)
|
||||
{
|
||||
try (ResponseBody responseBody = response.body()) {
|
||||
if (!response.isSuccessful())
|
||||
throw new IOException("Unexpected code " + response);
|
||||
if (responseBody == null) throw new IOException("No response");
|
||||
|
||||
String r = responseBody.string();
|
||||
Log.d("Server::Response", request.url().toString()+"\n"+r);
|
||||
|
||||
JSONObject json = (JSONObject) new JSONTokener(r).nextValue();
|
||||
|
||||
if (!json_bool(json, "success")) SCNApp.showToast(json_str(json, "message"), 4000);
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e("SC:ack", e.toString());
|
||||
SCNApp.showToast("Communication with server failed", 4000);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.e("SC:ack", e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public static void expand(int id, String key, long scn_msg_id, View loader, Func5to0<String, String, PriorityEnum, Long, Long> okResult)
|
||||
{
|
||||
try
|
||||
{
|
||||
Request request = new Request.Builder()
|
||||
.url(BASE_URL + "expand.php?user_id=" + id + "&user_key=" + key + "&scn_msg_id=" + scn_msg_id)
|
||||
.build();
|
||||
|
||||
client.newCall(request).enqueue(new Callback() {
|
||||
@Override
|
||||
public void onFailure(Call call, IOException e) {
|
||||
Log.e("SC:expand", e.toString());
|
||||
SCNApp.showToast("Communication with server failed", 4000);
|
||||
SCNApp.runOnUiThread(() -> {
|
||||
if (loader != null) loader.setVisibility(View.GONE);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(Call call, Response response) {
|
||||
try (ResponseBody responseBody = response.body()) {
|
||||
if (!response.isSuccessful())
|
||||
throw new IOException("Unexpected code " + response);
|
||||
if (responseBody == null) throw new IOException("No response");
|
||||
|
||||
String r = responseBody.string();
|
||||
Log.d("Server::Response", request.url().toString()+"\n"+r);
|
||||
|
||||
JSONObject json = (JSONObject) new JSONTokener(r).nextValue();
|
||||
|
||||
if (!json_bool(json, "success"))
|
||||
{
|
||||
SCNApp.showToast(json_str(json, "message"), 4000);
|
||||
return;
|
||||
}
|
||||
|
||||
JSONObject o = json.getJSONObject("data");
|
||||
|
||||
long time = json_lng(o, "timestamp");
|
||||
String title = json_str(o, "title");
|
||||
String content = json_str(o, "body");
|
||||
PriorityEnum prio = PriorityEnum.parseAPI(json_int(o, "priority"));
|
||||
long scn_id = json_lng(o, "scn_msg_id");
|
||||
|
||||
okResult.invoke(title, content, prio, time, scn_id);
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e("SC:expand", e.toString());
|
||||
SCNApp.showToast("Communication with server failed", 4000);
|
||||
} finally {
|
||||
SCNApp.runOnUiThread(() -> {
|
||||
if (loader != null) loader.setVisibility(View.GONE);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.e("SC:expand", e.toString());
|
||||
SCNApp.showToast("Communication with server failed", 4000);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean json_bool(JSONObject o, String key) throws JSONException
|
||||
{
|
||||
Object v = o.get(key);
|
||||
if (v instanceof Integer) return ((int)v) != 0;
|
||||
if (v instanceof Boolean) return ((boolean)v);
|
||||
if (v instanceof String) return !Str.equals(((String)v), "0") && !Str.equals(((String)v), "false");
|
||||
|
||||
return o.getBoolean(key);
|
||||
}
|
||||
|
||||
private static int json_int(JSONObject o, String key) throws JSONException
|
||||
{
|
||||
return o.getInt(key);
|
||||
}
|
||||
|
||||
private static long json_lng(JSONObject o, String key) throws JSONException
|
||||
{
|
||||
return o.getLong(key);
|
||||
}
|
||||
|
||||
private static String json_str(JSONObject o, String key) throws JSONException
|
||||
{
|
||||
return o.getString(key);
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,42 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.service;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.blackforestbytes.simplecloudnotifier.view.MainActivity;
|
||||
|
||||
public class BroadcastReceiverService extends BroadcastReceiver
|
||||
{
|
||||
public static final int NOTIF_SHOW_MAIN = 10021;
|
||||
public static final int NOTIF_STOP_SOUND = 10022;
|
||||
public static final String ID_KEY = "com.blackforestbytes.simplecloudnotifier.BroadcastID";
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent)
|
||||
{
|
||||
if (intent == null) return;
|
||||
Bundle extras = intent.getExtras();
|
||||
if (extras == null) return;
|
||||
int notificationId = extras.getInt(ID_KEY, 0);
|
||||
|
||||
if (notificationId == 0) return;
|
||||
else if (notificationId == NOTIF_SHOW_MAIN) showMain(context);
|
||||
else if (notificationId == NOTIF_STOP_SOUND) stopNotificationSound();
|
||||
else return;
|
||||
}
|
||||
|
||||
private void stopNotificationSound()
|
||||
{
|
||||
SoundService.stop();
|
||||
}
|
||||
|
||||
private void showMain(Context ctxt)
|
||||
{
|
||||
SoundService.stop();
|
||||
|
||||
Intent intent = new Intent(ctxt, MainActivity.class);
|
||||
ctxt.startActivity(intent);
|
||||
}
|
||||
}
|
@@ -1,20 +1,16 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.service;
|
||||
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.blackforestbytes.simplecloudnotifier.R;
|
||||
import com.blackforestbytes.simplecloudnotifier.SCNApp;
|
||||
import com.blackforestbytes.simplecloudnotifier.lib.datatypes.Tuple4;
|
||||
import com.blackforestbytes.simplecloudnotifier.lib.datatypes.Tuple5;
|
||||
import com.blackforestbytes.simplecloudnotifier.model.CMessage;
|
||||
import com.blackforestbytes.simplecloudnotifier.model.CMessageList;
|
||||
import com.blackforestbytes.simplecloudnotifier.model.PriorityEnum;
|
||||
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
|
||||
import com.blackforestbytes.simplecloudnotifier.view.MainActivity;
|
||||
import com.blackforestbytes.simplecloudnotifier.model.ServerCommunication;
|
||||
import com.google.firebase.messaging.FirebaseMessagingService;
|
||||
import com.google.firebase.messaging.RemoteMessage;
|
||||
|
||||
@@ -32,6 +28,8 @@ public class FBMService extends FirebaseMessagingService
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!SCNSettings.inst().Enabled) return;
|
||||
|
||||
Log.i("FB::MessageReceived", "From: " + remoteMessage.getFrom());
|
||||
Log.i("FB::MessageReceived", "Payload: " + remoteMessage.getData());
|
||||
if (remoteMessage.getNotification() != null) Log.i("FB::MessageReceived", "Notify_Title: " + remoteMessage.getNotification().getTitle());
|
||||
@@ -41,17 +39,16 @@ public class FBMService extends FirebaseMessagingService
|
||||
String title = remoteMessage.getData().get("title");
|
||||
String content = remoteMessage.getData().get("body");
|
||||
PriorityEnum prio = PriorityEnum.parseAPI(remoteMessage.getData().get("priority"));
|
||||
long scn_id = Long.parseLong(remoteMessage.getData().get("scn_msg_id"));
|
||||
boolean trimmed = Boolean.parseBoolean(remoteMessage.getData().get("trimmed"));
|
||||
|
||||
CMessage msg = CMessageList.inst().add(time, title, content, prio);
|
||||
|
||||
|
||||
if (SCNApp.isBackground())
|
||||
if (trimmed)
|
||||
{
|
||||
NotificationService.inst().show(msg);
|
||||
ServerCommunication.expand(SCNSettings.inst().user_id, SCNSettings.inst().user_key, scn_id, null, (i1, i2, i3, i4, i5) -> recieveData(i4, i1, i2, i3, i5, false));
|
||||
}
|
||||
else
|
||||
{
|
||||
SCNApp.showToast("Message recieved: " + title, Toast.LENGTH_LONG);
|
||||
recieveData(time, title, content, prio, scn_id, false);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -60,4 +57,27 @@ public class FBMService extends FirebaseMessagingService
|
||||
SCNApp.showToast("Recieved invalid message from server", Toast.LENGTH_LONG);
|
||||
}
|
||||
}
|
||||
|
||||
public static void recieveData(long time, String title, String content, PriorityEnum prio, long scn_id, boolean alwaysAck)
|
||||
{
|
||||
if (CMessageList.inst().isAck(scn_id))
|
||||
{
|
||||
Log.w("FB::MessageReceived", "Recieved ack-ed message: " + scn_id);
|
||||
if (alwaysAck) ServerCommunication.ack(SCNSettings.inst().user_id, SCNSettings.inst().user_key, scn_id);
|
||||
return;
|
||||
}
|
||||
|
||||
CMessage msg = CMessageList.inst().add(scn_id, time, title, content, prio);
|
||||
|
||||
if (SCNApp.isBackground())
|
||||
{
|
||||
NotificationService.inst().showBackground(msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
NotificationService.inst().showForeground(msg);
|
||||
}
|
||||
|
||||
ServerCommunication.ack(SCNSettings.inst().user_id, SCNSettings.inst().user_key, scn_id);
|
||||
}
|
||||
}
|
@@ -0,0 +1,195 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.service;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.android.billingclient.api.BillingClient;
|
||||
import com.android.billingclient.api.BillingClientStateListener;
|
||||
import com.android.billingclient.api.BillingFlowParams;
|
||||
import com.android.billingclient.api.Purchase;
|
||||
import com.android.billingclient.api.PurchasesUpdatedListener;
|
||||
import com.blackforestbytes.simplecloudnotifier.SCNApp;
|
||||
import com.blackforestbytes.simplecloudnotifier.lib.lambda.Func0to0;
|
||||
import com.blackforestbytes.simplecloudnotifier.lib.string.Str;
|
||||
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
|
||||
import com.blackforestbytes.simplecloudnotifier.view.MainActivity;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import static androidx.constraintlayout.widget.Constraints.TAG;
|
||||
|
||||
public class IABService implements PurchasesUpdatedListener
|
||||
{
|
||||
public static final String IAB_PRO_MODE = "scn.pro.tier1";
|
||||
|
||||
private final static Object _lock = new Object();
|
||||
private static IABService _inst = null;
|
||||
public static IABService inst()
|
||||
{
|
||||
synchronized (_lock)
|
||||
{
|
||||
if (_inst != null) return _inst;
|
||||
throw new Error("IABService == null");
|
||||
}
|
||||
}
|
||||
public static void startup(MainActivity a)
|
||||
{
|
||||
synchronized (_lock)
|
||||
{
|
||||
_inst = new IABService(a);
|
||||
}
|
||||
}
|
||||
|
||||
private BillingClient client;
|
||||
private boolean isServiceConnected;
|
||||
private final List<Purchase> purchases = new ArrayList<>();
|
||||
|
||||
public IABService(Context c)
|
||||
{
|
||||
client = BillingClient
|
||||
.newBuilder(c)
|
||||
.setListener(this)
|
||||
.build();
|
||||
|
||||
startServiceConnection(this::queryPurchases, false);
|
||||
}
|
||||
|
||||
public void queryPurchases()
|
||||
{
|
||||
Func0to0 queryToExecute = () ->
|
||||
{
|
||||
long time = System.currentTimeMillis();
|
||||
Purchase.PurchasesResult purchasesResult = client.queryPurchases(BillingClient.SkuType.INAPP);
|
||||
Log.i(TAG, "Querying purchases elapsed time: " + (System.currentTimeMillis() - time) + "ms");
|
||||
|
||||
if (purchasesResult.getResponseCode() == BillingClient.BillingResponse.OK)
|
||||
{
|
||||
for (Purchase p : purchasesResult.getPurchasesList())
|
||||
{
|
||||
handlePurchase(p);
|
||||
}
|
||||
|
||||
boolean newProMode = getPurchaseCached(IAB_PRO_MODE) != null;
|
||||
if (newProMode != SCNSettings.inst().promode_local)
|
||||
{
|
||||
refreshProModeListener();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.w(TAG, "queryPurchases() got an error response code: " + purchasesResult.getResponseCode());
|
||||
}
|
||||
};
|
||||
|
||||
executeServiceRequest(queryToExecute, false);
|
||||
}
|
||||
|
||||
public void purchase(Activity a, String id)
|
||||
{
|
||||
executeServiceRequest(() ->
|
||||
{
|
||||
BillingFlowParams flowParams = BillingFlowParams
|
||||
.newBuilder()
|
||||
.setSku(id)
|
||||
.setType(BillingClient.SkuType.INAPP) // SkuType.SUB for subscription
|
||||
.build();
|
||||
client.launchBillingFlow(a, flowParams);
|
||||
}, true);
|
||||
}
|
||||
|
||||
private void executeServiceRequest(Func0to0 runnable, final boolean userRequest) {
|
||||
if (isServiceConnected)
|
||||
{
|
||||
runnable.invoke();
|
||||
}
|
||||
else
|
||||
{
|
||||
// If billing service was disconnected, we try to reconnect 1 time.
|
||||
// (feel free to introduce your retry policy here).
|
||||
startServiceConnection(runnable, userRequest);
|
||||
}
|
||||
}
|
||||
public void destroy()
|
||||
{
|
||||
if (client != null && client.isReady()) {
|
||||
client.endConnection();
|
||||
client = null;
|
||||
isServiceConnected = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPurchasesUpdated(int responseCode, @Nullable List<Purchase> purchases)
|
||||
{
|
||||
if (responseCode == BillingClient.BillingResponse.OK && purchases != null)
|
||||
{
|
||||
for (Purchase purchase : purchases)
|
||||
{
|
||||
handlePurchase(purchase);
|
||||
}
|
||||
}
|
||||
else if (responseCode == BillingClient.BillingResponse.ITEM_ALREADY_OWNED && purchases != null)
|
||||
{
|
||||
for (Purchase purchase : purchases)
|
||||
{
|
||||
handlePurchase(purchase);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handlePurchase(Purchase purchase)
|
||||
{
|
||||
Log.d(TAG, "Got a verified purchase: " + purchase);
|
||||
|
||||
purchases.add(purchase);
|
||||
|
||||
refreshProModeListener();
|
||||
}
|
||||
|
||||
private void refreshProModeListener() {
|
||||
MainActivity ma = SCNApp.getMainActivity();
|
||||
if (ma != null) ma.adpTabs.tab3.updateProState();
|
||||
if (ma != null) ma.adpTabs.tab1.updateProState();
|
||||
SCNSettings.inst().updateProState(null);
|
||||
}
|
||||
|
||||
public void startServiceConnection(final Func0to0 executeOnSuccess, final boolean userRequest)
|
||||
{
|
||||
client.startConnection(new BillingClientStateListener()
|
||||
{
|
||||
@Override
|
||||
public void onBillingSetupFinished(@BillingClient.BillingResponse int billingResponseCode)
|
||||
{
|
||||
if (billingResponseCode == BillingClient.BillingResponse.OK)
|
||||
{
|
||||
isServiceConnected = true;
|
||||
if (executeOnSuccess != null) executeOnSuccess.invoke();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (userRequest) SCNApp.showToast("Could not connect to google services", Toast.LENGTH_SHORT);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBillingServiceDisconnected() {
|
||||
isServiceConnected = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Purchase getPurchaseCached(String id)
|
||||
{
|
||||
for (Purchase p : purchases)
|
||||
{
|
||||
if (Str.equals(p.getSku(), id)) return p;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@@ -1,22 +1,37 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.service;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Color;
|
||||
import android.media.AudioManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.os.VibrationEffect;
|
||||
import android.os.Vibrator;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.blackforestbytes.simplecloudnotifier.R;
|
||||
import com.blackforestbytes.simplecloudnotifier.SCNApp;
|
||||
import com.blackforestbytes.simplecloudnotifier.lib.string.Str;
|
||||
import com.blackforestbytes.simplecloudnotifier.model.CMessage;
|
||||
import com.blackforestbytes.simplecloudnotifier.model.NotificationSettings;
|
||||
import com.blackforestbytes.simplecloudnotifier.model.PriorityEnum;
|
||||
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
|
||||
import com.blackforestbytes.simplecloudnotifier.view.MainActivity;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
|
||||
public class NotificationService
|
||||
{
|
||||
private final static String CHANNEL_ID = "CHAN_BFB_SCN_MESSAGES";
|
||||
private final static String CHANNEL_P0_ID = "CHAN_BFB_SCN_MESSAGES_P0";
|
||||
private final static String CHANNEL_P1_ID = "CHAN_BFB_SCN_MESSAGES_P1";
|
||||
private final static String CHANNEL_P2_ID = "CHAN_BFB_SCN_MESSAGES_P2";
|
||||
|
||||
private final static Object _lock = new Object();
|
||||
private static NotificationService _inst = null;
|
||||
@@ -31,39 +46,222 @@ public class NotificationService
|
||||
|
||||
private NotificationService()
|
||||
{
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
||||
createChannels();
|
||||
}
|
||||
|
||||
private void createChannels()
|
||||
{
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return;
|
||||
|
||||
Context ctxt = SCNApp.getContext();
|
||||
NotificationManager notifman = ctxt.getSystemService(NotificationManager.class);
|
||||
if (notifman == null) return;
|
||||
|
||||
{
|
||||
NotificationChannel channel0 = notifman.getNotificationChannel(CHANNEL_P0_ID);
|
||||
if (channel0 == null)
|
||||
{
|
||||
channel0 = new NotificationChannel(CHANNEL_P0_ID, "Push notifications (low priority)", NotificationManager.IMPORTANCE_DEFAULT);
|
||||
channel0.setDescription("Push notifications from the server with low priority.\nGo to the in-app settings to configure ringtone, volume and vibrations");
|
||||
channel0.setSound(null, null);
|
||||
channel0.setVibrationPattern(null);
|
||||
channel0.setLightColor(Color.CYAN);
|
||||
channel0.enableLights(true);
|
||||
notifman.createNotificationChannel(channel0);
|
||||
}
|
||||
}
|
||||
{
|
||||
NotificationChannel channel1 = notifman.getNotificationChannel(CHANNEL_P1_ID);
|
||||
if (channel1 == null)
|
||||
{
|
||||
channel1 = new NotificationChannel(CHANNEL_P1_ID, "Push notifications (normal priority)", NotificationManager.IMPORTANCE_DEFAULT);
|
||||
channel1.setDescription("Push notifications from the server with low priority.\nGo to the in-app settings to configure ringtone, volume and vibrations");
|
||||
channel1.setSound(null, null);
|
||||
channel1.setVibrationPattern(null);
|
||||
channel1.setLightColor(Color.CYAN);
|
||||
channel1.enableLights(true);
|
||||
notifman.createNotificationChannel(channel1);
|
||||
}
|
||||
}
|
||||
{
|
||||
NotificationChannel channel2 = notifman.getNotificationChannel(CHANNEL_P2_ID);
|
||||
if (channel2 == null)
|
||||
{
|
||||
channel2 = new NotificationChannel(CHANNEL_P2_ID, "Push notifications (high priority)", NotificationManager.IMPORTANCE_DEFAULT);
|
||||
channel2.setDescription("Push notifications from the server with low priority.\nGo to the in-app settings to configure ringtone, volume and vibrations");
|
||||
channel2.setSound(null, null);
|
||||
channel2.setVibrationPattern(null);
|
||||
channel2.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
|
||||
channel2.setLightColor(Color.CYAN);
|
||||
channel2.enableLights(true);
|
||||
notifman.createNotificationChannel(channel2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void showForeground(CMessage msg)
|
||||
{
|
||||
SCNApp.showToast("Message recieved: " + msg.Title, Toast.LENGTH_LONG);
|
||||
|
||||
try
|
||||
{
|
||||
NotificationSettings ns = SCNSettings.inst().PriorityNorm;
|
||||
switch (msg.Priority)
|
||||
{
|
||||
case LOW: ns = SCNSettings.inst().PriorityLow; break;
|
||||
case NORMAL: ns = SCNSettings.inst().PriorityNorm; break;
|
||||
case HIGH: ns = SCNSettings.inst().PriorityHigh; break;
|
||||
}
|
||||
|
||||
SoundService.play(ns.EnableSound, ns.SoundSource, ns.ForceVolume, ns.ForceVolumeValue, false);
|
||||
|
||||
if (ns.EnableVibration)
|
||||
{
|
||||
Vibrator v = (Vibrator) SCNApp.getContext().getSystemService(Context.VIBRATOR_SERVICE);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
v.vibrate(VibrationEffect.createOneShot(500, VibrationEffect.DEFAULT_AMPLITUDE));
|
||||
} else {
|
||||
v.vibrate(500);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void showBackground(CMessage msg)
|
||||
{
|
||||
Context ctxt = SCNApp.getContext();
|
||||
|
||||
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, "Push notifications", NotificationManager.IMPORTANCE_HIGH);
|
||||
channel.setDescription("Messages from the API");
|
||||
channel.setLightColor(Color.rgb(255, 0, 0));
|
||||
channel.setVibrationPattern(new long[]{200});
|
||||
channel.enableLights(true);
|
||||
channel.enableVibration(true);
|
||||
|
||||
NotificationManager notificationManager = ctxt.getSystemService(NotificationManager.class);
|
||||
if (notificationManager != null) notificationManager.createNotificationChannel(channel);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void show(CMessage msg)
|
||||
NotificationSettings ns = SCNSettings.inst().PriorityNorm;
|
||||
switch (msg.Priority)
|
||||
{
|
||||
Context ctxt = SCNApp.getContext();
|
||||
case LOW: ns = SCNSettings.inst().PriorityLow; break;
|
||||
case NORMAL: ns = SCNSettings.inst().PriorityNorm; break;
|
||||
case HIGH: ns = SCNSettings.inst().PriorityHigh; break;
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
|
||||
{
|
||||
// old
|
||||
showBackground_old(msg, ctxt, ns, msg.Priority);
|
||||
}
|
||||
else
|
||||
{
|
||||
// new
|
||||
showBackground_new(msg, ctxt, ns, msg.Priority);
|
||||
}
|
||||
}
|
||||
|
||||
private String getChannel(PriorityEnum p)
|
||||
{
|
||||
switch (p)
|
||||
{
|
||||
case LOW: return CHANNEL_P0_ID;
|
||||
case NORMAL: return CHANNEL_P1_ID;
|
||||
case HIGH: return CHANNEL_P2_ID;
|
||||
|
||||
default: return CHANNEL_P0_ID;
|
||||
}
|
||||
}
|
||||
|
||||
private void showBackground_old(CMessage msg, Context ctxt, NotificationSettings ns, PriorityEnum prio)
|
||||
{
|
||||
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(ctxt, getChannel(prio));
|
||||
mBuilder.setSmallIcon(R.drawable.ic_notification_white);
|
||||
mBuilder.setLargeIcon(BitmapFactory.decodeResource(ctxt.getResources(), R.mipmap.ic_notification_full));
|
||||
mBuilder.setContentTitle(msg.Title);
|
||||
mBuilder.setContentText(msg.Content);
|
||||
mBuilder.setShowWhen(true);
|
||||
mBuilder.setWhen(msg.Timestamp * 1000);
|
||||
mBuilder.setAutoCancel(true);
|
||||
mBuilder.setCategory(Notification.CATEGORY_MESSAGE);
|
||||
mBuilder.setGroup("com.blackforestbytes.simplecloudnotifier.notifications.group."+prio.toString());
|
||||
|
||||
if (msg.Priority == PriorityEnum.LOW) mBuilder.setPriority(NotificationCompat.PRIORITY_LOW);
|
||||
if (msg.Priority == PriorityEnum.NORMAL) mBuilder.setPriority(NotificationCompat.PRIORITY_DEFAULT);
|
||||
if (msg.Priority == PriorityEnum.HIGH) mBuilder.setPriority(NotificationCompat.PRIORITY_HIGH);
|
||||
if (ns.EnableVibration) mBuilder.setVibrate(new long[]{500});
|
||||
if (ns.EnableLED) mBuilder.setLights(ns.LEDColor, 500, 500);
|
||||
|
||||
if (ns.EnableSound && !ns.SoundSource.isEmpty() && !ns.RepeatSound) mBuilder.setSound(Uri.parse(ns.SoundSource), AudioManager.STREAM_NOTIFICATION);
|
||||
|
||||
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(ctxt, CHANNEL_ID)
|
||||
.setSmallIcon(R.drawable.ic_bfb)
|
||||
.setContentTitle(msg.Title)
|
||||
.setContentText(msg.Content)
|
||||
.setShowWhen(true)
|
||||
.setWhen(msg.Timestamp)
|
||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||
.setAutoCancel(true);
|
||||
Intent intent = new Intent(ctxt, MainActivity.class);
|
||||
PendingIntent pi = PendingIntent.getActivity(ctxt, 0, intent, 0);
|
||||
mBuilder.setContentIntent(pi);
|
||||
NotificationManager mNotificationManager = (NotificationManager) ctxt.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
if (mNotificationManager != null) mNotificationManager.notify(0, mBuilder.build());
|
||||
|
||||
if (ns.EnableSound && !ns.SoundSource.isEmpty() && ns.RepeatSound)
|
||||
{
|
||||
Intent intnt_stop = new Intent(SCNApp.getContext(), BroadcastReceiverService.class);
|
||||
intnt_stop.putExtra(BroadcastReceiverService.ID_KEY, BroadcastReceiverService.NOTIF_STOP_SOUND);
|
||||
PendingIntent pi_stop = PendingIntent.getBroadcast(SCNApp.getContext().getApplicationContext(), BroadcastReceiverService.NOTIF_STOP_SOUND, intnt_stop, 0);
|
||||
mBuilder.addAction(new NotificationCompat.Action(-1, "Stop", pi_stop));
|
||||
mBuilder.setDeleteIntent(pi_stop);
|
||||
|
||||
SoundService.play(ns.EnableSound, ns.SoundSource, ns.ForceVolume, ns.ForceVolumeValue, ns.RepeatSound);
|
||||
}
|
||||
|
||||
Notification n = mBuilder.build();
|
||||
|
||||
if (mNotificationManager != null) mNotificationManager.notify((int)msg.SCN_ID, n);
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
private void showBackground_new(CMessage msg, Context ctxt, NotificationSettings ns, PriorityEnum prio)
|
||||
{
|
||||
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(ctxt, getChannel(prio));
|
||||
mBuilder.setSmallIcon(R.drawable.ic_notification_white);
|
||||
mBuilder.setLargeIcon(BitmapFactory.decodeResource(ctxt.getResources(), R.mipmap.ic_notification_full));
|
||||
mBuilder.setContentTitle(msg.Title);
|
||||
mBuilder.setContentText(msg.Content);
|
||||
mBuilder.setShowWhen(true);
|
||||
mBuilder.setWhen(msg.Timestamp * 1000);
|
||||
mBuilder.setAutoCancel(true);
|
||||
mBuilder.setCategory(Notification.CATEGORY_MESSAGE);
|
||||
mBuilder.setGroup("com.blackforestbytes.simplecloudnotifier.notifications.group."+prio.toString());
|
||||
|
||||
if (ns.EnableLED) mBuilder.setLights(ns.LEDColor, 500, 500);
|
||||
|
||||
if (msg.Priority == PriorityEnum.LOW) mBuilder.setPriority(NotificationCompat.PRIORITY_LOW);
|
||||
if (msg.Priority == PriorityEnum.NORMAL) mBuilder.setPriority(NotificationCompat.PRIORITY_DEFAULT);
|
||||
if (msg.Priority == PriorityEnum.HIGH) mBuilder.setPriority(NotificationCompat.PRIORITY_HIGH);
|
||||
|
||||
Intent intnt_click = new Intent(SCNApp.getContext(), BroadcastReceiverService.class);
|
||||
intnt_click.putExtra(BroadcastReceiverService.ID_KEY, BroadcastReceiverService.NOTIF_SHOW_MAIN);
|
||||
PendingIntent pi = PendingIntent.getBroadcast(ctxt, 0, intnt_click, 0);
|
||||
mBuilder.setContentIntent(pi);
|
||||
NotificationManager mNotificationManager = (NotificationManager) ctxt.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
if (mNotificationManager == null) return;
|
||||
|
||||
if (ns.EnableSound && !Str.isNullOrWhitespace(ns.SoundSource))
|
||||
{
|
||||
if (ns.RepeatSound)
|
||||
{
|
||||
Intent intnt_stop = new Intent(SCNApp.getContext(), BroadcastReceiverService.class);
|
||||
intnt_stop.putExtra(BroadcastReceiverService.ID_KEY, BroadcastReceiverService.NOTIF_STOP_SOUND);
|
||||
PendingIntent pi_stop = PendingIntent.getBroadcast(ctxt, BroadcastReceiverService.NOTIF_STOP_SOUND, intnt_stop, 0);
|
||||
mBuilder.addAction(new NotificationCompat.Action(-1, "Stop", pi_stop));
|
||||
mBuilder.setDeleteIntent(pi_stop);
|
||||
}
|
||||
|
||||
SoundService.play(ns.EnableSound, ns.SoundSource, ns.ForceVolume, ns.ForceVolumeValue, ns.RepeatSound);
|
||||
}
|
||||
|
||||
Notification n = mBuilder.build();
|
||||
n.flags |= Notification.FLAG_AUTO_CANCEL;
|
||||
|
||||
mNotificationManager.notify((int)msg.SCN_ID, n);
|
||||
|
||||
if (ns.EnableVibration)
|
||||
{
|
||||
Vibrator v = (Vibrator) SCNApp.getContext().getSystemService(Context.VIBRATOR_SERVICE);
|
||||
v.vibrate(VibrationEffect.createOneShot(500, VibrationEffect.DEFAULT_AMPLITUDE));
|
||||
}
|
||||
|
||||
//if (ns.EnableLED) { } // no LED in Android-O -- configure via Channel
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,56 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.service;
|
||||
|
||||
import android.content.Context;
|
||||
import android.media.AudioAttributes;
|
||||
import android.media.AudioManager;
|
||||
import android.media.MediaPlayer;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
import com.blackforestbytes.simplecloudnotifier.SCNApp;
|
||||
import com.blackforestbytes.simplecloudnotifier.lib.string.Str;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class SoundService
|
||||
{
|
||||
private static MediaPlayer mpLast = null;
|
||||
|
||||
public static void play(boolean enableSound, String soundSource, boolean forceVolume, int forceVolumeValue, boolean loop)
|
||||
{
|
||||
if (!enableSound) return;
|
||||
if (Str.isNullOrWhitespace(soundSource)) return;
|
||||
|
||||
stop();
|
||||
|
||||
if (forceVolume)
|
||||
{
|
||||
AudioManager aman = (AudioManager) SCNApp.getContext().getSystemService(Context.AUDIO_SERVICE);
|
||||
int maxVolume = aman.getStreamMaxVolume(AudioManager.STREAM_NOTIFICATION);
|
||||
aman.setStreamVolume(AudioManager.STREAM_NOTIFICATION, (int)(maxVolume * (forceVolumeValue / 100.0)), 0);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
MediaPlayer player = new MediaPlayer();
|
||||
player.setAudioAttributes(new AudioAttributes.Builder().setLegacyStreamType(AudioManager.STREAM_NOTIFICATION).build());
|
||||
player.setAudioStreamType(AudioManager.STREAM_NOTIFICATION);
|
||||
player.setDataSource(SCNApp.getContext(), Uri.parse(soundSource));
|
||||
player.setLooping(loop);
|
||||
player.setOnCompletionListener( mp -> { mp.stop(); mp.release(); });
|
||||
player.setOnSeekCompleteListener(mp -> { mp.stop(); mp.release(); });
|
||||
player.prepare();
|
||||
player.start();
|
||||
mpLast = player;
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
Log.e("Sound::play", e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public static void stop()
|
||||
{
|
||||
if (mpLast != null && mpLast.isPlaying()) { mpLast.stop(); mpLast.release(); mpLast = null; }
|
||||
}
|
||||
}
|
@@ -0,0 +1,89 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.util;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.view.View;
|
||||
|
||||
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
|
||||
import com.blackforestbytes.simplecloudnotifier.view.MessageAdapter;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
public class MessageAdapterTouchHelper extends ItemTouchHelper.SimpleCallback
|
||||
{
|
||||
private MessageAdapterTouchHelperListener listener;
|
||||
|
||||
private int dir = 0;
|
||||
|
||||
public MessageAdapterTouchHelper(int dragDirs, int swipeDirs, MessageAdapterTouchHelperListener listener)
|
||||
{
|
||||
super(dragDirs, swipeDirs);
|
||||
this.dir = swipeDirs;
|
||||
this.listener = listener;
|
||||
updateEnabled();
|
||||
}
|
||||
|
||||
public void updateEnabled()
|
||||
{
|
||||
int sdir = SCNSettings.inst().EnableDeleteSwipe ? ItemTouchHelper.LEFT : 0;
|
||||
if (dir == sdir) return;
|
||||
setDefaultSwipeDirs(dir = sdir);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState)
|
||||
{
|
||||
if (viewHolder != null)
|
||||
{
|
||||
final View foregroundView = ((MessageAdapter.MessagePresenter) viewHolder).viewForeground;
|
||||
|
||||
getDefaultUIUtil().onSelected(foregroundView);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChildDrawOver(@NonNull Canvas c, @NonNull RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive)
|
||||
{
|
||||
final View foregroundView = ((MessageAdapter.MessagePresenter) viewHolder).viewForeground;
|
||||
getDefaultUIUtil().onDrawOver(c, recyclerView, foregroundView, dX, dY, actionState, isCurrentlyActive);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder)
|
||||
{
|
||||
final View foregroundView = ((MessageAdapter.MessagePresenter) viewHolder).viewForeground;
|
||||
getDefaultUIUtil().clearView(foregroundView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive)
|
||||
{
|
||||
final View foregroundView = ((MessageAdapter.MessagePresenter) viewHolder).viewForeground;
|
||||
|
||||
getDefaultUIUtil().onDraw(c, recyclerView, foregroundView, dX, dY, actionState, isCurrentlyActive);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction)
|
||||
{
|
||||
listener.onSwiped(viewHolder, direction, viewHolder.getAdapterPosition());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int convertToAbsoluteDirection(int flags, int layoutDirection)
|
||||
{
|
||||
return super.convertToAbsoluteDirection(flags, layoutDirection);
|
||||
}
|
||||
|
||||
public interface MessageAdapterTouchHelperListener
|
||||
{
|
||||
void onSwiped(RecyclerView.ViewHolder viewHolder, int direction, int position);
|
||||
}
|
||||
}
|
@@ -1,17 +1,19 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.view;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.blackforestbytes.simplecloudnotifier.R;
|
||||
@@ -22,6 +24,10 @@ import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
|
||||
import net.glxn.qrgen.android.QRCode;
|
||||
import net.glxn.qrgen.core.image.ImageType;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import static android.content.Context.CLIPBOARD_SERVICE;
|
||||
|
||||
public class AccountFragment extends Fragment
|
||||
@@ -54,20 +60,52 @@ public class AccountFragment extends Fragment
|
||||
|
||||
v.findViewById(R.id.btnAccountReset).setOnClickListener(cv ->
|
||||
{
|
||||
Activity a = getActivity();
|
||||
if (a == null) return;
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(a);
|
||||
|
||||
builder.setTitle("Confirm");
|
||||
builder.setMessage("Reset account key?");
|
||||
|
||||
builder.setPositiveButton("YES", (dialog, which) -> {
|
||||
View lpnl = v.findViewById(R.id.loadingPanel);
|
||||
lpnl.setVisibility(View.VISIBLE);
|
||||
SCNSettings.inst().reset(lpnl);
|
||||
dialog.dismiss();
|
||||
});
|
||||
|
||||
builder.setNegativeButton("NO", (dialog, which) -> dialog.dismiss());
|
||||
|
||||
AlertDialog alert = builder.create();
|
||||
alert.show();
|
||||
});
|
||||
|
||||
v.findViewById(R.id.btnClearLocalStorage).setOnClickListener(cv ->
|
||||
{
|
||||
Activity a = getActivity();
|
||||
if (a == null) return;
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(a);
|
||||
|
||||
builder.setTitle("Confirm");
|
||||
builder.setMessage("Clear local messages?");
|
||||
|
||||
builder.setPositiveButton("YES", (dialog, which) -> {
|
||||
CMessageList.inst().clear();
|
||||
SCNApp.showToast("Notifications cleared", 1000);
|
||||
SCNApp.showToast("Messages cleared", 1000);
|
||||
dialog.dismiss();
|
||||
});
|
||||
|
||||
builder.setNegativeButton("NO", (dialog, which) -> dialog.dismiss());
|
||||
|
||||
AlertDialog alert = builder.create();
|
||||
alert.show();
|
||||
});
|
||||
|
||||
v.findViewById(R.id.btnQR).setOnClickListener(cv ->
|
||||
{
|
||||
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(SCNSettings.inst().createOnlineURL()));
|
||||
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(SCNSettings.inst().createOnlineURL(true)));
|
||||
startActivity(browserIntent);
|
||||
});
|
||||
|
||||
@@ -93,6 +131,7 @@ public class AccountFragment extends Fragment
|
||||
TextView tvUserID = v.findViewById(R.id.tvUserID);
|
||||
TextView tvUserKey = v.findViewById(R.id.tvUserKey);
|
||||
TextView tvQuota = v.findViewById(R.id.tvQuota);
|
||||
ImageView ivQuota = v.findViewById(R.id.ic_img_quota);
|
||||
ImageButton btnQR = v.findViewById(R.id.btnQR);
|
||||
|
||||
SCNSettings s = SCNSettings.inst();
|
||||
@@ -102,7 +141,8 @@ public class AccountFragment extends Fragment
|
||||
tvUserID.setText(String.valueOf(s.user_id));
|
||||
tvUserKey.setText(s.user_key);
|
||||
tvQuota.setText(String.format("%d / %d", s.quota_curr, s.quota_max));
|
||||
btnQR.setImageBitmap(QRCode.from(s.createOnlineURL()).to(ImageType.PNG).withSize(512, 512).bitmap());
|
||||
btnQR.setImageBitmap(QRCode.from(s.createOnlineURL(false)).to(ImageType.PNG).withSize(512, 512).bitmap());
|
||||
ivQuota.setColorFilter(s.quota_curr>=s.quota_max ? Color.rgb(200, 0, 0) : Color.rgb(128, 128, 128));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -110,6 +150,7 @@ public class AccountFragment extends Fragment
|
||||
tvUserKey.setText(R.string.str_not_connected);
|
||||
tvQuota.setText(R.string.str_not_connected);
|
||||
btnQR.setImageResource(R.drawable.qr_default);
|
||||
ivQuota.setColorFilter(0x80_80_80);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,28 +1,25 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.view;
|
||||
|
||||
import android.support.design.widget.TabLayout;
|
||||
import android.support.v4.view.PagerAdapter;
|
||||
import android.support.v4.view.ViewPager;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.RelativeLayout;
|
||||
|
||||
import com.blackforestbytes.simplecloudnotifier.R;
|
||||
import com.blackforestbytes.simplecloudnotifier.SCNApp;
|
||||
import com.blackforestbytes.simplecloudnotifier.model.CMessageList;
|
||||
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
|
||||
import com.blackforestbytes.simplecloudnotifier.model.ServerCommunication;
|
||||
import com.blackforestbytes.simplecloudnotifier.service.IABService;
|
||||
import com.blackforestbytes.simplecloudnotifier.service.NotificationService;
|
||||
import com.google.firebase.iid.FirebaseInstanceId;
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.viewpager.widget.PagerAdapter;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
public class MainActivity extends AppCompatActivity
|
||||
{
|
||||
public TabAdapter adpTabs;
|
||||
public RelativeLayout layoutRoot;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState)
|
||||
@@ -33,6 +30,8 @@ public class MainActivity extends AppCompatActivity
|
||||
NotificationService.inst();
|
||||
CMessageList.inst();
|
||||
|
||||
layoutRoot = findViewById(R.id.layoutRoot);
|
||||
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
|
||||
@@ -44,8 +43,24 @@ public class MainActivity extends AppCompatActivity
|
||||
tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
|
||||
tabLayout.setupWithViewPager(viewPager);
|
||||
|
||||
SCNApp.register(this);
|
||||
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
|
||||
@Override
|
||||
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { /* */ }
|
||||
|
||||
@Override
|
||||
public void onPageSelected(int position)
|
||||
{
|
||||
if (position != 2) adpTabs.tab3.onViewpagerHide();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageScrollStateChanged(int state) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
SCNApp.register(this);
|
||||
IABService.startup(this);
|
||||
SCNSettings.inst().work(this);
|
||||
}
|
||||
|
||||
@@ -54,6 +69,16 @@ public class MainActivity extends AppCompatActivity
|
||||
{
|
||||
super.onStop();
|
||||
|
||||
SCNSettings.inst().save();
|
||||
CMessageList.inst().fullSave();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy()
|
||||
{
|
||||
super.onDestroy();
|
||||
|
||||
CMessageList.inst().fullSave();
|
||||
IABService.inst().destroy();
|
||||
}
|
||||
}
|
||||
|
@@ -1,26 +1,40 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.view;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.graphics.Color;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.blackforestbytes.simplecloudnotifier.R;
|
||||
import com.blackforestbytes.simplecloudnotifier.SCNApp;
|
||||
import com.blackforestbytes.simplecloudnotifier.model.CMessage;
|
||||
import com.blackforestbytes.simplecloudnotifier.model.CMessageList;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
public class MessageAdapter extends RecyclerView.Adapter
|
||||
{
|
||||
private final View vNoElements;
|
||||
private final LinearLayoutManager manLayout;
|
||||
private final RecyclerView viewRecycler;
|
||||
|
||||
public MessageAdapter(View noElementsView)
|
||||
private WeakHashMap<MessagePresenter, Boolean> viewHolders = new WeakHashMap<>();
|
||||
|
||||
public MessageAdapter(View noElementsView, LinearLayoutManager layout, RecyclerView recycler)
|
||||
{
|
||||
vNoElements = noElementsView;
|
||||
manLayout = layout;
|
||||
viewRecycler = recycler;
|
||||
CMessageList.inst().register(this);
|
||||
|
||||
vNoElements.setVisibility(getItemCount()>0 ? View.GONE : View.VISIBLE);
|
||||
@@ -37,9 +51,17 @@ public class MessageAdapter extends RecyclerView.Adapter
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position)
|
||||
{
|
||||
CMessage msg = CMessageList.inst().tryGet(position);
|
||||
CMessage msg = CMessageList.inst().tryGetFromBack(position);
|
||||
MessagePresenter view = (MessagePresenter) holder;
|
||||
view.setMessage(msg);
|
||||
|
||||
viewHolders.put(view, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewRecycled(@NonNull RecyclerView.ViewHolder holder)
|
||||
{
|
||||
if (holder instanceof MessagePresenter) viewHolders.remove(holder);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -60,13 +82,34 @@ public class MessageAdapter extends RecyclerView.Adapter
|
||||
vNoElements.setVisibility(getItemCount()>0 ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
|
||||
private class MessagePresenter extends RecyclerView.ViewHolder implements View.OnClickListener
|
||||
public void scrollToTop()
|
||||
{
|
||||
manLayout.smoothScrollToPosition(viewRecycler, null, 0);
|
||||
}
|
||||
|
||||
public CMessage removeItem(int position)
|
||||
{
|
||||
CMessage i = CMessageList.inst().removeFromBack(position);
|
||||
notifyDataSetChanged();
|
||||
return i;
|
||||
}
|
||||
|
||||
public void restoreItem(CMessage item, int position)
|
||||
{
|
||||
CMessageList.inst().insert(position, item);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public class MessagePresenter extends RecyclerView.ViewHolder implements View.OnClickListener
|
||||
{
|
||||
private TextView tvTimestamp;
|
||||
private TextView tvTitle;
|
||||
private TextView tvMessage;
|
||||
private ImageView ivPriority;
|
||||
|
||||
public RelativeLayout viewForeground;
|
||||
public RelativeLayout viewBackground;
|
||||
|
||||
private CMessage data;
|
||||
|
||||
MessagePresenter(View itemView)
|
||||
@@ -76,7 +119,15 @@ public class MessageAdapter extends RecyclerView.Adapter
|
||||
tvTitle = itemView.findViewById(R.id.tvTitle);
|
||||
tvMessage = itemView.findViewById(R.id.tvMessage);
|
||||
ivPriority = itemView.findViewById(R.id.ivPriority);
|
||||
viewForeground = itemView.findViewById(R.id.layoutFront);
|
||||
viewBackground = itemView.findViewById(R.id.layoutBack);
|
||||
|
||||
itemView.setOnClickListener(this);
|
||||
tvTimestamp.setOnClickListener(this);
|
||||
tvTitle.setOnClickListener(this);
|
||||
tvMessage.setOnClickListener(this);
|
||||
ivPriority.setOnClickListener(this);
|
||||
viewForeground.setOnClickListener(this);
|
||||
}
|
||||
|
||||
void setMessage(CMessage msg)
|
||||
@@ -90,13 +141,16 @@ public class MessageAdapter extends RecyclerView.Adapter
|
||||
case LOW:
|
||||
ivPriority.setVisibility(View.VISIBLE);
|
||||
ivPriority.setImageResource(R.drawable.priority_low);
|
||||
ivPriority.setColorFilter(Color.rgb(176, 176, 176));
|
||||
break;
|
||||
case NORMAL:
|
||||
ivPriority.setVisibility(View.GONE);
|
||||
ivPriority.setColorFilter(Color.rgb(176, 176, 176));
|
||||
break;
|
||||
case HIGH:
|
||||
ivPriority.setVisibility(View.VISIBLE);
|
||||
ivPriority.setImageResource(R.drawable.priority_high);
|
||||
ivPriority.setColorFilter(Color.rgb(200, 0, 0));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -106,7 +160,16 @@ public class MessageAdapter extends RecyclerView.Adapter
|
||||
@Override
|
||||
public void onClick(View v)
|
||||
{
|
||||
//SCNApp.showToast(data.Title, Toast.LENGTH_LONG);
|
||||
for (MessagePresenter holder : MessageAdapter.this.viewHolders.keySet())
|
||||
{
|
||||
if (holder == null) continue;
|
||||
if (holder == this) continue;
|
||||
if (holder.tvMessage == null) continue;
|
||||
if (holder.tvMessage.getMaxLines() == 6) continue;
|
||||
holder.tvMessage.setMaxLines(6);
|
||||
}
|
||||
|
||||
tvMessage.setMaxLines(9999);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,20 +1,34 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.blackforestbytes.simplecloudnotifier.R;
|
||||
import com.blackforestbytes.simplecloudnotifier.SCNApp;
|
||||
import com.blackforestbytes.simplecloudnotifier.model.CMessage;
|
||||
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
|
||||
import com.blackforestbytes.simplecloudnotifier.service.IABService;
|
||||
import com.blackforestbytes.simplecloudnotifier.util.MessageAdapterTouchHelper;
|
||||
import com.google.android.gms.ads.doubleclick.PublisherAdRequest;
|
||||
import com.google.android.gms.ads.doubleclick.PublisherAdView;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
|
||||
public class NotificationsFragment extends Fragment
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
public class NotificationsFragment extends Fragment implements MessageAdapterTouchHelper.MessageAdapterTouchHelperListener
|
||||
{
|
||||
private PublisherAdView adView;
|
||||
private MessageAdapter adpMessages;
|
||||
|
||||
public MessageAdapterTouchHelper touchHelper;
|
||||
|
||||
public NotificationsFragment()
|
||||
{
|
||||
// Required empty public constructor
|
||||
@@ -26,9 +40,46 @@ public class NotificationsFragment extends Fragment
|
||||
View v = inflater.inflate(R.layout.fragment_notifications, container, false);
|
||||
|
||||
RecyclerView rvMessages = v.findViewById(R.id.rvMessages);
|
||||
rvMessages.setLayoutManager(new LinearLayoutManager(this.getContext(), RecyclerView.VERTICAL, true));
|
||||
rvMessages.setAdapter(new MessageAdapter(v.findViewById(R.id.tvNoElements)));
|
||||
LinearLayoutManager lman = new LinearLayoutManager(this.getContext(), RecyclerView.VERTICAL, false);
|
||||
rvMessages.setLayoutManager(lman);
|
||||
rvMessages.setAdapter(adpMessages = new MessageAdapter(v.findViewById(R.id.tvNoElements), lman, rvMessages));
|
||||
|
||||
ItemTouchHelper.SimpleCallback itemTouchHelperCallback = touchHelper = new MessageAdapterTouchHelper(0, ItemTouchHelper.LEFT, this);
|
||||
new ItemTouchHelper(itemTouchHelperCallback).attachToRecyclerView(rvMessages);
|
||||
|
||||
adView = v.findViewById(R.id.adBanner);
|
||||
PublisherAdRequest adRequest = new PublisherAdRequest.Builder().build();
|
||||
adView.loadAd(adRequest);
|
||||
|
||||
adView.setVisibility(SCNSettings.inst().promode_local ? View.GONE : View.VISIBLE);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
public void updateProState()
|
||||
{
|
||||
if (adView != null) adView.setVisibility(IABService.inst().getPurchaseCached(IABService.IAB_PRO_MODE) != null ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction, int position)
|
||||
{
|
||||
if (viewHolder instanceof MessageAdapter.MessagePresenter)
|
||||
{
|
||||
final int deletedIndex = viewHolder.getAdapterPosition();
|
||||
|
||||
final CMessage deletedItem = adpMessages.removeItem(viewHolder.getAdapterPosition());
|
||||
String name = deletedItem.Title;
|
||||
|
||||
Snackbar snackbar = Snackbar.make(SCNApp.getMainActivity().layoutRoot, name + " removed", Snackbar.LENGTH_LONG);
|
||||
snackbar.setAction("UNDO", view -> adpMessages.restoreItem(deletedItem, deletedIndex));
|
||||
snackbar.setActionTextColor(Color.YELLOW);
|
||||
snackbar.show();
|
||||
}
|
||||
}
|
||||
|
||||
public void updateDeleteSwipeEnabled()
|
||||
{
|
||||
if (touchHelper != null) touchHelper.updateEnabled();
|
||||
}
|
||||
}
|
||||
|
@@ -1,15 +1,507 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.media.AudioAttributes;
|
||||
import android.media.AudioManager;
|
||||
import android.media.MediaPlayer;
|
||||
import android.media.Ringtone;
|
||||
import android.media.RingtoneManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.preference.PreferenceFragmentCompat;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.Switch;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.android.billingclient.api.Purchase;
|
||||
import com.blackforestbytes.simplecloudnotifier.R;
|
||||
import com.blackforestbytes.simplecloudnotifier.SCNApp;
|
||||
import com.blackforestbytes.simplecloudnotifier.lib.android.ThreadUtils;
|
||||
import com.blackforestbytes.simplecloudnotifier.lib.lambda.FI;
|
||||
import com.blackforestbytes.simplecloudnotifier.lib.string.Str;
|
||||
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
|
||||
import com.blackforestbytes.simplecloudnotifier.service.IABService;
|
||||
|
||||
public class SettingsFragment extends PreferenceFragmentCompat
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import top.defaults.colorpicker.ColorPickerPopup;
|
||||
import xyz.aprildown.ultimatemusicpicker.MusicPickerListener;
|
||||
import xyz.aprildown.ultimatemusicpicker.UltimateMusicPicker;
|
||||
|
||||
public class SettingsFragment extends Fragment implements MusicPickerListener
|
||||
{
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey)
|
||||
private Switch prefAppEnabled;
|
||||
private Spinner prefLocalCacheSize;
|
||||
private Button prefUpgradeAccount;
|
||||
private TextView prefUpgradeAccount_msg;
|
||||
private TextView prefUpgradeAccount_info;
|
||||
private Switch prefEnableDeleteSwipe;
|
||||
|
||||
private Switch prefMsgLowEnableSound;
|
||||
private TextView prefMsgLowRingtone_value;
|
||||
private View prefMsgLowRingtone_container;
|
||||
private Switch prefMsgLowRepeatSound;
|
||||
private Switch prefMsgLowEnableLED;
|
||||
private View prefMsgLowLedColor_container;
|
||||
private ImageView prefMsgLowLedColor_value;
|
||||
private Switch prefMsgLowEnableVibrations;
|
||||
private Switch prefMsgLowForceVolume;
|
||||
private SeekBar prefMsgLowVolume;
|
||||
private ImageView prefMsgLowVolumeTest;
|
||||
|
||||
private Switch prefMsgNormEnableSound;
|
||||
private TextView prefMsgNormRingtone_value;
|
||||
private View prefMsgNormRingtone_container;
|
||||
private Switch prefMsgNormRepeatSound;
|
||||
private Switch prefMsgNormEnableLED;
|
||||
private View prefMsgNormLedColor_container;
|
||||
private ImageView prefMsgNormLedColor_value;
|
||||
private Switch prefMsgNormEnableVibrations;
|
||||
private Switch prefMsgNormForceVolume;
|
||||
private SeekBar prefMsgNormVolume;
|
||||
private ImageView prefMsgNormVolumeTest;
|
||||
|
||||
private Switch prefMsgHighEnableSound;
|
||||
private TextView prefMsgHighRingtone_value;
|
||||
private View prefMsgHighRingtone_container;
|
||||
private Switch prefMsgHighRepeatSound;
|
||||
private Switch prefMsgHighEnableLED;
|
||||
private View prefMsgHighLedColor_container;
|
||||
private ImageView prefMsgHighLedColor_value;
|
||||
private Switch prefMsgHighEnableVibrations;
|
||||
private Switch prefMsgHighForceVolume;
|
||||
private SeekBar prefMsgHighVolume;
|
||||
private ImageView prefMsgHighVolumeTest;
|
||||
|
||||
private int musicPickerSwitch = -1;
|
||||
|
||||
private MediaPlayer[] mPlayers = new MediaPlayer[3];
|
||||
|
||||
public SettingsFragment()
|
||||
{
|
||||
setPreferencesFromResource(R.xml.preferences, rootKey);
|
||||
// Required empty public constructor
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
|
||||
{
|
||||
View v = inflater.inflate(R.layout.fragment_settings, container, false);
|
||||
|
||||
initFields(v);
|
||||
updateUI();
|
||||
initListener();
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
private void initFields(View v)
|
||||
{
|
||||
prefAppEnabled = v.findViewById(R.id.prefAppEnabled);
|
||||
prefLocalCacheSize = v.findViewById(R.id.prefLocalCacheSize);
|
||||
prefUpgradeAccount = v.findViewById(R.id.prefUpgradeAccount);
|
||||
prefUpgradeAccount_msg = v.findViewById(R.id.prefUpgradeAccount2);
|
||||
prefUpgradeAccount_info = v.findViewById(R.id.prefUpgradeAccount_info);
|
||||
prefEnableDeleteSwipe = v.findViewById(R.id.prefEnableDeleteSwipe);
|
||||
|
||||
prefMsgLowEnableSound = v.findViewById(R.id.prefMsgLowEnableSound);
|
||||
prefMsgLowRingtone_value = v.findViewById(R.id.prefMsgLowRingtone_value);
|
||||
prefMsgLowRingtone_container = v.findViewById(R.id.prefMsgLowRingtone_container);
|
||||
prefMsgLowRepeatSound = v.findViewById(R.id.prefMsgLowRepeatSound);
|
||||
prefMsgLowEnableLED = v.findViewById(R.id.prefMsgLowEnableLED);
|
||||
prefMsgLowLedColor_value = v.findViewById(R.id.prefMsgLowLedColor_value);
|
||||
prefMsgLowLedColor_container = v.findViewById(R.id.prefMsgLowLedColor_container);
|
||||
prefMsgLowEnableVibrations = v.findViewById(R.id.prefMsgLowEnableVibrations);
|
||||
prefMsgLowForceVolume = v.findViewById(R.id.prefMsgLowForceVolume);
|
||||
prefMsgLowVolume = v.findViewById(R.id.prefMsgLowVolume);
|
||||
prefMsgLowVolumeTest = v.findViewById(R.id.btnLowVolumeTest);
|
||||
|
||||
prefMsgNormEnableSound = v.findViewById(R.id.prefMsgNormEnableSound);
|
||||
prefMsgNormRingtone_value = v.findViewById(R.id.prefMsgNormRingtone_value);
|
||||
prefMsgNormRingtone_container = v.findViewById(R.id.prefMsgNormRingtone_container);
|
||||
prefMsgNormRepeatSound = v.findViewById(R.id.prefMsgNormRepeatSound);
|
||||
prefMsgNormEnableLED = v.findViewById(R.id.prefMsgNormEnableLED);
|
||||
prefMsgNormLedColor_value = v.findViewById(R.id.prefMsgNormLedColor_value);
|
||||
prefMsgNormLedColor_container = v.findViewById(R.id.prefMsgNormLedColor_container);
|
||||
prefMsgNormEnableVibrations = v.findViewById(R.id.prefMsgNormEnableVibrations);
|
||||
prefMsgNormForceVolume = v.findViewById(R.id.prefMsgNormForceVolume);
|
||||
prefMsgNormVolume = v.findViewById(R.id.prefMsgNormVolume);
|
||||
prefMsgNormVolumeTest = v.findViewById(R.id.btnNormVolumeTest);
|
||||
|
||||
prefMsgHighEnableSound = v.findViewById(R.id.prefMsgHighEnableSound);
|
||||
prefMsgHighRingtone_value = v.findViewById(R.id.prefMsgHighRingtone_value);
|
||||
prefMsgHighRingtone_container = v.findViewById(R.id.prefMsgHighRingtone_container);
|
||||
prefMsgHighRepeatSound = v.findViewById(R.id.prefMsgHighRepeatSound);
|
||||
prefMsgHighEnableLED = v.findViewById(R.id.prefMsgHighEnableLED);
|
||||
prefMsgHighLedColor_value = v.findViewById(R.id.prefMsgHighLedColor_value);
|
||||
prefMsgHighLedColor_container = v.findViewById(R.id.prefMsgHighLedColor_container);
|
||||
prefMsgHighEnableVibrations = v.findViewById(R.id.prefMsgHighEnableVibrations);
|
||||
prefMsgHighForceVolume = v.findViewById(R.id.prefMsgHighForceVolume);
|
||||
prefMsgHighVolume = v.findViewById(R.id.prefMsgHighVolume);
|
||||
prefMsgHighVolumeTest = v.findViewById(R.id.btnHighVolumeTest);
|
||||
|
||||
ArrayAdapter<Integer> 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);
|
||||
}
|
||||
|
||||
private void updateUI()
|
||||
{
|
||||
SCNSettings s = SCNSettings.inst();
|
||||
Context c = getContext();
|
||||
if (c == null) return;
|
||||
|
||||
if (prefAppEnabled.isChecked() != s.Enabled) prefAppEnabled.setChecked(s.Enabled);
|
||||
if (prefEnableDeleteSwipe.isChecked() != s.EnableDeleteSwipe) prefEnableDeleteSwipe.setChecked(s.EnableDeleteSwipe);
|
||||
|
||||
prefUpgradeAccount.setVisibility( SCNSettings.inst().promode_local ? View.GONE : View.VISIBLE);
|
||||
prefUpgradeAccount_info.setVisibility(SCNSettings.inst().promode_local ? View.GONE : View.VISIBLE);
|
||||
prefUpgradeAccount_msg.setVisibility( SCNSettings.inst().promode_local ? View.VISIBLE : View.GONE );
|
||||
|
||||
if (prefLocalCacheSize.getSelectedItemPosition() != getCacheSizeIndex(s.LocalCacheSize)) prefLocalCacheSize.setSelection(getCacheSizeIndex(s.LocalCacheSize));
|
||||
|
||||
if (prefMsgLowEnableSound.isChecked() != s.PriorityLow.EnableSound) prefMsgLowEnableSound.setChecked(s.PriorityLow.EnableSound);
|
||||
if (!prefMsgLowRingtone_value.getText().equals(s.PriorityLow.SoundName)) prefMsgLowRingtone_value.setText(s.PriorityLow.SoundName);
|
||||
if (prefMsgLowRepeatSound.isChecked() != s.PriorityLow.RepeatSound) prefMsgLowRepeatSound.setChecked(s.PriorityLow.RepeatSound);
|
||||
if (prefMsgLowEnableLED.isChecked() != s.PriorityLow.EnableLED) prefMsgLowEnableLED.setChecked(s.PriorityLow.EnableLED);
|
||||
prefMsgLowLedColor_value.setColorFilter(s.PriorityLow.LEDColor);
|
||||
if (prefMsgLowEnableVibrations.isChecked() != s.PriorityLow.EnableVibration) prefMsgLowEnableVibrations.setChecked(s.PriorityLow.EnableVibration);
|
||||
if (prefMsgLowForceVolume.isChecked() != s.PriorityLow.ForceVolume) prefMsgLowForceVolume.setChecked(s.PriorityLow.ForceVolume);
|
||||
if (prefMsgLowVolume.getMax() != 100) prefMsgLowVolume.setMax(100);
|
||||
if (prefMsgLowVolume.getProgress() != s.PriorityLow.ForceVolumeValue) prefMsgLowVolume.setProgress(s.PriorityLow.ForceVolumeValue);
|
||||
if (prefMsgLowVolume.isEnabled() != s.PriorityLow.ForceVolume) prefMsgLowVolume.setEnabled(s.PriorityLow.ForceVolume);
|
||||
if (prefMsgLowVolumeTest.isEnabled() != s.PriorityLow.ForceVolume) prefMsgLowVolumeTest.setEnabled(s.PriorityLow.ForceVolume);
|
||||
if (s.PriorityLow.ForceVolume) prefMsgLowVolumeTest.setColorFilter(null); else prefMsgLowVolumeTest.setColorFilter(Color.argb(150,200,200,200));
|
||||
|
||||
if (prefMsgNormEnableSound.isChecked() != s.PriorityNorm.EnableSound) prefMsgNormEnableSound.setChecked(s.PriorityNorm.EnableSound);
|
||||
if (!prefMsgNormRingtone_value.getText().equals(s.PriorityNorm.SoundName)) prefMsgNormRingtone_value.setText(s.PriorityNorm.SoundName);
|
||||
if (prefMsgNormRepeatSound.isChecked() != s.PriorityNorm.RepeatSound) prefMsgNormRepeatSound.setChecked(s.PriorityNorm.RepeatSound);
|
||||
if (prefMsgNormEnableLED.isChecked() != s.PriorityNorm.EnableLED) prefMsgNormEnableLED.setChecked(s.PriorityNorm.EnableLED);
|
||||
prefMsgNormLedColor_value.setColorFilter(s.PriorityNorm.LEDColor);
|
||||
if (prefMsgNormEnableVibrations.isChecked() != s.PriorityNorm.EnableVibration) prefMsgNormEnableVibrations.setChecked(s.PriorityNorm.EnableVibration);
|
||||
if (prefMsgNormForceVolume.isChecked() != s.PriorityNorm.ForceVolume) prefMsgNormForceVolume.setChecked(s.PriorityNorm.ForceVolume);
|
||||
if (prefMsgNormVolume.getMax() != 100) prefMsgNormVolume.setMax(100);
|
||||
if (prefMsgNormVolume.getProgress() != s.PriorityNorm.ForceVolumeValue) prefMsgNormVolume.setProgress(s.PriorityNorm.ForceVolumeValue);
|
||||
if (prefMsgNormVolume.isEnabled() != s.PriorityNorm.ForceVolume) prefMsgNormVolume.setEnabled(s.PriorityNorm.ForceVolume);
|
||||
if (prefMsgNormVolumeTest.isEnabled() != s.PriorityNorm.ForceVolume) prefMsgNormVolumeTest.setEnabled(s.PriorityNorm.ForceVolume);
|
||||
if (s.PriorityNorm.ForceVolume) prefMsgNormVolumeTest.setColorFilter(null); else prefMsgNormVolumeTest.setColorFilter(Color.argb(150,200,200,200));
|
||||
|
||||
if (prefMsgHighEnableSound.isChecked() != s.PriorityHigh.EnableSound) prefMsgHighEnableSound.setChecked(s.PriorityHigh.EnableSound);
|
||||
if (!prefMsgHighRingtone_value.getText().equals(s.PriorityHigh.SoundName)) prefMsgHighRingtone_value.setText(s.PriorityHigh.SoundName);
|
||||
if (prefMsgHighRepeatSound.isChecked() != s.PriorityHigh.RepeatSound) prefMsgHighRepeatSound.setChecked(s.PriorityHigh.RepeatSound);
|
||||
if (prefMsgHighEnableLED.isChecked() != s.PriorityHigh.EnableLED) prefMsgHighEnableLED.setChecked(s.PriorityHigh.EnableLED);
|
||||
prefMsgHighLedColor_value.setColorFilter(s.PriorityHigh.LEDColor);
|
||||
if (prefMsgHighEnableVibrations.isChecked() != s.PriorityHigh.EnableVibration) prefMsgHighEnableVibrations.setChecked(s.PriorityHigh.EnableVibration);
|
||||
if (prefMsgHighForceVolume.isChecked() != s.PriorityHigh.ForceVolume) prefMsgHighForceVolume.setChecked(s.PriorityHigh.ForceVolume);
|
||||
if (prefMsgHighVolume.getMax() != 100) prefMsgHighVolume.setMax(100);
|
||||
if (prefMsgHighVolume.getProgress() != s.PriorityHigh.ForceVolumeValue) prefMsgHighVolume.setProgress(s.PriorityHigh.ForceVolumeValue);
|
||||
if (prefMsgHighVolume.isEnabled() != s.PriorityHigh.ForceVolume) prefMsgHighVolume.setEnabled(s.PriorityHigh.ForceVolume);
|
||||
if (prefMsgHighVolumeTest.isEnabled() != s.PriorityHigh.ForceVolume) prefMsgHighVolumeTest.setEnabled(s.PriorityHigh.ForceVolume);
|
||||
if (s.PriorityHigh.ForceVolume) prefMsgHighVolumeTest.setColorFilter(null); else prefMsgHighVolumeTest.setColorFilter(Color.argb(150,200,200,200));
|
||||
}
|
||||
|
||||
private void initListener()
|
||||
{
|
||||
SCNSettings s = SCNSettings.inst();
|
||||
|
||||
prefAppEnabled.setOnCheckedChangeListener((a,b) -> { boolean prev=s.Enabled; s.Enabled=b; saveAndUpdate(); updateEnabled(prev, b); });
|
||||
prefEnableDeleteSwipe.setOnCheckedChangeListener((a,b) -> { s.EnableDeleteSwipe=b; saveAndUpdate(); });
|
||||
|
||||
prefLocalCacheSize.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener()
|
||||
{
|
||||
@Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
s.LocalCacheSize = prefLocalCacheSize.getSelectedItemPosition()>=0 ? SCNSettings.CHOOSABLE_CACHE_SIZES[prefLocalCacheSize.getSelectedItemPosition()] : 100;
|
||||
saveAndUpdate();
|
||||
}
|
||||
@Override public void onNothingSelected(AdapterView<?> parent) { /* */ }
|
||||
});
|
||||
|
||||
prefUpgradeAccount.setOnClickListener(a -> onUpgradeAccount());
|
||||
|
||||
prefMsgLowEnableSound.setOnCheckedChangeListener((a,b) -> { s.PriorityLow.EnableSound=b; saveAndUpdate(); });
|
||||
prefMsgLowRingtone_container.setOnClickListener(a -> chooseRingtoneLow());
|
||||
prefMsgLowRepeatSound.setOnCheckedChangeListener((a,b) -> { s.PriorityLow.RepeatSound=b; saveAndUpdate(); });
|
||||
prefMsgLowEnableLED.setOnCheckedChangeListener((a,b) -> { s.PriorityLow.EnableLED=b; saveAndUpdate(); });
|
||||
prefMsgLowLedColor_container.setOnClickListener(a -> chooseLEDColorLow());
|
||||
prefMsgLowEnableVibrations.setOnCheckedChangeListener((a,b) -> { s.PriorityLow.EnableVibration=b; saveAndUpdate(); });
|
||||
prefMsgLowForceVolume.setOnCheckedChangeListener((a,b) -> { s.PriorityLow.ForceVolume=b; saveAndUpdate(); });
|
||||
prefMsgLowVolume.setOnSeekBarChangeListener(FI.SeekBarChanged((a,b,c) -> { if (c) { s.PriorityLow.ForceVolumeValue=b; saveAndUpdate(); updateVolume(0, b); } }));
|
||||
prefMsgLowVolumeTest.setOnClickListener((v) -> { if (s.PriorityLow.ForceVolume) playTestSound(0, prefMsgLowVolumeTest, s.PriorityLow.SoundSource, s.PriorityLow.ForceVolumeValue); });
|
||||
|
||||
prefMsgNormEnableSound.setOnCheckedChangeListener((a,b) -> { s.PriorityNorm.EnableSound=b; saveAndUpdate(); });
|
||||
prefMsgNormRingtone_container.setOnClickListener(a -> chooseRingtoneNorm());
|
||||
prefMsgNormRepeatSound.setOnCheckedChangeListener((a,b) -> { s.PriorityNorm.RepeatSound=b; saveAndUpdate(); });
|
||||
prefMsgNormEnableLED.setOnCheckedChangeListener((a,b) -> { s.PriorityNorm.EnableLED=b; saveAndUpdate(); });
|
||||
prefMsgNormLedColor_container.setOnClickListener(a -> chooseLEDColorNorm());
|
||||
prefMsgNormEnableVibrations.setOnCheckedChangeListener((a,b) -> { s.PriorityNorm.EnableVibration=b; saveAndUpdate(); });
|
||||
prefMsgNormForceVolume.setOnCheckedChangeListener((a,b) -> { s.PriorityNorm.ForceVolume=b; saveAndUpdate(); });
|
||||
prefMsgNormVolume.setOnSeekBarChangeListener(FI.SeekBarChanged((a,b,c) -> { if (c) { s.PriorityNorm.ForceVolumeValue=b; saveAndUpdate(); updateVolume(1, b); } }));
|
||||
prefMsgNormVolumeTest.setOnClickListener((v) -> { if (s.PriorityNorm.ForceVolume) playTestSound(1, prefMsgNormVolumeTest, s.PriorityNorm.SoundSource, s.PriorityNorm.ForceVolumeValue); });
|
||||
|
||||
prefMsgHighEnableSound.setOnCheckedChangeListener((a,b) -> { s.PriorityHigh.EnableSound=b; saveAndUpdate(); });
|
||||
prefMsgHighRingtone_container.setOnClickListener(a -> chooseRingtoneHigh());
|
||||
prefMsgHighRepeatSound.setOnCheckedChangeListener((a,b) -> { s.PriorityHigh.RepeatSound=b; saveAndUpdate(); });
|
||||
prefMsgHighEnableLED.setOnCheckedChangeListener((a,b) -> { s.PriorityHigh.EnableLED=b; saveAndUpdate(); });
|
||||
prefMsgHighLedColor_container.setOnClickListener(a -> chooseLEDColorHigh());
|
||||
prefMsgHighEnableVibrations.setOnCheckedChangeListener((a,b) -> { s.PriorityHigh.EnableVibration=b; saveAndUpdate(); });
|
||||
prefMsgHighForceVolume.setOnCheckedChangeListener((a,b) -> { s.PriorityHigh.ForceVolume=b; saveAndUpdate(); });
|
||||
prefMsgHighVolume.setOnSeekBarChangeListener(FI.SeekBarChanged((a,b,c) -> { if (c) { s.PriorityHigh.ForceVolumeValue=b; saveAndUpdate(); updateVolume(2, b); } }));
|
||||
prefMsgHighVolumeTest.setOnClickListener((v) -> { if (s.PriorityHigh.ForceVolume) playTestSound(2, prefMsgHighVolumeTest, s.PriorityHigh.SoundSource, s.PriorityHigh.ForceVolumeValue); });
|
||||
}
|
||||
|
||||
private void updateEnabled(boolean prev, boolean now)
|
||||
{
|
||||
if (!prev && now)
|
||||
{
|
||||
SCNApp.showToast("SimpleCloudNotifier is now enabled", Toast.LENGTH_SHORT);
|
||||
}
|
||||
else if (prev && !now)
|
||||
{
|
||||
SCNApp.showToast("SimpleCloudNotifier is now disabled\nYou won't recieve new messages.", Toast.LENGTH_LONG);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateVolume(int idx, int volume)
|
||||
{
|
||||
if (mPlayers[idx] != null && mPlayers[idx].isPlaying())
|
||||
{
|
||||
AudioManager aman = (AudioManager) SCNApp.getContext().getSystemService(Context.AUDIO_SERVICE);
|
||||
int maxVolume = aman.getStreamMaxVolume(AudioManager.STREAM_NOTIFICATION);
|
||||
aman.setStreamVolume(AudioManager.STREAM_NOTIFICATION, (int)(maxVolume * (volume / 100.0)), 0);
|
||||
}
|
||||
}
|
||||
|
||||
private void stopSound(final int idx, final ImageView iv)
|
||||
{
|
||||
if (mPlayers[idx] != null && mPlayers[idx].isPlaying())
|
||||
{
|
||||
mPlayers[idx].stop();
|
||||
mPlayers[idx].release();
|
||||
iv.setImageResource(R.drawable.ic_play);
|
||||
mPlayers[idx] = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void playTestSound(final int idx, final ImageView iv, String src, int volume)
|
||||
{
|
||||
if (mPlayers[idx] != null && mPlayers[idx].isPlaying())
|
||||
{
|
||||
mPlayers[idx].stop();
|
||||
mPlayers[idx].release();
|
||||
iv.setImageResource(R.drawable.ic_play);
|
||||
mPlayers[idx] = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Str.isNullOrWhitespace(src)) return;
|
||||
if (volume == 0) return;
|
||||
|
||||
Context ctxt = getContext();
|
||||
if (ctxt == null) return;
|
||||
|
||||
iv.setImageResource(R.drawable.ic_pause);
|
||||
|
||||
AudioManager aman = (AudioManager) SCNApp.getContext().getSystemService(Context.AUDIO_SERVICE);
|
||||
int maxVolume = aman.getStreamMaxVolume(AudioManager.STREAM_NOTIFICATION);
|
||||
aman.setStreamVolume(AudioManager.STREAM_NOTIFICATION, (int)(maxVolume * (volume / 100.0)), 0);
|
||||
|
||||
MediaPlayer player = mPlayers[idx] = new MediaPlayer();
|
||||
player.setAudioAttributes(new AudioAttributes.Builder().setLegacyStreamType(AudioManager.STREAM_NOTIFICATION).build());
|
||||
player.setAudioStreamType(AudioManager.STREAM_NOTIFICATION);
|
||||
|
||||
try
|
||||
{
|
||||
player.setDataSource(ctxt, Uri.parse(src));
|
||||
player.setLooping(false);
|
||||
player.setOnCompletionListener( mp -> SCNApp.runOnUiThread(() -> { mp.stop(); iv.setImageResource(R.drawable.ic_play); mPlayers[idx]=null; mp.release(); }));
|
||||
player.setOnSeekCompleteListener(mp -> SCNApp.runOnUiThread(() -> { mp.stop(); iv.setImageResource(R.drawable.ic_play); mPlayers[idx]=null; mp.release(); }));
|
||||
player.prepare();
|
||||
player.start();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
Log.e("SFRAG:play", e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private void saveAndUpdate()
|
||||
{
|
||||
SCNSettings.inst().save();
|
||||
updateUI();
|
||||
SCNApp.getMainActivity().adpTabs.tab1.updateDeleteSwipeEnabled();
|
||||
}
|
||||
|
||||
private void onUpgradeAccount()
|
||||
{
|
||||
IABService.inst().purchase(getActivity(), IABService.IAB_PRO_MODE);
|
||||
}
|
||||
|
||||
public void updateProState()
|
||||
{
|
||||
Purchase p = IABService.inst().getPurchaseCached(IABService.IAB_PRO_MODE);
|
||||
|
||||
if (prefUpgradeAccount != null) prefUpgradeAccount.setVisibility( p != null ? View.GONE : View.VISIBLE);
|
||||
if (prefUpgradeAccount_info != null) prefUpgradeAccount_info.setVisibility(p != null ? View.GONE : View.VISIBLE);
|
||||
if (prefUpgradeAccount_msg != null) prefUpgradeAccount_msg.setVisibility( p != null ? View.VISIBLE : View.GONE );
|
||||
}
|
||||
|
||||
private int getCacheSizeIndex(int value)
|
||||
{
|
||||
for (int i = 0; i < SCNSettings.CHOOSABLE_CACHE_SIZES.length; i++)
|
||||
{
|
||||
if (SCNSettings.CHOOSABLE_CACHE_SIZES[i] == value) return i;
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
|
||||
private void chooseRingtoneLow()
|
||||
{
|
||||
musicPickerSwitch = 1;
|
||||
UltimateMusicPicker ump = new UltimateMusicPicker();
|
||||
ump.windowTitle("Choose notification sound");
|
||||
ump.removeSilent();
|
||||
ump.streamType(AudioManager.STREAM_NOTIFICATION);
|
||||
ump.ringtone();
|
||||
ump.notification();
|
||||
ump.alarm();
|
||||
ump.music();
|
||||
if (!SCNSettings.inst().PriorityLow.SoundSource.isEmpty())ump.selectUri(Uri.parse(SCNSettings.inst().PriorityLow.SoundSource));
|
||||
ump.goWithDialog(getChildFragmentManager());
|
||||
}
|
||||
|
||||
private void chooseRingtoneNorm()
|
||||
{
|
||||
musicPickerSwitch = 2;
|
||||
UltimateMusicPicker ump = new UltimateMusicPicker();
|
||||
ump.windowTitle("Choose notification sound");
|
||||
ump.removeSilent();
|
||||
ump.streamType(AudioManager.STREAM_NOTIFICATION);
|
||||
ump.ringtone();
|
||||
ump.notification();
|
||||
ump.alarm();
|
||||
ump.music();
|
||||
if (!SCNSettings.inst().PriorityNorm.SoundSource.isEmpty())ump.defaultUri(Uri.parse(SCNSettings.inst().PriorityNorm.SoundSource));
|
||||
ump.goWithDialog(getChildFragmentManager());
|
||||
}
|
||||
|
||||
private void chooseRingtoneHigh()
|
||||
{
|
||||
musicPickerSwitch = 3;
|
||||
UltimateMusicPicker ump = new UltimateMusicPicker();
|
||||
ump.windowTitle("Choose notification sound");
|
||||
ump.removeSilent();
|
||||
ump.streamType(AudioManager.STREAM_NOTIFICATION);
|
||||
ump.ringtone();
|
||||
ump.notification();
|
||||
ump.alarm();
|
||||
ump.music();
|
||||
if (!SCNSettings.inst().PriorityHigh.SoundSource.isEmpty())ump.defaultUri(Uri.parse(SCNSettings.inst().PriorityHigh.SoundSource));
|
||||
ump.goWithDialog(getChildFragmentManager());
|
||||
}
|
||||
|
||||
private void chooseLEDColorLow()
|
||||
{
|
||||
new ColorPickerPopup.Builder(getContext())
|
||||
.initialColor(SCNSettings.inst().PriorityLow.LEDColor) // Set initial color
|
||||
.enableBrightness(true) // Enable brightness slider or not
|
||||
.okTitle("Choose")
|
||||
.cancelTitle("Cancel")
|
||||
.showIndicator(true)
|
||||
.showValue(false)
|
||||
.build()
|
||||
.show(getView(), new ColorPickerPopup.ColorPickerObserver()
|
||||
{
|
||||
@Override
|
||||
public void onColorPicked(int color) {
|
||||
SCNSettings.inst().PriorityLow.LEDColor = color;
|
||||
saveAndUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onColor(int color, boolean fromUser) { }
|
||||
});
|
||||
}
|
||||
|
||||
private void chooseLEDColorNorm()
|
||||
{
|
||||
new ColorPickerPopup.Builder(getContext())
|
||||
.initialColor(SCNSettings.inst().PriorityNorm.LEDColor) // Set initial color
|
||||
.enableBrightness(true) // Enable brightness slider or not
|
||||
.okTitle("Choose")
|
||||
.cancelTitle("Cancel")
|
||||
.showIndicator(true)
|
||||
.showValue(false)
|
||||
.build()
|
||||
.show(getView(), new ColorPickerPopup.ColorPickerObserver()
|
||||
{
|
||||
@Override
|
||||
public void onColorPicked(int color) {
|
||||
SCNSettings.inst().PriorityNorm.LEDColor = color;
|
||||
saveAndUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onColor(int color, boolean fromUser) { }
|
||||
});
|
||||
}
|
||||
|
||||
private void chooseLEDColorHigh()
|
||||
{
|
||||
new ColorPickerPopup.Builder(getContext())
|
||||
.initialColor(SCNSettings.inst().PriorityHigh.LEDColor) // Set initial color
|
||||
.enableBrightness(true) // Enable brightness slider or not
|
||||
.okTitle("Choose")
|
||||
.cancelTitle("Cancel")
|
||||
.showIndicator(true)
|
||||
.showValue(false)
|
||||
.build()
|
||||
.show(getView(), new ColorPickerPopup.ColorPickerObserver()
|
||||
{
|
||||
@Override
|
||||
public void onColorPicked(int color) {
|
||||
SCNSettings.inst().PriorityHigh.LEDColor = color;
|
||||
saveAndUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onColor(int color, boolean fromUser) { }
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMusicPick(@NotNull Uri uri, @NotNull String s)
|
||||
{
|
||||
if (musicPickerSwitch == 1) { SCNSettings.inst().PriorityLow.SoundSource =uri.toString(); SCNSettings.inst().PriorityLow.SoundName =s; saveAndUpdate(); }
|
||||
if (musicPickerSwitch == 2) { SCNSettings.inst().PriorityNorm.SoundSource=uri.toString(); SCNSettings.inst().PriorityNorm.SoundName=s; saveAndUpdate(); }
|
||||
if (musicPickerSwitch == 3) { SCNSettings.inst().PriorityHigh.SoundSource=uri.toString(); SCNSettings.inst().PriorityHigh.SoundName=s; saveAndUpdate(); }
|
||||
|
||||
musicPickerSwitch = -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPickCanceled()
|
||||
{
|
||||
musicPickerSwitch = -1;
|
||||
}
|
||||
|
||||
public void onViewpagerHide()
|
||||
{
|
||||
stopSound(0, prefMsgLowVolumeTest);
|
||||
stopSound(1, prefMsgNormVolumeTest);
|
||||
stopSound(2, prefMsgHighVolumeTest);
|
||||
}
|
||||
}
|
@@ -1,8 +1,8 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.view;
|
||||
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v4.app.FragmentStatePagerAdapter;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentStatePagerAdapter;
|
||||
|
||||
public class TabAdapter extends FragmentStatePagerAdapter {
|
||||
|
||||
@@ -35,7 +35,7 @@ public class TabAdapter extends FragmentStatePagerAdapter {
|
||||
{
|
||||
switch (position)
|
||||
{
|
||||
case 0: return "Notifications";
|
||||
case 0: return "Messages";
|
||||
case 1: return "Account";
|
||||
case 2: return "Settings";
|
||||
default: return null;
|
||||
|
BIN
android/app/src/main/res/drawable-hdpi/ic_notification_white.png
Normal file
After Width: | Height: | Size: 472 B |
BIN
android/app/src/main/res/drawable-mdpi/ic_notification_white.png
Normal file
After Width: | Height: | Size: 320 B |
After Width: | Height: | Size: 551 B |
After Width: | Height: | Size: 949 B |
After Width: | Height: | Size: 1.0 KiB |
6
android/app/src/main/res/drawable/circle.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<vector android:height="24dp" android:viewportHeight="1000"
|
||||
android:viewportWidth="1000" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000"
|
||||
android:pathData="M500,500m-500,0a500,500 0,1 1,1000 0a500,500 0,1 1,-1000 0"
|
||||
android:strokeColor="#000000" android:strokeWidth="1"/>
|
||||
</vector>
|
@@ -1,15 +0,0 @@
|
||||
<vector android:height="24dp" android:viewportHeight="1000"
|
||||
android:viewportWidth="1000" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000"
|
||||
android:pathData="M500,500m-500,0a500,500 0,1 1,1000 0a500,500 0,1 1,-1000 0"
|
||||
android:strokeColor="#000000" android:strokeWidth="1"/>
|
||||
<path android:fillColor="#ffffff" android:pathData="M300,694L700,694L500,136L300,694"/>
|
||||
<path android:fillColor="#ffffff" android:pathData="M473,559L527,559L527,774L473,774"/>
|
||||
<path android:fillColor="#000000" android:pathData="M376,640L624,640L500,295L376,640"/>
|
||||
<path android:fillColor="#ffffff" android:pathData="M100,730L500,730L300,172L100,730"/>
|
||||
<path android:fillColor="#000000" android:pathData="M176,676L424,676L300,331L176,676"/>
|
||||
<path android:fillColor="#ffffff" android:pathData="M273,595L327,595L327,810L273,810"/>
|
||||
<path android:fillColor="#ffffff" android:pathData="M500,730L900,730L700,172L500,730"/>
|
||||
<path android:fillColor="#000000" android:pathData="M576,676L824,676L700,331L576,676"/>
|
||||
<path android:fillColor="#ffffff" android:pathData="M673,595L727,595L727,810L673,810"/>
|
||||
</vector>
|
16
android/app/src/main/res/drawable/ic_pause.xml
Normal file
@@ -0,0 +1,16 @@
|
||||
<vector
|
||||
android:height="24dp"
|
||||
android:width="24dp"
|
||||
android:viewportHeight="438.536"
|
||||
android:viewportWidth="438.536"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<path
|
||||
android:fillColor="#FF3F51B5"
|
||||
android:pathData="M164.453,0H18.276C13.324,0 9.041,1.807 5.425,5.424C1.808,9.04 0.001,13.322 0.001,18.271v401.991c0,4.948 1.807,9.233 5.424,12.847c3.619,3.617 7.902,5.428 12.851,5.428h146.181c4.949,0 9.231,-1.811 12.847,-5.428c3.617,-3.613 5.424,-7.898 5.424,-12.847V18.271c0,-4.952 -1.807,-9.231 -5.428,-12.847C173.685,1.807 169.402,0 164.453,0z"/>
|
||||
|
||||
<path
|
||||
android:fillColor="#FF3F51B5"
|
||||
android:pathData="M433.113,5.424C429.496,1.807 425.215,0 420.267,0H274.086c-4.949,0 -9.237,1.807 -12.847,5.424c-3.621,3.615 -5.432,7.898 -5.432,12.847v401.991c0,4.948 1.811,9.233 5.432,12.847c3.609,3.617 7.897,5.428 12.847,5.428h146.181c4.948,0 9.229,-1.811 12.847,-5.428c3.614,-3.613 5.421,-7.898 5.421,-12.847V18.271C438.534,13.319 436.73,9.04 433.113,5.424z"/>
|
||||
|
||||
</vector>
|
12
android/app/src/main/res/drawable/ic_play.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<vector
|
||||
android:height="24dp"
|
||||
android:viewportHeight="41.999"
|
||||
android:viewportWidth="41.999"
|
||||
android:width="24dp"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<path
|
||||
android:fillColor="#FF3F51B5"
|
||||
android:pathData="M36.068,20.176l-29,-20C6.761,-0.035 6.363,-0.057 6.035,0.114C5.706,0.287 5.5,0.627 5.5,0.999v40c0,0.372 0.206,0.713 0.535,0.886c0.146,0.076 0.306,0.114 0.465,0.114c0.199,0 0.397,-0.06 0.568,-0.177l29,-20c0.271,-0.187 0.432,-0.494 0.432,-0.823S36.338,20.363 36.068,20.176z"/>
|
||||
|
||||
</vector>
|
16
android/app/src/main/res/drawable/ic_trash.xml
Normal file
@@ -0,0 +1,16 @@
|
||||
<vector
|
||||
android:height="24dp"
|
||||
android:width="24dp"
|
||||
android:viewportHeight="53"
|
||||
android:viewportWidth="53"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M42.943,6H33.5V3c0,-1.654 -1.346,-3 -3,-3h-8c-1.654,0 -3,1.346 -3,3v3h-9.443C8.096,6 6.5,7.596 6.5,9.557V14h2h36h2V9.557C46.5,7.596 44.904,6 42.943,6zM31.5,6h-10V3c0,-0.552 0.449,-1 1,-1h8c0.551,0 1,0.448 1,1V6z"/>
|
||||
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M8.5,49.271C8.5,51.327 10.173,53 12.229,53h28.541c2.057,0 3.729,-1.673 3.729,-3.729V16h-36V49.271z"/>
|
||||
|
||||
</vector>
|
12
android/app/src/main/res/drawable/ic_volume.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<vector
|
||||
android:height="24dp"
|
||||
android:viewportHeight="459"
|
||||
android:viewportWidth="459"
|
||||
android:width="24dp"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<path
|
||||
android:fillColor="#FF757575"
|
||||
android:pathData="M0,153v153h102l127.5,127.5v-408L102,153H0zM344.25,229.5c0,-45.9 -25.5,-84.15 -63.75,-102v204C318.75,313.65 344.25,275.4 344.25,229.5zM280.5,5.1v53.55C354.45,81.6 408,147.899 408,229.5S354.45,377.4 280.5,400.35V453.9C382.5,430.949 459,339.15 459,229.5C459,119.85 382.5,28.049 280.5,5.1z"/>
|
||||
|
||||
</vector>
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 114 KiB |
@@ -1,12 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout
|
||||
android:id="@+id/layoutRoot"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:showIn="@layout/activity_main">
|
||||
|
||||
<android.support.v7.widget.Toolbar
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -16,7 +17,7 @@
|
||||
android:minHeight="?attr/actionBarSize"
|
||||
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
|
||||
|
||||
<android.support.design.widget.TabLayout
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/tab_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -26,7 +27,7 @@
|
||||
android:minHeight="?attr/actionBarSize"
|
||||
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"/>
|
||||
|
||||
<android.support.v4.view.ViewPager
|
||||
<androidx.viewpager.widget.ViewPager
|
||||
android:id="@+id/pager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="fill_parent"
|
||||
|
@@ -7,7 +7,7 @@
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".view.AccountFragment">
|
||||
|
||||
<android.support.constraint.ConstraintLayout
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
@@ -216,7 +216,7 @@
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
tools:layout_editor_absoluteY="352dp" />
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/loadingPanel"
|
||||
|
@@ -3,9 +3,22 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:ads="http://schemas.android.com/apk/res-auto"
|
||||
tools:context=".view.NotificationsFragment">
|
||||
|
||||
<android.support.v7.widget.RecyclerView
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/pnlMessages"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@+id/adBanner"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rvMessages"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -19,4 +32,20 @@
|
||||
android:text="@string/no_notifications"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
|
||||
<com.google.android.gms.ads.doubleclick.PublisherAdView
|
||||
android:id="@+id/adBanner"
|
||||
app:layout_constraintTop_toBottomOf="@+id/pnlMessages"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
ads:adSize="SMART_BANNER"
|
||||
ads:adUnitId="ca-app-pub-3320562328966175/5524654300" />
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</FrameLayout>
|
768
android/app/src/main/res/layout/fragment_settings.xml
Normal file
@@ -0,0 +1,768 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<ScrollView
|
||||
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=".view.SettingsFragment">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="12dp">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:background="@color/colorHeader"
|
||||
android:textColor="@color/colorHeaderForeground"
|
||||
android:textSize="16sp"
|
||||
android:padding="4dp"
|
||||
android:text="@string/str_common_settings"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<Switch
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:id="@+id/prefAppEnabled"
|
||||
android:text="@string/str_enabled"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="48dp" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:background="#c0c0c0"/>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="48dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvLocalCacheSize"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/str_localcachesize"
|
||||
android:textColor="#000"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/prefLocalCacheSize"
|
||||
app:layout_constraintHorizontal_bias="0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<Spinner
|
||||
android:minWidth="64dp"
|
||||
android:id="@+id/prefLocalCacheSize"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:spinnerMode="dialog"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:background="#c0c0c0"/>
|
||||
|
||||
<Switch
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:id="@+id/prefEnableDeleteSwipe"
|
||||
android:text="@string/str_deleteswipe"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="48dp" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:background="#c0c0c0"/>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center|center"
|
||||
android:minHeight="48dp">
|
||||
|
||||
<Button
|
||||
android:id="@+id/prefUpgradeAccount"
|
||||
android:text="@string/str_upgrade_account"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/prefUpgradeAccount_info"
|
||||
android:textAlignment="center"
|
||||
android:text="@string/str_promode_info"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/prefUpgradeAccount2"
|
||||
android:textColor="#FF4D00"
|
||||
android:textStyle="bold"
|
||||
android:textAlignment="center"
|
||||
android:text="@string/str_promode"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="12dp">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:background="@color/colorHeader"
|
||||
android:textColor="@color/colorHeaderForeground"
|
||||
android:textSize="16sp"
|
||||
android:padding="4dp"
|
||||
android:text="@string/str_header_prio0"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<Switch
|
||||
android:id="@+id/prefMsgLowEnableSound"
|
||||
android:text="@string/str_msg_enablesound"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:minHeight="48dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:background="#c0c0c0"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/prefMsgLowRingtone_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:minHeight="48dp"
|
||||
android:gravity="center">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvMsgLowRingtone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/str_notificationsound"
|
||||
android:textColor="#000" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/prefMsgLowRingtone_value"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minWidth="64dp"
|
||||
android:spinnerMode="dialog"
|
||||
android:text="Whatever"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:background="#c0c0c0"/>
|
||||
|
||||
<Switch
|
||||
android:id="@+id/prefMsgLowRepeatSound"
|
||||
android:text="@string/str_repeatnotificationsound"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:minHeight="48dp" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:background="#c0c0c0"/>
|
||||
|
||||
<Switch
|
||||
android:id="@+id/prefMsgLowForceVolume"
|
||||
android:text="@string/str_forcevolume"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:minHeight="48dp" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:columnCount="3"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="4dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/icnLowVolume"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@string/volume_icon"
|
||||
android:src="@drawable/ic_volume"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/prefMsgLowVolume"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/btnLowVolumeTest"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toEndOf="@+id/icnLowVolume"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/btnLowVolumeTest"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_play"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:contentDescription="@string/play_test_sound" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:background="#c0c0c0"/>
|
||||
|
||||
<Switch
|
||||
android:id="@+id/prefMsgLowEnableLED"
|
||||
android:text="@string/str_enable_led"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:minHeight="48dp" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:background="#c0c0c0"/>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/prefMsgLowLedColor_container"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="48dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/str_ledcolor"
|
||||
android:textColor="#000"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/prefMsgLowLedColor_value"
|
||||
app:layout_constraintHorizontal_bias="0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:src="@drawable/circle"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:id="@+id/prefMsgLowLedColor_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:spinnerMode="dialog"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:background="#c0c0c0"/>
|
||||
|
||||
<Switch
|
||||
android:id="@+id/prefMsgLowEnableVibrations"
|
||||
android:text="@string/str_enable_vibration"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:minHeight="48dp" />
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="12dp">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:background="@color/colorHeader"
|
||||
android:textColor="@color/colorHeaderForeground"
|
||||
android:textSize="16sp"
|
||||
android:padding="4dp"
|
||||
android:text="@string/str_header_prio1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<Switch
|
||||
android:id="@+id/prefMsgNormEnableSound"
|
||||
android:text="@string/str_msg_enablesound"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:minHeight="48dp" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:background="#c0c0c0"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/prefMsgNormRingtone_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:minHeight="48dp"
|
||||
android:gravity="center">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvMsgNormRingtone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/str_notificationsound"
|
||||
android:textColor="#000" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/prefMsgNormRingtone_value"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minWidth="64dp"
|
||||
android:spinnerMode="dialog"
|
||||
android:text="Whatever"
|
||||
tools:ignore="HardcodedText" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:background="#c0c0c0"/>
|
||||
|
||||
<Switch
|
||||
android:id="@+id/prefMsgNormForceVolume"
|
||||
android:text="@string/str_forcevolume"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:minHeight="48dp" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:columnCount="3"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="4dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/icnNormVolume"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@string/volume_icon"
|
||||
android:src="@drawable/ic_volume"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/prefMsgNormVolume"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/btnNormVolumeTest"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toEndOf="@+id/icnNormVolume"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/btnNormVolumeTest"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_play"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:contentDescription="@string/play_test_sound" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:background="#c0c0c0"/>
|
||||
|
||||
<Switch
|
||||
android:id="@+id/prefMsgNormRepeatSound"
|
||||
android:text="@string/str_repeatnotificationsound"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:minHeight="48dp" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:background="#c0c0c0"/>
|
||||
|
||||
<Switch
|
||||
android:id="@+id/prefMsgNormEnableLED"
|
||||
android:text="@string/str_enable_led"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:minHeight="48dp" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:background="#c0c0c0"/>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/prefMsgNormLedColor_container"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="48dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/str_ledcolor"
|
||||
android:textColor="#000"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/prefMsgNormLedColor_value"
|
||||
app:layout_constraintHorizontal_bias="0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:src="@drawable/circle"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:id="@+id/prefMsgNormLedColor_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:spinnerMode="dialog"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:background="#c0c0c0"/>
|
||||
|
||||
<Switch
|
||||
android:id="@+id/prefMsgNormEnableVibrations"
|
||||
android:text="@string/str_enable_vibration"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:minHeight="48dp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="12dp">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:background="@color/colorHeader"
|
||||
android:textColor="@color/colorHeaderForeground"
|
||||
android:textSize="16sp"
|
||||
android:padding="4dp"
|
||||
android:text="@string/str_header_prio2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<Switch
|
||||
android:id="@+id/prefMsgHighEnableSound"
|
||||
android:text="@string/str_msg_enablesound"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:minHeight="48dp" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:background="#c0c0c0"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/prefMsgHighRingtone_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:minHeight="48dp"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvMsgHighRingtone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/str_notificationsound"
|
||||
android:textColor="#000" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/prefMsgHighRingtone_value"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minWidth="64dp"
|
||||
android:spinnerMode="dialog"
|
||||
android:text="Whatever"
|
||||
tools:ignore="HardcodedText" />
|
||||
</LinearLayout>
|
||||
|
||||
<Switch
|
||||
android:id="@+id/prefMsgHighForceVolume"
|
||||
android:text="@string/str_forcevolume"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:minHeight="48dp" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:columnCount="3"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="4dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/icnHighVolume"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@string/volume_icon"
|
||||
android:src="@drawable/ic_volume"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/prefMsgHighVolume"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/btnHighVolumeTest"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toEndOf="@+id/icnHighVolume"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/btnHighVolumeTest"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_play"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:contentDescription="@string/play_test_sound" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:background="#c0c0c0"/>
|
||||
|
||||
<Switch
|
||||
android:id="@+id/prefMsgHighRepeatSound"
|
||||
android:text="@string/str_repeatnotificationsound"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:minHeight="48dp" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:background="#c0c0c0"/>
|
||||
|
||||
<Switch
|
||||
android:id="@+id/prefMsgHighEnableLED"
|
||||
android:text="@string/str_enable_led"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:minHeight="48dp" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:background="#c0c0c0"/>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/prefMsgHighLedColor_container"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="48dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/str_ledcolor"
|
||||
android:textColor="#000"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/prefMsgHighLedColor_value"
|
||||
app:layout_constraintHorizontal_bias="0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:src="@drawable/circle"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:id="@+id/prefMsgHighLedColor_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:spinnerMode="dialog"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:background="#c0c0c0"/>
|
||||
|
||||
<Switch
|
||||
android:id="@+id/prefMsgHighEnableVibrations"
|
||||
android:text="@string/str_enable_vibration"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:minHeight="48dp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
@@ -1,10 +1,44 @@
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:card_view="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<android.support.v7.widget.CardView
|
||||
<RelativeLayout
|
||||
android:id="@+id/layoutBack"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="@dimen/card_margin"
|
||||
android:background="@color/bg_row_background">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/delete_icon"
|
||||
android:layout_width="@dimen/ic_delete"
|
||||
android:layout_height="@dimen/ic_delete"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginRight="@dimen/padd_10"
|
||||
android:src="@drawable/ic_trash"
|
||||
android:contentDescription="@string/delete" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginRight="@dimen/padd_10"
|
||||
android:layout_toLeftOf="@id/delete_icon"
|
||||
android:text="@string/delete"
|
||||
android:textColor="#fff"
|
||||
android:textSize="13dp" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/layoutFront"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/card_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -14,7 +48,7 @@
|
||||
card_view:cardCornerRadius="@dimen/card_album_radius">
|
||||
|
||||
|
||||
<android.support.constraint.ConstraintLayout
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:background="#FFFFFFFF"
|
||||
android:layout_margin="3dp"
|
||||
android:layout_width="match_parent"
|
||||
@@ -55,14 +89,15 @@
|
||||
app:layout_constraintRight_toLeftOf="@+id/ivPriority"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
android:layout_margin="4sp"
|
||||
android:ellipsize="none"
|
||||
android:maxLines="32"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="6"
|
||||
android:scrollHorizontally="false"
|
||||
|
||||
android:text="asdasd asdasd asdasd a" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivPriority"
|
||||
android:tint="#BBB"
|
||||
android:visibility="gone"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
@@ -73,8 +108,10 @@
|
||||
android:paddingTop="3dp"
|
||||
android:contentDescription="@string/desc_priority_icon" />
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</android.support.v7.widget.CardView>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
</LinearLayout>
|
||||
</RelativeLayout>
|
||||
|
||||
</FrameLayout>
|
BIN
android/app/src/main/res/mipmap-hdpi/ic_notification_full.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
android/app/src/main/res/mipmap-mdpi/ic_notification_full.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
android/app/src/main/res/mipmap-xhdpi/ic_notification_full.png
Normal file
After Width: | Height: | Size: 6.5 KiB |
BIN
android/app/src/main/res/mipmap-xxhdpi/ic_notification_full.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
android/app/src/main/res/mipmap-xxxhdpi/ic_notification_full.png
Normal file
After Width: | Height: | Size: 17 KiB |
@@ -3,6 +3,10 @@
|
||||
<color name="colorPrimary">#3F51B5</color>
|
||||
<color name="colorPrimaryDark">#303F9F</color>
|
||||
<color name="colorAccent">#FF4081</color>
|
||||
<color name="colorHeader">#3F51B5</color>
|
||||
<color name="colorHeaderForeground">#FFFFFF</color>
|
||||
|
||||
<color name="colorBlack">#000</color>
|
||||
|
||||
<color name="bg_row_background">#fa315b</color>
|
||||
</resources>
|
||||
|
@@ -2,4 +2,8 @@
|
||||
<resources>
|
||||
<dimen name="card_margin">5dp</dimen>
|
||||
<dimen name="card_album_radius">0dp</dimen>
|
||||
|
||||
<dimen name="padd_10">10dp</dimen>
|
||||
<dimen name="ic_delete">30dp</dimen>
|
||||
<dimen name="thumbnail">90dp</dimen>
|
||||
</resources>
|
@@ -11,8 +11,28 @@
|
||||
<string name="ic_img_fuel_desc">Icon Fuel</string>
|
||||
<string name="str_qr_code">QR Code</string>
|
||||
<string name="str_reset_account">Reset Account</string>
|
||||
<string name="no_notifications">No notifications</string>
|
||||
<string name="no_notifications">No messages</string>
|
||||
<string name="str_not_connected">not connected</string>
|
||||
<string name="str_reload">reload</string>
|
||||
<string name="desc_priority_icon">Priority icon</string>
|
||||
<string name="str_common_settings">Common Settings</string>
|
||||
<string name="str_enabled">Enabled</string>
|
||||
<string name="str_localcachesize">Remember the last x messages locally</string>
|
||||
<string name="str_header_prio0">Notifications (priority 0 - Low)</string>
|
||||
<string name="str_header_prio1">Notifications (priority 1 - Normal)</string>
|
||||
<string name="str_header_prio2">Notifications (priority 2 - High)</string>
|
||||
<string name="str_msg_enablesound">Enable notification sound</string>
|
||||
<string name="str_notificationsound">Notification sound</string>
|
||||
<string name="str_repeatnotificationsound">Repeat notification sound</string>
|
||||
<string name="str_forcevolume">Automatically set the volume</string>
|
||||
<string name="str_enable_led">Enable notification light</string>
|
||||
<string name="str_ledcolor">Notification light color</string>
|
||||
<string name="str_enable_vibration">Enable notification vibration</string>
|
||||
<string name="str_upgrade_account">Upgrade account</string>
|
||||
<string name="str_deleteswipe">Delete messages by swiping left</string>
|
||||
<string name="str_promode">Thank you for supporting the app and using the pro mode</string>
|
||||
<string name="str_promode_info">Increase your daily quota, remove the ad banner and support the developer (that\'s me)</string>
|
||||
<string name="volume_icon">Volume icon</string>
|
||||
<string name="play_test_sound">Play test sound</string>
|
||||
<string name="delete">DELETE</string>
|
||||
</resources>
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="@style/PreferenceFixTheme.Light.NoActionBar">
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
<!-- your app branding color for the app bar -->
|
||||
<item name="colorPrimary">#3F51B5</item>
|
||||
<!-- darker variant for the status bar and contextual app bars -->
|
||||
|
@@ -1,33 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<EditTextPreference
|
||||
android:key="message_count"
|
||||
android:title="Keep x notifications"
|
||||
android:summary="Remember tha last x notifications locally"
|
||||
android:defaultValue="200" />
|
||||
|
||||
<PreferenceCategory
|
||||
android:title="Notifications"
|
||||
android:key="pref_key_notifications">
|
||||
|
||||
<CheckBoxPreference
|
||||
android:key="notification_enable_sound"
|
||||
android:title="Notification sound"
|
||||
android:summary="Play a sound when a notification is recieved"
|
||||
android:defaultValue="false" />
|
||||
|
||||
<CheckBoxPreference
|
||||
android:key="notification_enable_light"
|
||||
android:title="Notification light"
|
||||
android:summary="Turn the notification LED on when a notification is recieved"
|
||||
android:defaultValue="true" />
|
||||
|
||||
<CheckBoxPreference
|
||||
android:key="notification_enable_vibrate"
|
||||
android:title="Notification vibration"
|
||||
android:summary="Vibrate when a notification is recieved"
|
||||
android:defaultValue="false" />
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
</PreferenceScreen>
|
@@ -1,3 +1,3 @@
|
||||
#Sat Oct 20 02:47:36 CEST 2018
|
||||
VERSION_NAME=0.0.2
|
||||
VERSION_CODE=2
|
||||
#Mon Nov 19 18:43:09 CET 2018
|
||||
VERSION_NAME=1.1.0
|
||||
VERSION_CODE=15
|
||||
|
@@ -8,9 +8,7 @@ buildscript {
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.2.1'
|
||||
classpath 'com.google.gms:google-services:4.0.1'
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
classpath 'com.google.gms:google-services:4.2.0'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +18,9 @@ allprojects {
|
||||
jcenter()
|
||||
maven { url "https://jitpack.io" }
|
||||
maven { url "https://dl.bintray.com/gericop/maven" }
|
||||
maven { url "https://maven.google.com" }
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -11,3 +11,6 @@ org.gradle.jvmargs=-Xmx1536m
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
|
BIN
data/README/badge_amazon.png
Normal file
After Width: | Height: | Size: 3.8 KiB |
BIN
data/README/badge_apple.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
data/README/badge_apple_2.png
Normal file
After Width: | Height: | Size: 729 B |
BIN
data/README/badge_google.png
Normal file
After Width: | Height: | Size: 8.0 KiB |
BIN
data/README/badge_google_2.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
data/README/badge_microsoft.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
data/graphic.png
Before Width: | Height: | Size: 107 KiB |
BIN
data/icon.pdn
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 114 KiB |
BIN
data/icon_512_nobox.png
Normal file
After Width: | Height: | Size: 237 KiB |
BIN
data/icon_512_transparent.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
data/icon_web.pdn
Normal file
BIN
data/phone.pdn
Normal file
BIN
data/phone.png
Normal file
After Width: | Height: | Size: 1.0 MiB |
77
examples/scn_send.sh
Normal file
@@ -0,0 +1,77 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
#
|
||||
# Call with `scn_send title`
|
||||
# or `scn_send title content`
|
||||
# or `scn_send title content priority`
|
||||
#
|
||||
#
|
||||
|
||||
if [ "$#" -lt 1 ]; then
|
||||
echo "no title supplied via parameter"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
################################################################################
|
||||
# INSERT YOUR DATA HERE #
|
||||
################################################################################
|
||||
user_id=999
|
||||
user_key="????????????????????????????????????????????????????????????????"
|
||||
################################################################################
|
||||
|
||||
title=$1
|
||||
content=""
|
||||
|
||||
if [ "$#" -gt 1 ]; then
|
||||
content=$2
|
||||
fi
|
||||
|
||||
priority=1
|
||||
|
||||
if [ "$#" -gt 2 ]; then
|
||||
priority=$3
|
||||
fi
|
||||
|
||||
usr_msg_id=$(uuidgen)
|
||||
|
||||
while true ; do
|
||||
|
||||
curlresp=$(curl -s -o /dev/null -w "%{http_code}" \
|
||||
-d "user_id=$user_id" -d "user_key=$user_key" -d "title=$title" \
|
||||
-d "content=$content" -d "priority=$priority" -d "msg_id=$usr_msg_id" \
|
||||
https://scn.blackforestbytes.com/send.php)
|
||||
|
||||
if [ "$curlresp" == 200 ] ; then
|
||||
echo "Successfully send"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$curlresp" == 400 ] ; then
|
||||
echo "Bad request - something went wrong"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$curlresp" == 401 ] ; then
|
||||
echo "Unauthorized - wrong userid/userkey"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$curlresp" == 403 ] ; then
|
||||
echo "Quota exceeded - wait one hour before re-try"
|
||||
sleep 3600
|
||||
fi
|
||||
|
||||
if [ "$curlresp" == 412 ] ; then
|
||||
echo "Precondition Failed - No device linked"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$curlresp" == 500 ] ; then
|
||||
echo "Internal server error - waiting for better times"
|
||||
sleep 60
|
||||
fi
|
||||
|
||||
# if none of the above matched we probably hav no network ...
|
||||
echo "Send failed (response code $curlresp) ... try again in 5s"
|
||||
sleep 5
|
||||
done
|
@@ -1,13 +1,13 @@
|
||||
SimpleCloudNotifier is a app to display messages that you can send to your phone with a simple POST request to the right URL.
|
||||
|
||||
After you start the app it generates a userID and a private key.
|
||||
After you start the app it generates a UserID and a UserSecret.
|
||||
Now you can send your message to https://simplecloudnotifier.blackforestbytes.com/send.php and a notification will be pushed to your phone.
|
||||
(see https://simplecloudnotifier.blackforestbytes.com/ for an example with curl)
|
||||
|
||||
|
||||
Use it to
|
||||
- send yourself automated messages from cron jobs
|
||||
- notify youreself when long-running scripts finish
|
||||
- notify yourself when long-running scripts finish
|
||||
- send server error messages directly to your phone
|
||||
- integrate with other online services
|
||||
|
||||
|
BIN
store/screenshot_1.png
Normal file
After Width: | Height: | Size: 85 KiB |
BIN
store/screenshot_2.png
Normal file
After Width: | Height: | Size: 46 KiB |
BIN
store/screenshot_3.png
Normal file
After Width: | Height: | Size: 55 KiB |
1
web/.gitignore
vendored
@@ -181,3 +181,4 @@ $RECYCLE.BIN/
|
||||
#################
|
||||
|
||||
config.php
|
||||
.verify_accesstoken
|
57
web/api.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<?php
|
||||
if (file_exists('/var/www/openwebanalytics/owa_php.php'))
|
||||
{
|
||||
require_once('/var/www/openwebanalytics/owa_php.php');
|
||||
$owa = new owa_php();
|
||||
$owa->setSiteId('6386b0efc00d2e84ef642525345e1207');
|
||||
$owa->setPageTitle('API (Short)');
|
||||
$owa->trackPageView();
|
||||
}
|
||||
?>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" href="/css/mini-default.min.css"> <!-- https://minicss.org/docs -->
|
||||
<title>Simple Cloud Notifications - API</title>
|
||||
<!--<link rel="stylesheet" href="/css/mini-nord.min.css">-->
|
||||
<!--<link rel="stylesheet" href="/css/mini-dark.min.css">-->
|
||||
<link rel="stylesheet" href="/css/style.css">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" type="image/png" href="/favicon.png"/>
|
||||
<link rel="icon" type="image/png" href="/favicon.ico"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="copyinfo">
|
||||
<a tabindex="-1" href="https://www.blackforestbytes.com">© blackforestbytes</a>
|
||||
<a tabindex="-1" href="https://www.mikescher.com">made by Mike Schwörer</a>
|
||||
</div>
|
||||
|
||||
<div id="mainpnl">
|
||||
<a tabindex="-1" href="https://play.google.com/store/apps/details?id=com.blackforestbytes.simplecloudnotifier" class="button bordered" id="tl_link"><span class="icn-google-play"></span></a>
|
||||
<a tabindex="-1" href="/index.php" class="button bordered" id="tr_link">Send</a>
|
||||
|
||||
<a tabindex="-1" href="/" class="linkcaption"><h1>Simple Cloud Notifier</h1></a>
|
||||
|
||||
<p>Get your user-id and user-key from the app and send notifications to your phone by performing a POST request against <code>https://simplecloudnotifier.blackforestbytes.com/send.php</code></p>
|
||||
<pre>curl \
|
||||
--data "user_id={userid}" \
|
||||
--data "user_key={userkey}" \
|
||||
--data "title={message_title}" \
|
||||
--data "content={message_body}" \
|
||||
--data "priority={0|1|2}" \
|
||||
--data "msg_id={unique_message_id}" \
|
||||
https://scn.blackforestbytes.com/send.php</pre>
|
||||
<p>The <code>content</code>, <code>priority</code> and <code>msg_id</code> parameters are optional, you can also send message with only a title and the default priority</p>
|
||||
<pre>curl \
|
||||
--data "user_id={userid}" \
|
||||
--data "user_key={userkey}" \
|
||||
--data "title={message_title}" \
|
||||
https://scn.blackforestbytes.com/send.php</pre>
|
||||
|
||||
<a href="/api_more.php" class="button bordered tertiary" style="float: right; min-width: 100px; text-align: center">More</a>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
2
web/api/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
config.php
|
||||
.verify_accesstoken
|
56
web/api/__config_example.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
// insert your values here and rename to config.php
|
||||
|
||||
return
|
||||
[
|
||||
'global' =>
|
||||
[
|
||||
'prod' => true,
|
||||
],
|
||||
|
||||
'database' =>
|
||||
[
|
||||
'host' => '?',
|
||||
'database' => '?',
|
||||
'user' => '?',
|
||||
'password' => '?',
|
||||
],
|
||||
|
||||
'firebase' =>
|
||||
[
|
||||
'type' => 'service_account',
|
||||
'project_id' => '?',
|
||||
'private_key_id' => '???',
|
||||
'client_email' => '???.iam.gserviceaccount.com',
|
||||
'client_id' => '???',
|
||||
'auth_uri' => 'https://accounts.google.com/o/oauth2/auth',
|
||||
'token_uri' => 'https://oauth2.googleapis.com/token',
|
||||
'auth_provider_x509_cert_url' => 'https://www.googleapis.com/oauth2/v1/certs',
|
||||
'client_x509_cert_url' => 'https://www.googleapis.com/robot/v1/metadata/x509/???f.iam.gserviceaccount.com',
|
||||
'private_key' => "-----BEGIN PRIVATE KEY-----\n"
|
||||
. "??????????\n"
|
||||
. "-----END PRIVATE KEY-----\n",
|
||||
'server_key' => '????',
|
||||
],
|
||||
|
||||
'verify_api' =>
|
||||
[
|
||||
'package_name' => 'com.blackforestbytes.simplecloudnotifier',
|
||||
'product_id' => '???',
|
||||
|
||||
'clientid' => '???.apps.googleusercontent.com',
|
||||
'clientsecret' => '???',
|
||||
'accesstoken' => file_exists('.verify_accesstoken') ? file_get_contents('.verify_accesstoken') : '',
|
||||
'refreshtoken' => '???',
|
||||
'scope' => 'https://www.googleapis.com/auth/androidpublisher',
|
||||
],
|
||||
|
||||
'error_reporting' =>
|
||||
[
|
||||
'send-mail' => true,
|
||||
'email-error-target' => '???@???.com',
|
||||
'email-error-sender' => '???@???.com',
|
||||
],
|
||||
|
||||
];
|