Compare commits
62 Commits
Author | SHA1 | Date | |
---|---|---|---|
885d997ff3
|
|||
5118ab3cbf
|
|||
![]() |
2812377f5c | ||
93b40a9c7f
|
|||
b95ddcc811
|
|||
90ba3c1134
|
|||
05174958b2
|
|||
![]() |
24be9b2013 | ||
29ce4b727c
|
|||
![]() |
f178019ffe | ||
0a1b948042
|
|||
![]() |
74cbfb235e | ||
63c4104a89
|
|||
2597287b1e
|
|||
![]() |
316156f0f0 | ||
5286e869cc
|
|||
d6dcf28d89
|
|||
1d983b9ac0
|
|||
e525221010
|
|||
4cde4703f2
|
|||
4a07e58c16
|
|||
b12356575a
|
|||
77f571de7d
|
|||
![]() |
f5eef7563b | ||
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 |
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
|
||||||
|
|
||||||
|
|
||||||
|
  
|
32
android/.idea/assetWizardSettings.xml
generated
@@ -1,32 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="WizardSettings">
|
|
||||||
<option name="children">
|
|
||||||
<map>
|
|
||||||
<entry key="vectorWizard">
|
|
||||||
<value>
|
|
||||||
<PersistentState>
|
|
||||||
<option name="children">
|
|
||||||
<map>
|
|
||||||
<entry key="vectorAssetStep">
|
|
||||||
<value>
|
|
||||||
<PersistentState>
|
|
||||||
<option name="values">
|
|
||||||
<map>
|
|
||||||
<entry key="assetSourceType" value="FILE" />
|
|
||||||
<entry key="outputName" value="ic_garbage" />
|
|
||||||
<entry key="sourceFile" value="C:\Users\Mike\Downloads\garbage.svg" />
|
|
||||||
</map>
|
|
||||||
</option>
|
|
||||||
</PersistentState>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
</map>
|
|
||||||
</option>
|
|
||||||
</PersistentState>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
</map>
|
|
||||||
</option>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
134
android/.idea/codeStyles/Project.xml
generated
@@ -1,29 +1,113 @@
|
|||||||
<component name="ProjectCodeStyleConfiguration">
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
<code_scheme name="Project" version="173">
|
<code_scheme name="Project" version="173">
|
||||||
<Objective-C-extensions>
|
<codeStyleSettings language="XML">
|
||||||
<file>
|
<arrangement>
|
||||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
|
<rules>
|
||||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
|
<section>
|
||||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
|
<rule>
|
||||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
|
<match>
|
||||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
|
<AND>
|
||||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
|
<NAME>xmlns:android</NAME>
|
||||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
|
<XML_ATTRIBUTE />
|
||||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
|
</AND>
|
||||||
</file>
|
</match>
|
||||||
<class>
|
</rule>
|
||||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
|
</section>
|
||||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
|
<section>
|
||||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
|
<rule>
|
||||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
|
<match>
|
||||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
|
<AND>
|
||||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
|
<NAME>xmlns:.*</NAME>
|
||||||
</class>
|
<XML_ATTRIBUTE />
|
||||||
<extensions>
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
<pair source="cpp" header="h" fileNamingConvention="NONE" />
|
</AND>
|
||||||
<pair source="c" header="h" fileNamingConvention="NONE" />
|
</match>
|
||||||
</extensions>
|
<order>BY_NAME</order>
|
||||||
</Objective-C-extensions>
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*:id</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*:name</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>name</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>style</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
<order>BY_NAME</order>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
<order>ANDROID_ATTRIBUTE_ORDER</order>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*</NAME>
|
||||||
|
<XML_ATTRIBUTE />
|
||||||
|
<XML_NAMESPACE>.*</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
<order>BY_NAME</order>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
</rules>
|
||||||
|
</arrangement>
|
||||||
|
</codeStyleSettings>
|
||||||
</code_scheme>
|
</code_scheme>
|
||||||
</component>
|
</component>
|
@@ -43,15 +43,17 @@ dependencies {
|
|||||||
implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'
|
implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'
|
||||||
|
|
||||||
implementation 'com.google.android.material:material:1.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-core:16.0.6'
|
||||||
implementation 'com.google.firebase:firebase-messaging:17.3.4'
|
implementation 'com.google.firebase:firebase-messaging:17.3.4'
|
||||||
implementation 'com.google.android.gms:play-services-ads:17.1.0'
|
implementation 'com.google.android.gms:play-services-ads:17.1.2'
|
||||||
implementation 'com.android.billingclient:billing:1.2'
|
implementation 'com.android.billingclient:billing:1.2'
|
||||||
|
|
||||||
implementation 'com.squareup.okhttp3:okhttp:3.10.0'
|
implementation 'com.squareup.okhttp3:okhttp:3.10.0'
|
||||||
implementation 'com.github.kenglxn.QRGen:android:2.5.0'
|
implementation 'com.github.kenglxn.QRGen:android:2.5.0'
|
||||||
implementation "com.github.DeweyReed:UltimateMusicPicker:2.0.0"
|
implementation "com.github.DeweyReed:UltimateMusicPicker:2.0.0"
|
||||||
implementation 'com.github.duanhong169:colorpicker:1.1.5'
|
implementation 'com.github.duanhong169:colorpicker:1.1.5'
|
||||||
|
|
||||||
|
implementation 'net.danlew:android.joda:2.9.9.2'
|
||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: 'com.google.gms.google-services'
|
apply plugin: 'com.google.gms.google-services'
|
||||||
|
@@ -1,43 +1,68 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
package="com.blackforestbytes.simplecloudnotifier">
|
package="com.blackforestbytes.simplecloudnotifier">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.VIBRATE" />
|
<uses-permission android:name="android.permission.VIBRATE" />
|
||||||
<uses-permission android:name="com.android.vending.BILLING" />
|
<uses-permission android:name="com.android.vending.BILLING" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
|
android:name=".SCNApp"
|
||||||
android:allowBackup="false"
|
android:allowBackup="false"
|
||||||
android:name="SCNApp"
|
|
||||||
android:icon="@drawable/icon"
|
android:icon="@drawable/icon"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/AppTheme"
|
android:theme="@style/AppTheme"
|
||||||
tools:ignore="GoogleAppIndexingWarning">
|
tools:ignore="GoogleAppIndexingWarning">
|
||||||
|
|
||||||
<activity android:name=".view.MainActivity">
|
<activity android:name=".view.MainActivity">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<meta-data android:name="com.google.firebase.messaging.default_notification_icon" android:resource="@drawable/icon" />
|
<provider
|
||||||
<meta-data android:name="com.google.firebase.messaging.default_notification_color" android:resource="@color/colorAccent" />
|
android:name="androidx.core.content.FileProvider"
|
||||||
<meta-data android:name="com.google.android.gms.ads.AD_MANAGER_APP" android:value="true"/>
|
android:authorities="com.blackforestbytes.simplecloudnotifier.fileprovider"
|
||||||
<meta-data android:name="com.google.android.gms.ads.APPLICATION_ID" android:value="ca-app-pub-3320562328966175~7579972005"/>
|
android:grantUriPermissions="true"
|
||||||
|
android:exported="false">
|
||||||
|
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/filepaths" />
|
||||||
|
</provider>
|
||||||
|
|
||||||
<service android:name=".service.FBMService" android:exported="false">
|
|
||||||
|
<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"
|
||||||
|
android:exported="false">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="com.google.firebase.MESSAGING_EVENT" />
|
<action android:name="com.google.firebase.MESSAGING_EVENT" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<receiver android:name=".service.BroadcastReceiverService" android:exported="false" />
|
<receiver
|
||||||
|
android:name=".service.BroadcastReceiverService"
|
||||||
|
android:exported="false" />
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".view.debug.QueryLogActivity"
|
||||||
|
android:label="@string/title_activity_query_log"
|
||||||
|
android:theme="@style/AppTheme" />
|
||||||
|
<activity android:name=".view.debug.SingleQueryLogActivity" />
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</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 |
@@ -5,6 +5,7 @@ import android.content.Context;
|
|||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.android.billingclient.api.BillingClient;
|
import com.android.billingclient.api.BillingClient;
|
||||||
|
import com.blackforestbytes.simplecloudnotifier.model.QueryLog;
|
||||||
import com.blackforestbytes.simplecloudnotifier.view.AccountFragment;
|
import com.blackforestbytes.simplecloudnotifier.view.AccountFragment;
|
||||||
import com.blackforestbytes.simplecloudnotifier.view.MainActivity;
|
import com.blackforestbytes.simplecloudnotifier.view.MainActivity;
|
||||||
import com.blackforestbytes.simplecloudnotifier.view.TabAdapter;
|
import com.blackforestbytes.simplecloudnotifier.view.TabAdapter;
|
||||||
@@ -99,14 +100,5 @@ public class SCNApp extends Application implements LifecycleObserver
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
//TODO: Config for collapsed line count
|
||||||
==TODO==
|
//TODO: Sometimes ads but promode
|
||||||
|
|
||||||
[ ] - test notification channels
|
|
||||||
[ ] - startup time
|
|
||||||
[ ] - periodically get non-ack (option - even when not in-app)
|
|
||||||
|
|
||||||
[ ] - publish (+ HN post ?)
|
|
||||||
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
@@ -24,6 +24,11 @@ public final class CollectionHelper
|
|||||||
return output;
|
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)
|
public static <T, U extends Comparable<U>> List<T> sort(List<T> input, Func1to1<T, U> mapper)
|
||||||
{
|
{
|
||||||
return sort(input, mapper, 1);
|
return sort(input, mapper, 1);
|
||||||
|
@@ -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,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);
|
||||||
|
}
|
@@ -9,6 +9,8 @@ import java.util.TimeZone;
|
|||||||
|
|
||||||
public class CMessage
|
public class CMessage
|
||||||
{
|
{
|
||||||
|
public boolean IsExpandedInAdapter = false;
|
||||||
|
|
||||||
public final long SCN_ID;
|
public final long SCN_ID;
|
||||||
public final long Timestamp;
|
public final long Timestamp;
|
||||||
public final String Title;
|
public final String Title;
|
||||||
|
@@ -3,17 +3,21 @@ package com.blackforestbytes.simplecloudnotifier.model;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
|
||||||
|
import com.blackforestbytes.simplecloudnotifier.lib.collections.CollectionHelper;
|
||||||
import com.blackforestbytes.simplecloudnotifier.lib.string.Str;
|
import com.blackforestbytes.simplecloudnotifier.lib.string.Str;
|
||||||
import com.blackforestbytes.simplecloudnotifier.view.MessageAdapter;
|
import com.blackforestbytes.simplecloudnotifier.view.MessageAdapter;
|
||||||
import com.blackforestbytes.simplecloudnotifier.SCNApp;
|
import com.blackforestbytes.simplecloudnotifier.SCNApp;
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
public class CMessageList
|
public class CMessageList
|
||||||
{
|
{
|
||||||
|
private final Object msg_lock = new Object();
|
||||||
|
|
||||||
public ArrayList<CMessage> Messages;
|
public ArrayList<CMessage> Messages;
|
||||||
public Set<String> AllAcks;
|
public Set<String> AllAcks;
|
||||||
|
|
||||||
@@ -32,23 +36,31 @@ public class CMessageList
|
|||||||
|
|
||||||
private CMessageList()
|
private CMessageList()
|
||||||
{
|
{
|
||||||
Messages = new ArrayList<>();
|
reloadPrefs();
|
||||||
AllAcks = new HashSet<>();
|
}
|
||||||
|
|
||||||
SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("CMessageList", Context.MODE_PRIVATE);
|
public void reloadPrefs()
|
||||||
int count = sharedPref.getInt("message_count", 0);
|
{
|
||||||
for (int i=0; i < count; i++)
|
synchronized (msg_lock)
|
||||||
{
|
{
|
||||||
long time = sharedPref.getLong("message["+i+"].timestamp", 0);
|
Messages = new ArrayList<>();
|
||||||
String title = sharedPref.getString("message["+i+"].title", "");
|
AllAcks = new HashSet<>();
|
||||||
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(scnid, time, title, content, prio));
|
SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("CMessageList", Context.MODE_PRIVATE);
|
||||||
|
int count = sharedPref.getInt("message_count", 0);
|
||||||
|
for (int i=0; i < count; i++)
|
||||||
|
{
|
||||||
|
long time = sharedPref.getLong("message["+i+"].timestamp", 0);
|
||||||
|
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(scnid, time, title, content, prio));
|
||||||
|
}
|
||||||
|
|
||||||
|
AllAcks = sharedPref.getStringSet("acks", new HashSet<>());
|
||||||
}
|
}
|
||||||
|
|
||||||
AllAcks = sharedPref.getStringSet("acks", new HashSet<>());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public CMessage add(final long scnid, 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)
|
||||||
@@ -60,29 +72,44 @@ public class CMessageList
|
|||||||
SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("CMessageList", Context.MODE_PRIVATE);
|
SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("CMessageList", Context.MODE_PRIVATE);
|
||||||
int count = sharedPref.getInt("message_count", 0);
|
int count = sharedPref.getInt("message_count", 0);
|
||||||
|
|
||||||
SharedPreferences.Editor e = sharedPref.edit();
|
synchronized (msg_lock)
|
||||||
|
{
|
||||||
|
Messages.add(msg);
|
||||||
|
AllAcks.add(Long.toHexString(msg.SCN_ID));
|
||||||
|
|
||||||
Messages.add(msg);
|
while (Messages.size()>SCNSettings.inst().LocalCacheSize) Messages.remove(0);
|
||||||
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
|
||||||
|
|
||||||
e.putInt( "message_count", count+1);
|
SharedPreferences.Editor e = sharedPref.edit();
|
||||||
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.putLong( "message["+count+"].scnid", scnid);
|
|
||||||
|
|
||||||
e.putStringSet("acks", AllAcks);
|
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.putLong( "message["+count+"].scnid", scnid);
|
||||||
|
|
||||||
|
e.putStringSet("acks", AllAcks);
|
||||||
|
|
||||||
|
e.apply();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// full save
|
||||||
|
|
||||||
|
fullSave(); // does sort in here
|
||||||
|
}
|
||||||
|
|
||||||
e.apply();
|
|
||||||
|
|
||||||
for (WeakReference<MessageAdapter> ref : _listener)
|
for (WeakReference<MessageAdapter> ref : _listener)
|
||||||
{
|
{
|
||||||
MessageAdapter a = ref.get();
|
MessageAdapter a = ref.get();
|
||||||
if (a == null) continue;
|
if (a == null) continue;
|
||||||
a.customNotifyItemInserted(count);
|
a.customNotifyDataSetChanged();
|
||||||
a.scrollToTop();
|
a.scrollToTop();
|
||||||
}
|
}
|
||||||
CleanUpListener();
|
CleanUpListener();
|
||||||
@@ -90,9 +117,12 @@ public class CMessageList
|
|||||||
|
|
||||||
if (!run)
|
if (!run)
|
||||||
{
|
{
|
||||||
Messages.add(new CMessage(scnid, time, title, content, pe));
|
synchronized (msg_lock)
|
||||||
AllAcks.add(Long.toHexString(msg.SCN_ID));
|
{
|
||||||
fullSave();
|
Messages.add(new CMessage(scnid, time, title, content, pe));
|
||||||
|
AllAcks.add(Long.toHexString(msg.SCN_ID));
|
||||||
|
}
|
||||||
|
fullSave(); // does sort in here
|
||||||
}
|
}
|
||||||
|
|
||||||
return msg;
|
return msg;
|
||||||
@@ -114,31 +144,39 @@ public class CMessageList
|
|||||||
|
|
||||||
public void fullSave()
|
public void fullSave()
|
||||||
{
|
{
|
||||||
SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("CMessageList", Context.MODE_PRIVATE);
|
synchronized (msg_lock)
|
||||||
SharedPreferences.Editor e = sharedPref.edit();
|
|
||||||
|
|
||||||
e.clear();
|
|
||||||
|
|
||||||
e.putInt("message_count", Messages.size());
|
|
||||||
|
|
||||||
for (int i = 0; i < Messages.size(); i++)
|
|
||||||
{
|
{
|
||||||
e.putLong( "message["+i+"].timestamp", Messages.get(i).Timestamp);
|
CollectionHelper.sort_inplace(Messages, (a,b) -> Long.compare(a.Timestamp, b.Timestamp));
|
||||||
e.putString("message["+i+"].title", Messages.get(i).Title);
|
|
||||||
e.putString("message["+i+"].content", Messages.get(i).Content);
|
SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("CMessageList", Context.MODE_PRIVATE);
|
||||||
e.putInt( "message["+i+"].priority", Messages.get(i).Priority.ID);
|
SharedPreferences.Editor e = sharedPref.edit();
|
||||||
e.putLong( "message["+i+"].scnid", Messages.get(i).SCN_ID);
|
|
||||||
|
e.clear();
|
||||||
|
|
||||||
|
e.putInt("message_count", Messages.size());
|
||||||
|
|
||||||
|
for (int i = 0; i < Messages.size(); i++)
|
||||||
|
{
|
||||||
|
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.putLong( "message["+i+"].scnid", Messages.get(i).SCN_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
e.putStringSet("acks", AllAcks);
|
||||||
|
|
||||||
|
e.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
e.putStringSet("acks", AllAcks);
|
|
||||||
|
|
||||||
e.apply();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public CMessage tryGet(int pos)
|
public CMessage tryGet(int pos)
|
||||||
{
|
{
|
||||||
if (pos < 0 || pos >= Messages.size()) return null;
|
synchronized (msg_lock)
|
||||||
return Messages.get(pos);
|
{
|
||||||
|
if (pos < 0 || pos >= Messages.size()) return null;
|
||||||
|
return Messages.get(pos);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public CMessage tryGetFromBack(int pos)
|
public CMessage tryGetFromBack(int pos)
|
||||||
@@ -148,7 +186,10 @@ public class CMessageList
|
|||||||
|
|
||||||
public int size()
|
public int size()
|
||||||
{
|
{
|
||||||
return Messages.size();
|
synchronized (msg_lock)
|
||||||
|
{
|
||||||
|
return Messages.size();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void register(MessageAdapter adp)
|
public void register(MessageAdapter adp)
|
||||||
@@ -167,18 +208,30 @@ public class CMessageList
|
|||||||
|
|
||||||
public boolean isAck(long id)
|
public boolean isAck(long id)
|
||||||
{
|
{
|
||||||
return AllAcks.contains(Long.toHexString(id));
|
synchronized (msg_lock)
|
||||||
|
{
|
||||||
|
return AllAcks.contains(Long.toHexString(id));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void remove(int index)
|
public CMessage removeFromBack(int pos)
|
||||||
{
|
{
|
||||||
Messages.remove(index);
|
CMessage r;
|
||||||
fullSave();
|
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)
|
public void insert(int index, CMessage item)
|
||||||
{
|
{
|
||||||
Messages.add(index, item);
|
synchronized (msg_lock)
|
||||||
fullSave();
|
{
|
||||||
|
Messages.add(index, item);
|
||||||
|
}
|
||||||
|
fullSave(); // does sort in here
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,58 @@
|
|||||||
|
package com.blackforestbytes.simplecloudnotifier.model;
|
||||||
|
|
||||||
|
|
||||||
|
import android.graphics.Color;
|
||||||
|
|
||||||
|
public enum LogLevel
|
||||||
|
{
|
||||||
|
DEBUG,
|
||||||
|
INFO,
|
||||||
|
WARN,
|
||||||
|
ERROR;
|
||||||
|
|
||||||
|
public String toUIString()
|
||||||
|
{
|
||||||
|
switch (this)
|
||||||
|
{
|
||||||
|
case DEBUG: return "Debug";
|
||||||
|
case INFO: return "Info";
|
||||||
|
case WARN: return "Warning";
|
||||||
|
case ERROR: return "Error";
|
||||||
|
default: return "???";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getColor()
|
||||||
|
{
|
||||||
|
switch (this)
|
||||||
|
{
|
||||||
|
case DEBUG: return Color.GRAY;
|
||||||
|
case WARN: return Color.rgb(171, 145, 68);
|
||||||
|
case INFO: return Color.BLACK;
|
||||||
|
case ERROR: return Color.RED;
|
||||||
|
default: return Color.MAGENTA;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int asInt()
|
||||||
|
{
|
||||||
|
switch (this)
|
||||||
|
{
|
||||||
|
case DEBUG: return 0;
|
||||||
|
case WARN: return 1;
|
||||||
|
case INFO: return 2;
|
||||||
|
case ERROR: return 3;
|
||||||
|
default: return 999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LogLevel fromInt(int i)
|
||||||
|
{
|
||||||
|
if (i == 0) return LogLevel.DEBUG;
|
||||||
|
if (i == 1) return LogLevel.WARN;
|
||||||
|
if (i == 2) return LogLevel.INFO;
|
||||||
|
if (i == 3) return LogLevel.ERROR;
|
||||||
|
|
||||||
|
return LogLevel.ERROR; // ????
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,69 @@
|
|||||||
|
package com.blackforestbytes.simplecloudnotifier.model;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.blackforestbytes.simplecloudnotifier.SCNApp;
|
||||||
|
import com.blackforestbytes.simplecloudnotifier.lib.collections.CollectionHelper;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class QueryLog
|
||||||
|
{
|
||||||
|
private final static int MAX_HISTORY_SIZE = 192;
|
||||||
|
|
||||||
|
private static QueryLog _instance;
|
||||||
|
public static QueryLog inst() { if (_instance == null) synchronized (QueryLog.class) { if (_instance == null) _instance = new QueryLog(); } return _instance; }
|
||||||
|
|
||||||
|
private QueryLog(){ reloadPrefs(); }
|
||||||
|
|
||||||
|
private final List<SingleQuery> history = new ArrayList<>();
|
||||||
|
|
||||||
|
public synchronized void add(SingleQuery r)
|
||||||
|
{
|
||||||
|
history.add(r);
|
||||||
|
while (history.size() > MAX_HISTORY_SIZE) history.remove(0);
|
||||||
|
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized List<SingleQuery> get()
|
||||||
|
{
|
||||||
|
List<SingleQuery> r = new ArrayList<>(history);
|
||||||
|
CollectionHelper.sort_inplace(r, (o1, o2) -> (-1) * o1.Timestamp.compareTo(o2.Timestamp));
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void save()
|
||||||
|
{
|
||||||
|
SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("QueryLog", Context.MODE_PRIVATE);
|
||||||
|
SharedPreferences.Editor e = sharedPref.edit();
|
||||||
|
|
||||||
|
e.clear();
|
||||||
|
|
||||||
|
e.putInt("history_count", history.size());
|
||||||
|
|
||||||
|
for (int i = 0; i < history.size(); i++) history.get(i).save(e, "message["+(i+1000)+"]");
|
||||||
|
|
||||||
|
e.apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void reloadPrefs()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Context c = SCNApp.getContext();
|
||||||
|
SharedPreferences sharedPref = c.getSharedPreferences("QueryLog", Context.MODE_PRIVATE);
|
||||||
|
int count = sharedPref.getInt("history_count", 0);
|
||||||
|
for (int i=0; i < count; i++) history.add(SingleQuery.load(sharedPref, "message["+(i+1000)+"]"));
|
||||||
|
|
||||||
|
CollectionHelper.sort_inplace(history, (o1, o2) -> (-1) * o1.Timestamp.compareTo(o2.Timestamp));
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.e("SC:QL:Load", e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -6,8 +6,8 @@ import android.content.SharedPreferences;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import com.android.billingclient.api.Purchase;
|
|
||||||
import com.blackforestbytes.simplecloudnotifier.SCNApp;
|
import com.blackforestbytes.simplecloudnotifier.SCNApp;
|
||||||
|
import com.blackforestbytes.simplecloudnotifier.lib.datatypes.Tuple3;
|
||||||
import com.blackforestbytes.simplecloudnotifier.lib.string.Str;
|
import com.blackforestbytes.simplecloudnotifier.lib.string.Str;
|
||||||
import com.blackforestbytes.simplecloudnotifier.service.IABService;
|
import com.blackforestbytes.simplecloudnotifier.service.IABService;
|
||||||
import com.google.firebase.iid.FirebaseInstanceId;
|
import com.google.firebase.iid.FirebaseInstanceId;
|
||||||
@@ -15,14 +15,19 @@ import com.google.firebase.iid.FirebaseInstanceId;
|
|||||||
public class SCNSettings
|
public class SCNSettings
|
||||||
{
|
{
|
||||||
private final static Object _lock = new Object();
|
private final static Object _lock = new Object();
|
||||||
private static SCNSettings _inst = null;
|
private static volatile SCNSettings _inst = null;
|
||||||
public static SCNSettings inst()
|
public static SCNSettings inst()
|
||||||
{
|
{
|
||||||
synchronized (_lock)
|
SCNSettings local = _inst;
|
||||||
|
if (local == null)
|
||||||
{
|
{
|
||||||
if (_inst != null) return _inst;
|
synchronized (_lock)
|
||||||
return _inst = new SCNSettings();
|
{
|
||||||
|
local = _inst;
|
||||||
|
if (local == null) _inst = local = new SCNSettings();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return local;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------
|
// ------------------------------------------------------------
|
||||||
@@ -47,6 +52,8 @@ public class SCNSettings
|
|||||||
|
|
||||||
public boolean Enabled = true;
|
public boolean Enabled = true;
|
||||||
public int LocalCacheSize = 500;
|
public int LocalCacheSize = 500;
|
||||||
|
public boolean EnableDeleteSwipe = false;
|
||||||
|
public int PreviewLineCount = 6;
|
||||||
|
|
||||||
public final NotificationSettings PriorityLow = new NotificationSettings(PriorityEnum.LOW);
|
public final NotificationSettings PriorityLow = new NotificationSettings(PriorityEnum.LOW);
|
||||||
public final NotificationSettings PriorityNorm = new NotificationSettings(PriorityEnum.NORMAL);
|
public final NotificationSettings PriorityNorm = new NotificationSettings(PriorityEnum.NORMAL);
|
||||||
@@ -55,6 +62,11 @@ public class SCNSettings
|
|||||||
// ------------------------------------------------------------
|
// ------------------------------------------------------------
|
||||||
|
|
||||||
public SCNSettings()
|
public SCNSettings()
|
||||||
|
{
|
||||||
|
reloadPrefs();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reloadPrefs()
|
||||||
{
|
{
|
||||||
SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("Config", Context.MODE_PRIVATE);
|
SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("Config", Context.MODE_PRIVATE);
|
||||||
|
|
||||||
@@ -70,6 +82,8 @@ public class SCNSettings
|
|||||||
|
|
||||||
Enabled = sharedPref.getBoolean("app_enabled", Enabled);
|
Enabled = sharedPref.getBoolean("app_enabled", Enabled);
|
||||||
LocalCacheSize = sharedPref.getInt("local_cache_size", LocalCacheSize);
|
LocalCacheSize = sharedPref.getInt("local_cache_size", LocalCacheSize);
|
||||||
|
EnableDeleteSwipe = sharedPref.getBoolean("do_del_swipe", EnableDeleteSwipe);
|
||||||
|
PreviewLineCount = sharedPref.getInt("preview_line_count", PreviewLineCount);
|
||||||
|
|
||||||
PriorityLow.EnableLED = sharedPref.getBoolean("priority_low:enabled_led", PriorityLow.EnableLED);
|
PriorityLow.EnableLED = sharedPref.getBoolean("priority_low:enabled_led", PriorityLow.EnableLED);
|
||||||
PriorityLow.EnableSound = sharedPref.getBoolean("priority_low:enabled_sound", PriorityLow.EnableSound);
|
PriorityLow.EnableSound = sharedPref.getBoolean("priority_low:enabled_sound", PriorityLow.EnableSound);
|
||||||
@@ -113,9 +127,14 @@ public class SCNSettings
|
|||||||
e.putString( "user_key", user_key);
|
e.putString( "user_key", user_key);
|
||||||
e.putString( "fcm_token_local", fcm_token_local);
|
e.putString( "fcm_token_local", fcm_token_local);
|
||||||
e.putString( "fcm_token_server", fcm_token_server);
|
e.putString( "fcm_token_server", fcm_token_server);
|
||||||
|
e.putBoolean("promode_local", promode_local);
|
||||||
|
e.putBoolean("promode_server", promode_server);
|
||||||
|
e.putString( "promode_token", promode_token);
|
||||||
|
|
||||||
e.putBoolean("app_enabled", Enabled);
|
e.putBoolean("app_enabled", Enabled);
|
||||||
e.putInt( "local_cache_size", LocalCacheSize);
|
e.putInt( "local_cache_size", LocalCacheSize);
|
||||||
|
e.putBoolean("do_del_swipe", EnableDeleteSwipe);
|
||||||
|
e.putInt( "preview_line_count", PreviewLineCount);
|
||||||
|
|
||||||
e.putBoolean("priority_low:enabled_led", PriorityLow.EnableLED);
|
e.putBoolean("priority_low:enabled_led", PriorityLow.EnableLED);
|
||||||
e.putBoolean("priority_low:enabled_sound", PriorityLow.EnableSound);
|
e.putBoolean("priority_low:enabled_sound", PriorityLow.EnableSound);
|
||||||
@@ -155,10 +174,12 @@ public class SCNSettings
|
|||||||
return user_id>=0 && user_key != null && !user_key.isEmpty();
|
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";
|
String base = longurl ? ServerCommunication.PAGE_URL_LONG : ServerCommunication.PAGE_URL_SHORT;
|
||||||
return ServerCommunication.BASE_URL + "index.php?preset_user_id="+user_id+"&preset_user_key="+user_key;
|
|
||||||
|
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)
|
public void setServerToken(String token, View loader)
|
||||||
@@ -184,7 +205,7 @@ public class SCNSettings
|
|||||||
FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(a, instanceIdResult ->
|
FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(a, instanceIdResult ->
|
||||||
{
|
{
|
||||||
String newToken = instanceIdResult.getToken();
|
String newToken = instanceIdResult.getToken();
|
||||||
Log.e("FB::GetInstanceId", newToken);
|
Log.d("FB::GetInstanceId", newToken);
|
||||||
SCNSettings.inst().setServerToken(newToken, null);
|
SCNSettings.inst().setServerToken(newToken, null);
|
||||||
}).addOnCompleteListener(r ->
|
}).addOnCompleteListener(r ->
|
||||||
{
|
{
|
||||||
@@ -219,7 +240,7 @@ public class SCNSettings
|
|||||||
FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(a, instanceIdResult ->
|
FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(a, instanceIdResult ->
|
||||||
{
|
{
|
||||||
String newToken = instanceIdResult.getToken();
|
String newToken = instanceIdResult.getToken();
|
||||||
Log.e("FB::GetInstanceId", newToken);
|
Log.d("FB::GetInstanceId", newToken);
|
||||||
SCNSettings.inst().setServerToken(newToken, loader); // does register in here
|
SCNSettings.inst().setServerToken(newToken, loader); // does register in here
|
||||||
}).addOnCompleteListener(r ->
|
}).addOnCompleteListener(r ->
|
||||||
{
|
{
|
||||||
@@ -230,14 +251,17 @@ public class SCNSettings
|
|||||||
|
|
||||||
public void updateProState(View loader)
|
public void updateProState(View loader)
|
||||||
{
|
{
|
||||||
Purchase purch = IABService.inst().getPurchaseCached(IABService.IAB_PRO_MODE);
|
Tuple3<Boolean, Boolean, String> state = IABService.inst().getPurchaseCachedExtended(IABService.IAB_PRO_MODE);
|
||||||
boolean promode_real = (purch != null);
|
if (!state.Item2) return; // not initialized
|
||||||
|
|
||||||
|
boolean promode_real = state.Item1;
|
||||||
|
|
||||||
if (promode_real != promode_local || promode_real != promode_server)
|
if (promode_real != promode_local || promode_real != promode_server)
|
||||||
{
|
{
|
||||||
promode_local = promode_real;
|
promode_local = promode_real;
|
||||||
|
promode_token = promode_real ? state.Item3 : "";
|
||||||
|
save();
|
||||||
|
|
||||||
promode_token = promode_real ? purch.getPurchaseToken() : "";
|
|
||||||
updateProStateOnServer(loader);
|
updateProStateOnServer(loader);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -4,9 +4,11 @@ import android.util.Log;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import com.blackforestbytes.simplecloudnotifier.SCNApp;
|
import com.blackforestbytes.simplecloudnotifier.SCNApp;
|
||||||
|
import com.blackforestbytes.simplecloudnotifier.lib.lambda.Func5to0;
|
||||||
import com.blackforestbytes.simplecloudnotifier.lib.string.Str;
|
import com.blackforestbytes.simplecloudnotifier.lib.string.Str;
|
||||||
import com.blackforestbytes.simplecloudnotifier.service.FBMService;
|
import com.blackforestbytes.simplecloudnotifier.service.FBMService;
|
||||||
|
|
||||||
|
import org.joda.time.Instant;
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
@@ -24,7 +26,9 @@ import okhttp3.ResponseBody;
|
|||||||
|
|
||||||
public class ServerCommunication
|
public class ServerCommunication
|
||||||
{
|
{
|
||||||
public static final String BASE_URL = /*SCNApp.LOCAL_DEBUG ? "http://localhost:1010/" : */"https://scn.blackforestbytes.com/api/";
|
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 static final OkHttpClient client = new OkHttpClient();
|
||||||
|
|
||||||
@@ -43,21 +47,21 @@ public class ServerCommunication
|
|||||||
@Override
|
@Override
|
||||||
public void onFailure(Call call, IOException e)
|
public void onFailure(Call call, IOException e)
|
||||||
{
|
{
|
||||||
Log.e("SC:register", e.toString());
|
handleError("register", call, null, Str.Empty, true, e);
|
||||||
SCNApp.showToast("Communication with server failed", 4000);
|
|
||||||
SCNApp.runOnUiThread(() -> { if (loader!=null)loader.setVisibility(View.GONE); });
|
SCNApp.runOnUiThread(() -> { if (loader!=null)loader.setVisibility(View.GONE); });
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResponse(Call call, Response response)
|
public void onResponse(Call call, Response response)
|
||||||
{
|
{
|
||||||
|
String r = Str.Empty;
|
||||||
try (ResponseBody responseBody = response.body())
|
try (ResponseBody responseBody = response.body())
|
||||||
{
|
{
|
||||||
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
|
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
|
||||||
if (responseBody == null) throw new IOException("No response");
|
if (responseBody == null) throw new IOException("No response");
|
||||||
|
|
||||||
String r = responseBody.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();
|
JSONObject json = (JSONObject) new JSONTokener(r).nextValue();
|
||||||
|
|
||||||
@@ -76,11 +80,12 @@ public class ServerCommunication
|
|||||||
SCNSettings.inst().save();
|
SCNSettings.inst().save();
|
||||||
|
|
||||||
SCNApp.refreshAccountTab();
|
SCNApp.refreshAccountTab();
|
||||||
|
|
||||||
|
handleSuccess("register", call, response, r);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Log.e("SC:register", e.toString());
|
handleError("register", call, response, r, false, e);
|
||||||
SCNApp.showToast("Communication with server failed", 4000);
|
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -91,8 +96,7 @@ public class ServerCommunication
|
|||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Log.e("SC:register", e.toString());
|
handleError("register", null, null, Str.Empty, false, e);
|
||||||
SCNApp.showToast("Communication with server failed", 4000);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,7 +105,7 @@ public class ServerCommunication
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
Request request = new Request.Builder()
|
Request request = new Request.Builder()
|
||||||
.url(BASE_URL + "updateFCMToken.php?user_id="+id+"&user_key="+key+"&fcm_token="+token)
|
.url(BASE_URL + "update.php?user_id="+id+"&user_key="+key+"&fcm_token="+token)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
client.newCall(request).enqueue(new Callback()
|
client.newCall(request).enqueue(new Callback()
|
||||||
@@ -109,21 +113,21 @@ public class ServerCommunication
|
|||||||
@Override
|
@Override
|
||||||
public void onFailure(Call call, IOException e)
|
public void onFailure(Call call, IOException e)
|
||||||
{
|
{
|
||||||
Log.e("SC:update_1", e.toString());
|
handleError("update<1>", call, null, Str.Empty, true, e);
|
||||||
SCNApp.showToast("Communication with server failed", 4000);
|
|
||||||
SCNApp.runOnUiThread(() -> { if (loader!=null)loader.setVisibility(View.GONE); });
|
SCNApp.runOnUiThread(() -> { if (loader!=null)loader.setVisibility(View.GONE); });
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResponse(Call call, Response response)
|
public void onResponse(Call call, Response response)
|
||||||
{
|
{
|
||||||
|
String r = Str.Empty;
|
||||||
try (ResponseBody responseBody = response.body())
|
try (ResponseBody responseBody = response.body())
|
||||||
{
|
{
|
||||||
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
|
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
|
||||||
if (responseBody == null) throw new IOException("No response");
|
if (responseBody == null) throw new IOException("No response");
|
||||||
|
|
||||||
String r = responseBody.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();
|
JSONObject json = (JSONObject) new JSONTokener(r).nextValue();
|
||||||
|
|
||||||
@@ -142,10 +146,12 @@ public class ServerCommunication
|
|||||||
SCNSettings.inst().save();
|
SCNSettings.inst().save();
|
||||||
|
|
||||||
SCNApp.refreshAccountTab();
|
SCNApp.refreshAccountTab();
|
||||||
|
|
||||||
|
handleSuccess("update<1>", call, response, r);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Log.e("SC:update_1", e.toString());
|
handleError("update<1>", call, response, r, false, e);
|
||||||
SCNApp.showToast("Communication with server failed", 4000);
|
SCNApp.showToast("Communication with server failed", 4000);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@@ -157,8 +163,7 @@ public class ServerCommunication
|
|||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Log.e("SC:update_1", e.toString());
|
handleError("update<1>", null, null, Str.Empty, false, e);
|
||||||
SCNApp.showToast("Communication with server failed", 4000);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,25 +172,29 @@ public class ServerCommunication
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
Request request = new Request.Builder()
|
Request request = new Request.Builder()
|
||||||
.url(BASE_URL + "updateFCMToken.php?user_id=" + id + "&user_key=" + key)
|
.url(BASE_URL + "update.php?user_id=" + id + "&user_key=" + key)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
client.newCall(request).enqueue(new Callback() {
|
client.newCall(request).enqueue(new Callback() {
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(Call call, IOException e) {
|
public void onFailure(Call call, IOException e)
|
||||||
Log.e("SC:update_2", e.toString());
|
{
|
||||||
|
handleError("update<1>", call, null, Str.Empty, true, e);
|
||||||
SCNApp.showToast("Communication with server failed", 4000);
|
SCNApp.showToast("Communication with server failed", 4000);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResponse(Call call, Response response) {
|
public void onResponse(Call call, Response response)
|
||||||
try (ResponseBody responseBody = response.body()) {
|
{
|
||||||
|
String r = Str.Empty;
|
||||||
|
try (ResponseBody responseBody = response.body())
|
||||||
|
{
|
||||||
if (!response.isSuccessful())
|
if (!response.isSuccessful())
|
||||||
throw new IOException("Unexpected code " + response);
|
throw new IOException("Unexpected code " + response);
|
||||||
if (responseBody == null) throw new IOException("No response");
|
if (responseBody == null) throw new IOException("No response");
|
||||||
|
|
||||||
String r = responseBody.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();
|
JSONObject json = (JSONObject) new JSONTokener(r).nextValue();
|
||||||
|
|
||||||
@@ -202,10 +211,16 @@ public class ServerCommunication
|
|||||||
SCNSettings.inst().save();
|
SCNSettings.inst().save();
|
||||||
|
|
||||||
SCNApp.refreshAccountTab();
|
SCNApp.refreshAccountTab();
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e("SC:update_2", e.toString());
|
handleSuccess("update<2>", call, response, r);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
handleError("update<2>", call, response, r, false, e);
|
||||||
SCNApp.showToast("Communication with server failed", 4000);
|
SCNApp.showToast("Communication with server failed", 4000);
|
||||||
} finally {
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
SCNApp.runOnUiThread(() -> {
|
SCNApp.runOnUiThread(() -> {
|
||||||
if (loader != null) loader.setVisibility(View.GONE);
|
if (loader != null) loader.setVisibility(View.GONE);
|
||||||
});
|
});
|
||||||
@@ -215,8 +230,7 @@ public class ServerCommunication
|
|||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Log.e("SC:update_2", e.toString());
|
handleError("update<2>", null, null, Str.Empty, false, e);
|
||||||
SCNApp.showToast("Communication with server failed", 4000);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,22 +245,24 @@ public class ServerCommunication
|
|||||||
client.newCall(request).enqueue(new Callback() {
|
client.newCall(request).enqueue(new Callback() {
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(Call call, IOException e) {
|
public void onFailure(Call call, IOException e) {
|
||||||
Log.e("SC:info", e.toString());
|
handleError("info", call, null, Str.Empty, true, e);
|
||||||
SCNApp.showToast("Communication with server failed", 4000);
|
|
||||||
SCNApp.runOnUiThread(() -> {
|
SCNApp.runOnUiThread(() -> {
|
||||||
if (loader != null) loader.setVisibility(View.GONE);
|
if (loader != null) loader.setVisibility(View.GONE);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResponse(Call call, Response response) {
|
public void onResponse(Call call, Response response)
|
||||||
try (ResponseBody responseBody = response.body()) {
|
{
|
||||||
|
String r = Str.Empty;
|
||||||
|
try (ResponseBody responseBody = response.body())
|
||||||
|
{
|
||||||
if (!response.isSuccessful())
|
if (!response.isSuccessful())
|
||||||
throw new IOException("Unexpected code " + response);
|
throw new IOException("Unexpected code " + response);
|
||||||
if (responseBody == null) throw new IOException("No response");
|
if (responseBody == null) throw new IOException("No response");
|
||||||
|
|
||||||
String r = responseBody.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();
|
JSONObject json = (JSONObject) new JSONTokener(r).nextValue();
|
||||||
|
|
||||||
@@ -283,26 +299,25 @@ public class ServerCommunication
|
|||||||
|
|
||||||
SCNApp.refreshAccountTab();
|
SCNApp.refreshAccountTab();
|
||||||
|
|
||||||
if (json_int(json, "unack_count")>0)
|
if (json_int(json, "unack_count")>0) ServerCommunication.requery(id, key, loader);
|
||||||
{
|
|
||||||
ServerCommunication.requery(id, key, loader);
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
handleSuccess("info", call, response, r);
|
||||||
Log.e("SC:info", e.toString());
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
handleError("info", call, response, r, false, e);
|
||||||
SCNApp.showToast("Communication with server failed", 4000);
|
SCNApp.showToast("Communication with server failed", 4000);
|
||||||
} finally {
|
}
|
||||||
SCNApp.runOnUiThread(() -> {
|
finally
|
||||||
if (loader != null) loader.setVisibility(View.GONE);
|
{
|
||||||
});
|
SCNApp.runOnUiThread(() -> { if (loader != null) loader.setVisibility(View.GONE); });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Log.e("SC:info", e.toString());
|
handleError("info", null, null, Str.Empty, false, e);
|
||||||
SCNApp.showToast("Communication with server failed", 4000);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -317,22 +332,24 @@ public class ServerCommunication
|
|||||||
client.newCall(request).enqueue(new Callback() {
|
client.newCall(request).enqueue(new Callback() {
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(Call call, IOException e) {
|
public void onFailure(Call call, IOException e) {
|
||||||
Log.e("SC:requery", e.toString());
|
handleError("requery", call, null, Str.Empty, true, e);
|
||||||
SCNApp.showToast("Communication with server failed", 4000);
|
|
||||||
SCNApp.runOnUiThread(() -> {
|
SCNApp.runOnUiThread(() -> {
|
||||||
if (loader != null) loader.setVisibility(View.GONE);
|
if (loader != null) loader.setVisibility(View.GONE);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResponse(Call call, Response response) {
|
public void onResponse(Call call, Response response)
|
||||||
try (ResponseBody responseBody = response.body()) {
|
{
|
||||||
|
String r = Str.Empty;
|
||||||
|
try (ResponseBody responseBody = response.body())
|
||||||
|
{
|
||||||
if (!response.isSuccessful())
|
if (!response.isSuccessful())
|
||||||
throw new IOException("Unexpected code " + response);
|
throw new IOException("Unexpected code " + response);
|
||||||
if (responseBody == null) throw new IOException("No response");
|
if (responseBody == null) throw new IOException("No response");
|
||||||
|
|
||||||
String r = responseBody.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();
|
JSONObject json = (JSONObject) new JSONTokener(r).nextValue();
|
||||||
|
|
||||||
@@ -346,7 +363,7 @@ public class ServerCommunication
|
|||||||
JSONArray arr = json.getJSONArray("data");
|
JSONArray arr = json.getJSONArray("data");
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
JSONObject o = arr.getJSONObject(0);
|
JSONObject o = arr.getJSONObject(i);
|
||||||
|
|
||||||
long time = json_lng(o, "timestamp");
|
long time = json_lng(o, "timestamp");
|
||||||
String title = json_str(o, "title");
|
String title = json_str(o, "title");
|
||||||
@@ -357,24 +374,15 @@ public class ServerCommunication
|
|||||||
FBMService.recieveData(time, title, content, prio, scn_id, true);
|
FBMService.recieveData(time, title, content, prio, scn_id, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
SCNSettings.inst().user_id = json_int(json, "user_id");
|
handleSuccess("requery", call, response, r);
|
||||||
SCNSettings.inst().quota_curr = json_int(json, "quota");
|
}
|
||||||
SCNSettings.inst().quota_max = json_int(json, "quota_max");
|
catch (Exception e)
|
||||||
SCNSettings.inst().promode_server = json_bool(json, "is_pro");
|
{
|
||||||
if (!json_bool(json, "fcm_token_set")) SCNSettings.inst().fcm_token_server = "";
|
handleError("requery", call, response, r, false, e);
|
||||||
SCNSettings.inst().save();
|
|
||||||
|
|
||||||
SCNApp.refreshAccountTab();
|
|
||||||
|
|
||||||
if (json_int(json, "unack_count")>0)
|
|
||||||
{
|
|
||||||
ServerCommunication.requery(id, key, loader);
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e("SC:info", e.toString());
|
|
||||||
SCNApp.showToast("Communication with server failed", 4000);
|
SCNApp.showToast("Communication with server failed", 4000);
|
||||||
} finally {
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
SCNApp.runOnUiThread(() -> {
|
SCNApp.runOnUiThread(() -> {
|
||||||
if (loader != null) loader.setVisibility(View.GONE);
|
if (loader != null) loader.setVisibility(View.GONE);
|
||||||
});
|
});
|
||||||
@@ -384,8 +392,7 @@ public class ServerCommunication
|
|||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Log.e("SC:requery", e.toString());
|
handleError("requery", null, null, Str.Empty, false, e);
|
||||||
SCNApp.showToast("Communication with server failed", 4000);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -399,22 +406,26 @@ public class ServerCommunication
|
|||||||
.url(BASE_URL + "upgrade.php?user_id=" + id + "&user_key=" + key + "&pro=" + pro + "&pro_token=" + URLEncoder.encode(pro_token, "utf-8"))
|
.url(BASE_URL + "upgrade.php?user_id=" + id + "&user_key=" + key + "&pro=" + pro + "&pro_token=" + URLEncoder.encode(pro_token, "utf-8"))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
client.newCall(request).enqueue(new Callback() {
|
client.newCall(request).enqueue(new Callback()
|
||||||
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(Call call, IOException e) {
|
public void onFailure(Call call, IOException e)
|
||||||
Log.e("SC:upgrade", e.toString());
|
{
|
||||||
SCNApp.showToast("Communication with server failed", 4000);
|
handleError("upgrade", call, null, Str.Empty, true, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResponse(Call call, Response response) {
|
public void onResponse(Call call, Response response)
|
||||||
try (ResponseBody responseBody = response.body()) {
|
{
|
||||||
|
String r = Str.Empty;
|
||||||
|
try (ResponseBody responseBody = response.body())
|
||||||
|
{
|
||||||
if (!response.isSuccessful())
|
if (!response.isSuccessful())
|
||||||
throw new IOException("Unexpected code " + response);
|
throw new IOException("Unexpected code " + response);
|
||||||
if (responseBody == null) throw new IOException("No response");
|
if (responseBody == null) throw new IOException("No response");
|
||||||
|
|
||||||
String r = responseBody.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();
|
JSONObject json = (JSONObject) new JSONTokener(r).nextValue();
|
||||||
|
|
||||||
@@ -430,10 +441,15 @@ public class ServerCommunication
|
|||||||
SCNSettings.inst().save();
|
SCNSettings.inst().save();
|
||||||
|
|
||||||
SCNApp.refreshAccountTab();
|
SCNApp.refreshAccountTab();
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e("SC:upgrade", e.toString());
|
handleSuccess("upgrade", call, response, r);
|
||||||
SCNApp.showToast("Communication with server failed", 4000);
|
}
|
||||||
} finally {
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
handleError("upgrade", call, response, r, false, e);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
SCNApp.runOnUiThread(() -> { if (loader != null) loader.setVisibility(View.GONE); });
|
SCNApp.runOnUiThread(() -> { if (loader != null) loader.setVisibility(View.GONE); });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -441,50 +457,120 @@ public class ServerCommunication
|
|||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
e.printStackTrace();
|
handleError("upgrade", null, null, Str.Empty, false, e);
|
||||||
SCNApp.showToast("Communication with server failed", 4000);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ack(int id, String key, CMessage msg)
|
public static void ack(int id, String key, long msg_scn_id)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Request request = new Request.Builder()
|
Request request = new Request.Builder()
|
||||||
.url(BASE_URL + "ack.php?user_id=" + id + "&user_key=" + key + "&scn_msg_id=" + msg.SCN_ID)
|
.url(BASE_URL + "ack.php?user_id=" + id + "&user_key=" + key + "&scn_msg_id=" + msg_scn_id)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
client.newCall(request).enqueue(new Callback() {
|
client.newCall(request).enqueue(new Callback()
|
||||||
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(Call call, IOException e) {
|
public void onFailure(Call call, IOException e)
|
||||||
Log.e("SC:ack", e.toString());
|
{
|
||||||
|
handleError("ack", call, null, Str.Empty, true, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResponse(Call call, Response response)
|
public void onResponse(Call call, Response response)
|
||||||
{
|
{
|
||||||
try (ResponseBody responseBody = response.body()) {
|
String r = Str.Empty;
|
||||||
|
try (ResponseBody responseBody = response.body())
|
||||||
|
{
|
||||||
if (!response.isSuccessful())
|
if (!response.isSuccessful())
|
||||||
throw new IOException("Unexpected code " + response);
|
throw new IOException("Unexpected code " + response);
|
||||||
if (responseBody == null) throw new IOException("No response");
|
if (responseBody == null) throw new IOException("No response");
|
||||||
|
|
||||||
String r = responseBody.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();
|
JSONObject json = (JSONObject) new JSONTokener(r).nextValue();
|
||||||
|
|
||||||
if (!json_bool(json, "success")) SCNApp.showToast(json_str(json, "message"), 4000);
|
if (!json_bool(json, "success")) SCNApp.showToast(json_str(json, "message"), 4000);
|
||||||
|
|
||||||
} catch (Exception e) {
|
handleSuccess("ack", call, response, r);
|
||||||
Log.e("SC:ack", e.toString());
|
}
|
||||||
SCNApp.showToast("Communication with server failed", 4000);
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
handleError("ack", call, response, r, false, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Log.e("SC:ack", e.toString());
|
handleError("ack", null, null, Str.Empty, false, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
handleError("expand", call, null, Str.Empty, true, e);
|
||||||
|
SCNApp.runOnUiThread(() -> { if (loader != null) loader.setVisibility(View.GONE); });
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResponse(Call call, Response response)
|
||||||
|
{
|
||||||
|
String r = Str.Empty;
|
||||||
|
try (ResponseBody responseBody = response.body())
|
||||||
|
{
|
||||||
|
if (!response.isSuccessful())
|
||||||
|
throw new IOException("Unexpected code " + response);
|
||||||
|
if (responseBody == null) throw new IOException("No response");
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
handleSuccess("expand", call, response, r);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
handleError("expand", call, response, r, false, e);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
SCNApp.runOnUiThread(() -> { if (loader != null) loader.setVisibility(View.GONE); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
handleError("expand", null, null, Str.Empty, false, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -512,4 +598,57 @@ public class ServerCommunication
|
|||||||
{
|
{
|
||||||
return o.getString(key);
|
return o.getString(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void handleSuccess(String source, Call call, Response resp, String respBody)
|
||||||
|
{
|
||||||
|
Log.d("SC:"+source, respBody);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Instant i = Instant.now();
|
||||||
|
String s = source;
|
||||||
|
String u = call.request().url().toString();
|
||||||
|
int rc = resp.code();
|
||||||
|
String r = respBody;
|
||||||
|
LogLevel l = LogLevel.INFO;
|
||||||
|
|
||||||
|
SingleQuery q = new SingleQuery(l, i, s, u, r, rc, "SUCCESS");
|
||||||
|
QueryLog.inst().add(q);
|
||||||
|
}
|
||||||
|
catch (Exception e2)
|
||||||
|
{
|
||||||
|
Log.e("SC:HandleSuccess", e2.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void handleError(String source, Call call, Response resp, String respBody, boolean isio, Exception e)
|
||||||
|
{
|
||||||
|
Log.e("SC:"+source, e.toString());
|
||||||
|
|
||||||
|
if (isio)
|
||||||
|
{
|
||||||
|
SCNApp.showToast("Can't connect to server", 3000);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SCNApp.showToast("Communication with server failed", 4000);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Instant i = Instant.now();
|
||||||
|
String s = source;
|
||||||
|
String u = (call==null)?Str.Empty:call.request().url().toString();
|
||||||
|
int rc = (resp==null)?-1:resp.code();
|
||||||
|
String r = respBody;
|
||||||
|
LogLevel l = isio?LogLevel.WARN:LogLevel.ERROR;
|
||||||
|
|
||||||
|
SingleQuery q = new SingleQuery(l, i, s, u, r, rc, e.toString());
|
||||||
|
QueryLog.inst().add(q);
|
||||||
|
}
|
||||||
|
catch (Exception e2)
|
||||||
|
{
|
||||||
|
Log.e("SC:HandleError", e2.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,82 @@
|
|||||||
|
package com.blackforestbytes.simplecloudnotifier.model;
|
||||||
|
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.os.BaseBundle;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import com.blackforestbytes.simplecloudnotifier.lib.string.Str;
|
||||||
|
|
||||||
|
import org.joda.time.Instant;
|
||||||
|
|
||||||
|
public class SingleQuery
|
||||||
|
{
|
||||||
|
public final Instant Timestamp;
|
||||||
|
|
||||||
|
public final LogLevel Level;
|
||||||
|
public final String Name;
|
||||||
|
public final String URL;
|
||||||
|
public final String Response;
|
||||||
|
public final int ResponseCode;
|
||||||
|
public final String ExceptionString;
|
||||||
|
|
||||||
|
public SingleQuery(LogLevel l, Instant i, String n, String u, String r, int rc, String e)
|
||||||
|
{
|
||||||
|
Level=l;
|
||||||
|
Timestamp=i;
|
||||||
|
Name=n;
|
||||||
|
URL=u;
|
||||||
|
Response=r;
|
||||||
|
ResponseCode=rc;
|
||||||
|
ExceptionString=e;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void save(SharedPreferences.Editor e, String base)
|
||||||
|
{
|
||||||
|
e.putInt(base+".Level", Level.asInt());
|
||||||
|
e.putLong(base+".Timestamp", Timestamp.getMillis());
|
||||||
|
e.putString(base+".Name", Name);
|
||||||
|
e.putString(base+".URL", URL);
|
||||||
|
e.putString(base+".Response", Response);
|
||||||
|
e.putInt(base+".ResponseCode", ResponseCode);
|
||||||
|
e.putString(base+".ExceptionString", ExceptionString);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void save(BaseBundle e, String base)
|
||||||
|
{
|
||||||
|
e.putInt(base+".Level", Level.asInt());
|
||||||
|
e.putLong(base+".Timestamp", Timestamp.getMillis());
|
||||||
|
e.putString(base+".Name", Name);
|
||||||
|
e.putString(base+".URL", URL);
|
||||||
|
e.putString(base+".Response", Response);
|
||||||
|
e.putInt(base+".ResponseCode", ResponseCode);
|
||||||
|
e.putString(base+".ExceptionString", ExceptionString);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SingleQuery load(SharedPreferences e, String base)
|
||||||
|
{
|
||||||
|
return new SingleQuery
|
||||||
|
(
|
||||||
|
LogLevel.fromInt(e.getInt(base+".Level", 0)),
|
||||||
|
new Instant(e.getLong(base+".Timestamp", 0)),
|
||||||
|
e.getString(base+".Name", Str.Empty),
|
||||||
|
e.getString(base+".URL", Str.Empty),
|
||||||
|
e.getString(base+".Response", Str.Empty),
|
||||||
|
e.getInt(base+".ResponseCode", -1),
|
||||||
|
e.getString(base+".ExceptionString", Str.Empty)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SingleQuery load(BaseBundle e, String base)
|
||||||
|
{
|
||||||
|
return new SingleQuery
|
||||||
|
(
|
||||||
|
LogLevel.fromInt(e.getInt(base+".Level", 0)),
|
||||||
|
new Instant(e.getLong(base+".Timestamp", 0)),
|
||||||
|
e.getString(base+".Name", Str.Empty),
|
||||||
|
e.getString(base+".URL", Str.Empty),
|
||||||
|
e.getString(base+".Response", Str.Empty),
|
||||||
|
e.getInt(base+".ResponseCode", -1),
|
||||||
|
e.getString(base+".ExceptionString", Str.Empty)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@@ -4,14 +4,21 @@ import android.util.Log;
|
|||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.blackforestbytes.simplecloudnotifier.SCNApp;
|
import com.blackforestbytes.simplecloudnotifier.SCNApp;
|
||||||
|
import com.blackforestbytes.simplecloudnotifier.lib.string.Str;
|
||||||
import com.blackforestbytes.simplecloudnotifier.model.CMessage;
|
import com.blackforestbytes.simplecloudnotifier.model.CMessage;
|
||||||
import com.blackforestbytes.simplecloudnotifier.model.CMessageList;
|
import com.blackforestbytes.simplecloudnotifier.model.CMessageList;
|
||||||
|
import com.blackforestbytes.simplecloudnotifier.model.LogLevel;
|
||||||
import com.blackforestbytes.simplecloudnotifier.model.PriorityEnum;
|
import com.blackforestbytes.simplecloudnotifier.model.PriorityEnum;
|
||||||
|
import com.blackforestbytes.simplecloudnotifier.model.QueryLog;
|
||||||
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
|
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
|
||||||
import com.blackforestbytes.simplecloudnotifier.model.ServerCommunication;
|
import com.blackforestbytes.simplecloudnotifier.model.ServerCommunication;
|
||||||
|
import com.blackforestbytes.simplecloudnotifier.model.SingleQuery;
|
||||||
import com.google.firebase.messaging.FirebaseMessagingService;
|
import com.google.firebase.messaging.FirebaseMessagingService;
|
||||||
import com.google.firebase.messaging.RemoteMessage;
|
import com.google.firebase.messaging.RemoteMessage;
|
||||||
|
|
||||||
|
import org.joda.time.Instant;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
public class FBMService extends FirebaseMessagingService
|
public class FBMService extends FirebaseMessagingService
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
@@ -38,8 +45,20 @@ public class FBMService extends FirebaseMessagingService
|
|||||||
String content = remoteMessage.getData().get("body");
|
String content = remoteMessage.getData().get("body");
|
||||||
PriorityEnum prio = PriorityEnum.parseAPI(remoteMessage.getData().get("priority"));
|
PriorityEnum prio = PriorityEnum.parseAPI(remoteMessage.getData().get("priority"));
|
||||||
long scn_id = Long.parseLong(remoteMessage.getData().get("scn_msg_id"));
|
long scn_id = Long.parseLong(remoteMessage.getData().get("scn_msg_id"));
|
||||||
|
boolean trimmed = Boolean.parseBoolean(remoteMessage.getData().get("trimmed"));
|
||||||
|
|
||||||
recieveData(time, title, content, prio, scn_id, false);
|
|
||||||
|
SingleQuery q = new SingleQuery(LogLevel.INFO, Instant.now(), "FBM<recieve>", Str.Empty, new JSONObject(remoteMessage.getData()).toString(), 0, "SUCCESS");
|
||||||
|
QueryLog.inst().add(q);
|
||||||
|
|
||||||
|
if (trimmed)
|
||||||
|
{
|
||||||
|
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
|
||||||
|
{
|
||||||
|
recieveData(time, title, content, prio, scn_id, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@@ -50,15 +69,15 @@ public class FBMService extends FirebaseMessagingService
|
|||||||
|
|
||||||
public static void recieveData(long time, String title, String content, PriorityEnum prio, long scn_id, boolean alwaysAck)
|
public static void recieveData(long time, String title, String content, PriorityEnum prio, long scn_id, boolean alwaysAck)
|
||||||
{
|
{
|
||||||
CMessage msg = CMessageList.inst().add(scn_id, time, title, content, prio);
|
|
||||||
|
|
||||||
if (CMessageList.inst().isAck(scn_id))
|
if (CMessageList.inst().isAck(scn_id))
|
||||||
{
|
{
|
||||||
Log.w("FB::MessageReceived", "Recieved ack-ed message: " + scn_id);
|
Log.w("FB::MessageReceived", "Recieved ack-ed message: " + scn_id);
|
||||||
if (alwaysAck) ServerCommunication.ack(SCNSettings.inst().user_id, SCNSettings.inst().user_key, msg);
|
if (alwaysAck) ServerCommunication.ack(SCNSettings.inst().user_id, SCNSettings.inst().user_key, scn_id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CMessage msg = CMessageList.inst().add(scn_id, time, title, content, prio);
|
||||||
|
|
||||||
if (SCNApp.isBackground())
|
if (SCNApp.isBackground())
|
||||||
{
|
{
|
||||||
NotificationService.inst().showBackground(msg);
|
NotificationService.inst().showBackground(msg);
|
||||||
@@ -68,6 +87,6 @@ public class FBMService extends FirebaseMessagingService
|
|||||||
NotificationService.inst().showForeground(msg);
|
NotificationService.inst().showForeground(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
ServerCommunication.ack(SCNSettings.inst().user_id, SCNSettings.inst().user_key, msg);
|
ServerCommunication.ack(SCNSettings.inst().user_id, SCNSettings.inst().user_key, scn_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -2,6 +2,7 @@ package com.blackforestbytes.simplecloudnotifier.service;
|
|||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
@@ -11,13 +12,18 @@ import com.android.billingclient.api.BillingFlowParams;
|
|||||||
import com.android.billingclient.api.Purchase;
|
import com.android.billingclient.api.Purchase;
|
||||||
import com.android.billingclient.api.PurchasesUpdatedListener;
|
import com.android.billingclient.api.PurchasesUpdatedListener;
|
||||||
import com.blackforestbytes.simplecloudnotifier.SCNApp;
|
import com.blackforestbytes.simplecloudnotifier.SCNApp;
|
||||||
|
import com.blackforestbytes.simplecloudnotifier.lib.datatypes.Tuple2;
|
||||||
|
import com.blackforestbytes.simplecloudnotifier.lib.datatypes.Tuple3;
|
||||||
import com.blackforestbytes.simplecloudnotifier.lib.lambda.Func0to0;
|
import com.blackforestbytes.simplecloudnotifier.lib.lambda.Func0to0;
|
||||||
import com.blackforestbytes.simplecloudnotifier.lib.string.Str;
|
import com.blackforestbytes.simplecloudnotifier.lib.string.Str;
|
||||||
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
|
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
|
||||||
import com.blackforestbytes.simplecloudnotifier.view.MainActivity;
|
import com.blackforestbytes.simplecloudnotifier.view.MainActivity;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Dictionary;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
@@ -45,12 +51,21 @@ public class IABService implements PurchasesUpdatedListener
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum SimplePurchaseState { YES, NO, UNINITIALIZED }
|
||||||
|
|
||||||
private BillingClient client;
|
private BillingClient client;
|
||||||
private boolean isServiceConnected;
|
private boolean isServiceConnected;
|
||||||
private final List<Purchase> purchases = new ArrayList<>();
|
private final List<Purchase> purchases = new ArrayList<>();
|
||||||
|
private boolean _isInitialized = false;
|
||||||
|
|
||||||
|
private Map<String, Boolean> _localCache= new HashMap<>();
|
||||||
|
|
||||||
public IABService(Context c)
|
public IABService(Context c)
|
||||||
{
|
{
|
||||||
|
_isInitialized = false;
|
||||||
|
|
||||||
|
loadCache();
|
||||||
|
|
||||||
client = BillingClient
|
client = BillingClient
|
||||||
.newBuilder(c)
|
.newBuilder(c)
|
||||||
.setListener(this)
|
.setListener(this)
|
||||||
@@ -59,6 +74,50 @@ public class IABService implements PurchasesUpdatedListener
|
|||||||
startServiceConnection(this::queryPurchases, false);
|
startServiceConnection(this::queryPurchases, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void reloadPrefs()
|
||||||
|
{
|
||||||
|
loadCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadCache()
|
||||||
|
{
|
||||||
|
_localCache.clear();
|
||||||
|
SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("IAB", Context.MODE_PRIVATE);
|
||||||
|
int count = sharedPref.getInt("c", 0);
|
||||||
|
for (int i=0; i < count; i++)
|
||||||
|
{
|
||||||
|
String k = sharedPref.getString("["+i+"]->key", null);
|
||||||
|
boolean v = sharedPref.getBoolean("["+i+"]->value", false);
|
||||||
|
if (k==null)continue;
|
||||||
|
_localCache.put(k, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveCache()
|
||||||
|
{
|
||||||
|
SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("IAB", Context.MODE_PRIVATE);
|
||||||
|
SharedPreferences.Editor editor= sharedPref.edit();
|
||||||
|
|
||||||
|
editor.putInt("c", _localCache.size());
|
||||||
|
int i = 0;
|
||||||
|
for (Map.Entry<String, Boolean> e : _localCache.entrySet())
|
||||||
|
{
|
||||||
|
editor.putString("["+i+"]->key", e.getKey());
|
||||||
|
editor.putBoolean("["+i+"]->value", e.getValue());
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
editor.apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ConstantConditions")
|
||||||
|
private synchronized void updateCache(String k, boolean v)
|
||||||
|
{
|
||||||
|
if (_localCache.containsKey(k) && _localCache.get(k)==v) return;
|
||||||
|
|
||||||
|
_localCache.put(k, v);
|
||||||
|
saveCache();
|
||||||
|
}
|
||||||
|
|
||||||
public void queryPurchases()
|
public void queryPurchases()
|
||||||
{
|
{
|
||||||
Func0to0 queryToExecute = () ->
|
Func0to0 queryToExecute = () ->
|
||||||
@@ -71,10 +130,12 @@ public class IABService implements PurchasesUpdatedListener
|
|||||||
{
|
{
|
||||||
for (Purchase p : purchasesResult.getPurchasesList())
|
for (Purchase p : purchasesResult.getPurchasesList())
|
||||||
{
|
{
|
||||||
handlePurchase(p);
|
handlePurchase(p, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean newProMode = getPurchaseCached(IAB_PRO_MODE) != null;
|
_isInitialized = true;
|
||||||
|
|
||||||
|
boolean newProMode = getPurchaseCachedSimple(IAB_PRO_MODE);
|
||||||
if (newProMode != SCNSettings.inst().promode_local)
|
if (newProMode != SCNSettings.inst().promode_local)
|
||||||
{
|
{
|
||||||
refreshProModeListener();
|
refreshProModeListener();
|
||||||
@@ -102,7 +163,8 @@ public class IABService implements PurchasesUpdatedListener
|
|||||||
}, true);
|
}, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void executeServiceRequest(Func0to0 runnable, final boolean userRequest) {
|
private void executeServiceRequest(Func0to0 runnable, final boolean userRequest)
|
||||||
|
{
|
||||||
if (isServiceConnected)
|
if (isServiceConnected)
|
||||||
{
|
{
|
||||||
runnable.invoke();
|
runnable.invoke();
|
||||||
@@ -130,28 +192,31 @@ public class IABService implements PurchasesUpdatedListener
|
|||||||
{
|
{
|
||||||
for (Purchase purchase : purchases)
|
for (Purchase purchase : purchases)
|
||||||
{
|
{
|
||||||
handlePurchase(purchase);
|
handlePurchase(purchase, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (responseCode == BillingClient.BillingResponse.ITEM_ALREADY_OWNED && purchases != null)
|
else if (responseCode == BillingClient.BillingResponse.ITEM_ALREADY_OWNED && purchases != null)
|
||||||
{
|
{
|
||||||
for (Purchase purchase : purchases)
|
for (Purchase purchase : purchases)
|
||||||
{
|
{
|
||||||
handlePurchase(purchase);
|
handlePurchase(purchase, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handlePurchase(Purchase purchase)
|
private void handlePurchase(Purchase purchase, boolean triggerUpdate)
|
||||||
{
|
{
|
||||||
Log.d(TAG, "Got a verified purchase: " + purchase);
|
Log.d(TAG, "Got a verified purchase: " + purchase);
|
||||||
|
|
||||||
purchases.add(purchase);
|
purchases.add(purchase);
|
||||||
|
|
||||||
refreshProModeListener();
|
if (triggerUpdate) refreshProModeListener();
|
||||||
|
|
||||||
|
updateCache(purchase.getSku(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void refreshProModeListener() {
|
private void refreshProModeListener()
|
||||||
|
{
|
||||||
MainActivity ma = SCNApp.getMainActivity();
|
MainActivity ma = SCNApp.getMainActivity();
|
||||||
if (ma != null) ma.adpTabs.tab3.updateProState();
|
if (ma != null) ma.adpTabs.tab3.updateProState();
|
||||||
if (ma != null) ma.adpTabs.tab1.updateProState();
|
if (ma != null) ma.adpTabs.tab1.updateProState();
|
||||||
@@ -183,13 +248,31 @@ public class IABService implements PurchasesUpdatedListener
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public Purchase getPurchaseCached(String id)
|
public boolean getPurchaseCachedSimple(String id)
|
||||||
{
|
{
|
||||||
for (Purchase p : purchases)
|
return getPurchaseCachedExtended(id).Item1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ConstantConditions")
|
||||||
|
public Tuple3<Boolean, Boolean, String> getPurchaseCachedExtended(String id)
|
||||||
|
{
|
||||||
|
// <state, initialized, token>
|
||||||
|
|
||||||
|
if (!_isInitialized)
|
||||||
{
|
{
|
||||||
if (Str.equals(p.getSku(), id)) return p;
|
if (_localCache.containsKey(id) && _localCache.get(id)) return new Tuple3<>(true, false, Str.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
for (Purchase p : purchases)
|
||||||
|
{
|
||||||
|
if (Str.equals(p.getSku(), id))
|
||||||
|
{
|
||||||
|
updateCache(id, true);
|
||||||
|
return new Tuple3<>(true, true, p.getPurchaseToken());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateCache(id, false);
|
||||||
|
return new Tuple3<>(false, true, Str.Empty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -6,6 +6,7 @@ import android.app.NotificationManager;
|
|||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.graphics.BitmapFactory;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.media.AudioManager;
|
import android.media.AudioManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
@@ -64,7 +65,8 @@ public class NotificationService
|
|||||||
channel0.setDescription("Push notifications from the server with low priority.\nGo to the in-app settings to configure ringtone, volume and vibrations");
|
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.setSound(null, null);
|
||||||
channel0.setVibrationPattern(null);
|
channel0.setVibrationPattern(null);
|
||||||
channel0.setLightColor(Color.BLUE);
|
channel0.setLightColor(Color.CYAN);
|
||||||
|
channel0.enableLights(true);
|
||||||
notifman.createNotificationChannel(channel0);
|
notifman.createNotificationChannel(channel0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -76,7 +78,8 @@ public class NotificationService
|
|||||||
channel1.setDescription("Push notifications from the server with low priority.\nGo to the in-app settings to configure ringtone, volume and vibrations");
|
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.setSound(null, null);
|
||||||
channel1.setVibrationPattern(null);
|
channel1.setVibrationPattern(null);
|
||||||
channel1.setLightColor(Color.BLUE);
|
channel1.setLightColor(Color.CYAN);
|
||||||
|
channel1.enableLights(true);
|
||||||
notifman.createNotificationChannel(channel1);
|
notifman.createNotificationChannel(channel1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -84,11 +87,13 @@ public class NotificationService
|
|||||||
NotificationChannel channel2 = notifman.getNotificationChannel(CHANNEL_P2_ID);
|
NotificationChannel channel2 = notifman.getNotificationChannel(CHANNEL_P2_ID);
|
||||||
if (channel2 == null)
|
if (channel2 == null)
|
||||||
{
|
{
|
||||||
channel2 = new NotificationChannel(CHANNEL_P1_ID, "Push notifications (high priority)", NotificationManager.IMPORTANCE_DEFAULT);
|
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.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.setSound(null, null);
|
||||||
channel2.setVibrationPattern(null);
|
channel2.setVibrationPattern(null);
|
||||||
channel2.setLightColor(Color.BLUE);
|
channel2.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
|
||||||
|
channel2.setLightColor(Color.CYAN);
|
||||||
|
channel2.enableLights(true);
|
||||||
notifman.createNotificationChannel(channel2);
|
notifman.createNotificationChannel(channel2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -114,9 +119,9 @@ public class NotificationService
|
|||||||
{
|
{
|
||||||
Vibrator v = (Vibrator) SCNApp.getContext().getSystemService(Context.VIBRATOR_SERVICE);
|
Vibrator v = (Vibrator) SCNApp.getContext().getSystemService(Context.VIBRATOR_SERVICE);
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
v.vibrate(VibrationEffect.createOneShot(1500, VibrationEffect.DEFAULT_AMPLITUDE));
|
v.vibrate(VibrationEffect.createOneShot(500, VibrationEffect.DEFAULT_AMPLITUDE));
|
||||||
} else {
|
} else {
|
||||||
v.vibrate(1500);
|
v.vibrate(500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -165,12 +170,15 @@ public class NotificationService
|
|||||||
private void showBackground_old(CMessage msg, Context ctxt, NotificationSettings ns, PriorityEnum prio)
|
private void showBackground_old(CMessage msg, Context ctxt, NotificationSettings ns, PriorityEnum prio)
|
||||||
{
|
{
|
||||||
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(ctxt, getChannel(prio));
|
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(ctxt, getChannel(prio));
|
||||||
mBuilder.setSmallIcon(R.drawable.ic_bfb);
|
mBuilder.setSmallIcon(R.drawable.ic_notification_white);
|
||||||
|
mBuilder.setLargeIcon(BitmapFactory.decodeResource(ctxt.getResources(), R.mipmap.ic_notification_full));
|
||||||
mBuilder.setContentTitle(msg.Title);
|
mBuilder.setContentTitle(msg.Title);
|
||||||
mBuilder.setContentText(msg.Content);
|
mBuilder.setContentText(msg.Content);
|
||||||
mBuilder.setShowWhen(true);
|
mBuilder.setShowWhen(true);
|
||||||
mBuilder.setWhen(msg.Timestamp * 1000);
|
mBuilder.setWhen(msg.Timestamp * 1000);
|
||||||
mBuilder.setAutoCancel(true);
|
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.LOW) mBuilder.setPriority(NotificationCompat.PRIORITY_LOW);
|
||||||
if (msg.Priority == PriorityEnum.NORMAL) mBuilder.setPriority(NotificationCompat.PRIORITY_DEFAULT);
|
if (msg.Priority == PriorityEnum.NORMAL) mBuilder.setPriority(NotificationCompat.PRIORITY_DEFAULT);
|
||||||
@@ -198,19 +206,22 @@ public class NotificationService
|
|||||||
|
|
||||||
Notification n = mBuilder.build();
|
Notification n = mBuilder.build();
|
||||||
|
|
||||||
if (mNotificationManager != null) mNotificationManager.notify(0, n);
|
if (mNotificationManager != null) mNotificationManager.notify((int)msg.SCN_ID, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||||
private void showBackground_new(CMessage msg, Context ctxt, NotificationSettings ns, PriorityEnum prio)
|
private void showBackground_new(CMessage msg, Context ctxt, NotificationSettings ns, PriorityEnum prio)
|
||||||
{
|
{
|
||||||
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(ctxt, getChannel(prio));
|
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(ctxt, getChannel(prio));
|
||||||
mBuilder.setSmallIcon(R.drawable.ic_bfb);
|
mBuilder.setSmallIcon(R.drawable.ic_notification_white);
|
||||||
|
mBuilder.setLargeIcon(BitmapFactory.decodeResource(ctxt.getResources(), R.mipmap.ic_notification_full));
|
||||||
mBuilder.setContentTitle(msg.Title);
|
mBuilder.setContentTitle(msg.Title);
|
||||||
mBuilder.setContentText(msg.Content);
|
mBuilder.setContentText(msg.Content);
|
||||||
mBuilder.setShowWhen(true);
|
mBuilder.setShowWhen(true);
|
||||||
mBuilder.setWhen(msg.Timestamp * 1000);
|
mBuilder.setWhen(msg.Timestamp * 1000);
|
||||||
mBuilder.setAutoCancel(true);
|
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 (ns.EnableLED) mBuilder.setLights(ns.LEDColor, 500, 500);
|
||||||
|
|
||||||
@@ -218,10 +229,10 @@ public class NotificationService
|
|||||||
if (msg.Priority == PriorityEnum.NORMAL) mBuilder.setPriority(NotificationCompat.PRIORITY_DEFAULT);
|
if (msg.Priority == PriorityEnum.NORMAL) mBuilder.setPriority(NotificationCompat.PRIORITY_DEFAULT);
|
||||||
if (msg.Priority == PriorityEnum.HIGH) mBuilder.setPriority(NotificationCompat.PRIORITY_HIGH);
|
if (msg.Priority == PriorityEnum.HIGH) mBuilder.setPriority(NotificationCompat.PRIORITY_HIGH);
|
||||||
|
|
||||||
Intent intnt_click = new Intent(SCNApp.getContext(), BroadcastReceiverService.class);
|
Intent intent = new Intent(ctxt, MainActivity.class);
|
||||||
intnt_click.putExtra(BroadcastReceiverService.ID_KEY, BroadcastReceiverService.NOTIF_SHOW_MAIN);
|
PendingIntent pi = PendingIntent.getActivity(ctxt, 0, intent, 0);
|
||||||
PendingIntent pi = PendingIntent.getBroadcast(ctxt, 0, intnt_click, 0);
|
|
||||||
mBuilder.setContentIntent(pi);
|
mBuilder.setContentIntent(pi);
|
||||||
|
|
||||||
NotificationManager mNotificationManager = (NotificationManager) ctxt.getSystemService(Context.NOTIFICATION_SERVICE);
|
NotificationManager mNotificationManager = (NotificationManager) ctxt.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||||
if (mNotificationManager == null) return;
|
if (mNotificationManager == null) return;
|
||||||
|
|
||||||
@@ -242,12 +253,12 @@ public class NotificationService
|
|||||||
Notification n = mBuilder.build();
|
Notification n = mBuilder.build();
|
||||||
n.flags |= Notification.FLAG_AUTO_CANCEL;
|
n.flags |= Notification.FLAG_AUTO_CANCEL;
|
||||||
|
|
||||||
mNotificationManager.notify(0, n);
|
mNotificationManager.notify((int)msg.SCN_ID, n);
|
||||||
|
|
||||||
if (ns.EnableVibration)
|
if (ns.EnableVibration)
|
||||||
{
|
{
|
||||||
Vibrator v = (Vibrator) SCNApp.getContext().getSystemService(Context.VIBRATOR_SERVICE);
|
Vibrator v = (Vibrator) SCNApp.getContext().getSystemService(Context.VIBRATOR_SERVICE);
|
||||||
v.vibrate(VibrationEffect.createOneShot(1500, VibrationEffect.DEFAULT_AMPLITUDE));
|
v.vibrate(VibrationEffect.createOneShot(500, VibrationEffect.DEFAULT_AMPLITUDE));
|
||||||
}
|
}
|
||||||
|
|
||||||
//if (ns.EnableLED) { } // no LED in Android-O -- configure via Channel
|
//if (ns.EnableLED) { } // no LED in Android-O -- configure via Channel
|
||||||
|
@@ -0,0 +1,56 @@
|
|||||||
|
package com.blackforestbytes.simplecloudnotifier.util;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.util.TypedValue;
|
||||||
|
import android.widget.ScrollView;
|
||||||
|
|
||||||
|
import com.blackforestbytes.simplecloudnotifier.R;
|
||||||
|
|
||||||
|
public class MaxHeightScrollView extends ScrollView
|
||||||
|
{
|
||||||
|
public int maxHeight = Integer.MAX_VALUE;//dp
|
||||||
|
|
||||||
|
public MaxHeightScrollView(Context context)
|
||||||
|
{
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MaxHeightScrollView(Context context, AttributeSet attrs)
|
||||||
|
{
|
||||||
|
super(context, attrs);
|
||||||
|
|
||||||
|
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.MaxHeightScrollView, 0, 0);
|
||||||
|
try {
|
||||||
|
maxHeight = a.getInteger(R.styleable.MaxHeightScrollView_maxHeightOverride, Integer.MAX_VALUE);
|
||||||
|
} finally {
|
||||||
|
a.recycle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public MaxHeightScrollView(Context context, AttributeSet attrs, int defStyleAttr)
|
||||||
|
{
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
|
||||||
|
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.MaxHeightScrollView, 0, 0);
|
||||||
|
try {
|
||||||
|
maxHeight = a.getInteger(R.styleable.MaxHeightScrollView_maxHeightOverride, Integer.MAX_VALUE);
|
||||||
|
} finally {
|
||||||
|
a.recycle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
|
||||||
|
{
|
||||||
|
heightMeasureSpec = MeasureSpec.makeMeasureSpec(dpToPx(getResources(), maxHeight), MeasureSpec.AT_MOST);
|
||||||
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int dpToPx(Resources res, int dp)
|
||||||
|
{
|
||||||
|
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, res.getDisplayMetrics());
|
||||||
|
}
|
||||||
|
}
|
@@ -3,6 +3,7 @@ package com.blackforestbytes.simplecloudnotifier.util;
|
|||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
|
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
|
||||||
import com.blackforestbytes.simplecloudnotifier.view.MessageAdapter;
|
import com.blackforestbytes.simplecloudnotifier.view.MessageAdapter;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
@@ -13,10 +14,21 @@ public class MessageAdapterTouchHelper extends ItemTouchHelper.SimpleCallback
|
|||||||
{
|
{
|
||||||
private MessageAdapterTouchHelperListener listener;
|
private MessageAdapterTouchHelperListener listener;
|
||||||
|
|
||||||
|
private int dir = 0;
|
||||||
|
|
||||||
public MessageAdapterTouchHelper(int dragDirs, int swipeDirs, MessageAdapterTouchHelperListener listener)
|
public MessageAdapterTouchHelper(int dragDirs, int swipeDirs, MessageAdapterTouchHelperListener listener)
|
||||||
{
|
{
|
||||||
super(dragDirs, swipeDirs);
|
super(dragDirs, swipeDirs);
|
||||||
|
this.dir = swipeDirs;
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
|
updateEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateEnabled()
|
||||||
|
{
|
||||||
|
int sdir = SCNSettings.inst().EnableDeleteSwipe ? ItemTouchHelper.LEFT : 0;
|
||||||
|
if (dir == sdir) return;
|
||||||
|
setDefaultSwipeDirs(dir = sdir);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -0,0 +1,25 @@
|
|||||||
|
package com.blackforestbytes.simplecloudnotifier.util;
|
||||||
|
|
||||||
|
import android.text.Editable;
|
||||||
|
import android.text.TextWatcher;
|
||||||
|
|
||||||
|
public abstract class TextChangedListener<T> implements TextWatcher {
|
||||||
|
private T target;
|
||||||
|
|
||||||
|
public TextChangedListener(T target) {
|
||||||
|
this.target = target;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTextChanged(CharSequence s, int start, int before, int count) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterTextChanged(Editable s) {
|
||||||
|
this.onTextChanged(target, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void onTextChanged(T target, Editable s);
|
||||||
|
}
|
@@ -6,12 +6,14 @@ import android.content.ClipData;
|
|||||||
import android.content.ClipboardManager;
|
import android.content.ClipboardManager;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.graphics.Color;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.ImageButton;
|
import android.widget.ImageButton;
|
||||||
|
import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import com.blackforestbytes.simplecloudnotifier.R;
|
import com.blackforestbytes.simplecloudnotifier.R;
|
||||||
@@ -91,7 +93,7 @@ public class AccountFragment extends Fragment
|
|||||||
|
|
||||||
builder.setPositiveButton("YES", (dialog, which) -> {
|
builder.setPositiveButton("YES", (dialog, which) -> {
|
||||||
CMessageList.inst().clear();
|
CMessageList.inst().clear();
|
||||||
SCNApp.showToast("Notifications cleared", 1000);
|
SCNApp.showToast("Messages cleared", 1000);
|
||||||
dialog.dismiss();
|
dialog.dismiss();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -103,7 +105,7 @@ public class AccountFragment extends Fragment
|
|||||||
|
|
||||||
v.findViewById(R.id.btnQR).setOnClickListener(cv ->
|
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);
|
startActivity(browserIntent);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -126,10 +128,11 @@ public class AccountFragment extends Fragment
|
|||||||
public void updateUI(View v)
|
public void updateUI(View v)
|
||||||
{
|
{
|
||||||
if (v == null) return;
|
if (v == null) return;
|
||||||
TextView tvUserID = v.findViewById(R.id.tvUserID);
|
TextView tvUserID = v.findViewById(R.id.tvUserID);
|
||||||
TextView tvUserKey = v.findViewById(R.id.tvUserKey);
|
TextView tvUserKey = v.findViewById(R.id.tvUserKey);
|
||||||
TextView tvQuota = v.findViewById(R.id.tvQuota);
|
TextView tvQuota = v.findViewById(R.id.tvQuota);
|
||||||
ImageButton btnQR = v.findViewById(R.id.btnQR);
|
ImageView ivQuota = v.findViewById(R.id.ic_img_quota);
|
||||||
|
ImageButton btnQR = v.findViewById(R.id.btnQR);
|
||||||
|
|
||||||
SCNSettings s = SCNSettings.inst();
|
SCNSettings s = SCNSettings.inst();
|
||||||
|
|
||||||
@@ -138,7 +141,8 @@ public class AccountFragment extends Fragment
|
|||||||
tvUserID.setText(String.valueOf(s.user_id));
|
tvUserID.setText(String.valueOf(s.user_id));
|
||||||
tvUserKey.setText(s.user_key);
|
tvUserKey.setText(s.user_key);
|
||||||
tvQuota.setText(String.format("%d / %d", s.quota_curr, s.quota_max));
|
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
|
else
|
||||||
{
|
{
|
||||||
@@ -146,6 +150,7 @@ public class AccountFragment extends Fragment
|
|||||||
tvUserKey.setText(R.string.str_not_connected);
|
tvUserKey.setText(R.string.str_not_connected);
|
||||||
tvQuota.setText(R.string.str_not_connected);
|
tvQuota.setText(R.string.str_not_connected);
|
||||||
btnQR.setImageResource(R.drawable.qr_default);
|
btnQR.setImageResource(R.drawable.qr_default);
|
||||||
|
ivQuota.setColorFilter(0x80_80_80);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,14 +1,23 @@
|
|||||||
package com.blackforestbytes.simplecloudnotifier.view;
|
package com.blackforestbytes.simplecloudnotifier.view;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
import android.widget.RelativeLayout;
|
import android.widget.RelativeLayout;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.blackforestbytes.simplecloudnotifier.R;
|
import com.blackforestbytes.simplecloudnotifier.R;
|
||||||
import com.blackforestbytes.simplecloudnotifier.SCNApp;
|
import com.blackforestbytes.simplecloudnotifier.SCNApp;
|
||||||
import com.blackforestbytes.simplecloudnotifier.model.CMessageList;
|
import com.blackforestbytes.simplecloudnotifier.model.CMessageList;
|
||||||
|
import com.blackforestbytes.simplecloudnotifier.model.QueryLog;
|
||||||
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
|
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
|
||||||
import com.blackforestbytes.simplecloudnotifier.service.IABService;
|
import com.blackforestbytes.simplecloudnotifier.service.IABService;
|
||||||
import com.blackforestbytes.simplecloudnotifier.service.NotificationService;
|
import com.blackforestbytes.simplecloudnotifier.service.NotificationService;
|
||||||
|
import com.blackforestbytes.simplecloudnotifier.view.debug.QueryLogActivity;
|
||||||
import com.google.android.material.tabs.TabLayout;
|
import com.google.android.material.tabs.TabLayout;
|
||||||
|
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
@@ -16,6 +25,12 @@ import androidx.appcompat.widget.Toolbar;
|
|||||||
import androidx.viewpager.widget.PagerAdapter;
|
import androidx.viewpager.widget.PagerAdapter;
|
||||||
import androidx.viewpager.widget.ViewPager;
|
import androidx.viewpager.widget.ViewPager;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.ObjectInputStream;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
public class MainActivity extends AppCompatActivity
|
public class MainActivity extends AppCompatActivity
|
||||||
{
|
{
|
||||||
public TabAdapter adpTabs;
|
public TabAdapter adpTabs;
|
||||||
@@ -24,6 +39,8 @@ public class MainActivity extends AppCompatActivity
|
|||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState)
|
protected void onCreate(Bundle savedInstanceState)
|
||||||
{
|
{
|
||||||
|
QueryLog.inst();
|
||||||
|
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_main);
|
setContentView(R.layout.activity_main);
|
||||||
|
|
||||||
@@ -33,6 +50,7 @@ public class MainActivity extends AppCompatActivity
|
|||||||
layoutRoot = findViewById(R.id.layoutRoot);
|
layoutRoot = findViewById(R.id.layoutRoot);
|
||||||
|
|
||||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||||
|
toolbar.setOnClickListener(this::onToolbackClicked);
|
||||||
setSupportActionBar(toolbar);
|
setSupportActionBar(toolbar);
|
||||||
|
|
||||||
ViewPager viewPager = findViewById(R.id.pager);
|
ViewPager viewPager = findViewById(R.id.pager);
|
||||||
@@ -81,4 +99,126 @@ public class MainActivity extends AppCompatActivity
|
|||||||
CMessageList.inst().fullSave();
|
CMessageList.inst().fullSave();
|
||||||
IABService.inst().destroy();
|
IABService.inst().destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int clickCount = 0;
|
||||||
|
private long lastClick = 0;
|
||||||
|
private void onToolbackClicked(View v)
|
||||||
|
{
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
if (now - lastClick > 200) clickCount=0;
|
||||||
|
clickCount++;
|
||||||
|
lastClick = now;
|
||||||
|
|
||||||
|
if (clickCount == 4) startActivity(new Intent(this, QueryLogActivity.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
if(requestCode == 1991 && resultCode == RESULT_OK)
|
||||||
|
{
|
||||||
|
Uri uri = data.getData(); //The uri with the location of the file
|
||||||
|
|
||||||
|
Context ctxt = this;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ObjectInputStream stream = new ObjectInputStream(getContentResolver().openInputStream(uri));
|
||||||
|
|
||||||
|
Map<String, ?> d1 = (Map<String, ?>)stream.readObject();
|
||||||
|
Map<String, ?> d2 = (Map<String, ?>)stream.readObject();
|
||||||
|
Map<String, ?> d3 = (Map<String, ?>)stream.readObject();
|
||||||
|
Map<String, ?> d4 = (Map<String, ?>)stream.readObject();
|
||||||
|
|
||||||
|
stream.close();
|
||||||
|
|
||||||
|
runOnUiThread(() ->
|
||||||
|
{
|
||||||
|
|
||||||
|
SharedPreferences.Editor e1 = ctxt.getSharedPreferences("Config", Context.MODE_PRIVATE).edit();
|
||||||
|
SharedPreferences.Editor e2 = ctxt.getSharedPreferences("IAB", Context.MODE_PRIVATE).edit();
|
||||||
|
SharedPreferences.Editor e3 = ctxt.getSharedPreferences("CMessageList", Context.MODE_PRIVATE).edit();
|
||||||
|
SharedPreferences.Editor e4 = ctxt.getSharedPreferences("QueryLog", Context.MODE_PRIVATE).edit();
|
||||||
|
|
||||||
|
e1.clear();
|
||||||
|
for (Map.Entry<String, ?> entry : d1.entrySet())
|
||||||
|
{
|
||||||
|
if (entry.getValue() instanceof String) e1.putString(entry.getKey(), (String)entry.getValue());
|
||||||
|
if (entry.getValue() instanceof Boolean) e1.putBoolean(entry.getKey(), (Boolean)entry.getValue());
|
||||||
|
if (entry.getValue() instanceof Float) e1.putFloat(entry.getKey(), (Float)entry.getValue());
|
||||||
|
if (entry.getValue() instanceof Integer) e1.putInt(entry.getKey(), (Integer)entry.getValue());
|
||||||
|
if (entry.getValue() instanceof Long) e1.putLong(entry.getKey(), (Long)entry.getValue());
|
||||||
|
if (entry.getValue() instanceof Set<?>) e1.putStringSet(entry.getKey(), (Set<String>)entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
e2.clear();
|
||||||
|
for (Map.Entry<String, ?> entry : d2.entrySet())
|
||||||
|
{
|
||||||
|
if (entry.getValue() instanceof String) e2.putString(entry.getKey(), (String)entry.getValue());
|
||||||
|
if (entry.getValue() instanceof Boolean) e2.putBoolean(entry.getKey(), (Boolean)entry.getValue());
|
||||||
|
if (entry.getValue() instanceof Float) e2.putFloat(entry.getKey(), (Float)entry.getValue());
|
||||||
|
if (entry.getValue() instanceof Integer) e2.putInt(entry.getKey(), (Integer)entry.getValue());
|
||||||
|
if (entry.getValue() instanceof Long) e2.putLong(entry.getKey(), (Long)entry.getValue());
|
||||||
|
if (entry.getValue() instanceof Set<?>) e2.putStringSet(entry.getKey(), (Set<String>)entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
e2.clear();
|
||||||
|
for (Map.Entry<String, ?> entry : d3.entrySet())
|
||||||
|
{
|
||||||
|
if (entry.getValue() instanceof String) e3.putString(entry.getKey(), (String)entry.getValue());
|
||||||
|
if (entry.getValue() instanceof Boolean) e3.putBoolean(entry.getKey(), (Boolean)entry.getValue());
|
||||||
|
if (entry.getValue() instanceof Float) e3.putFloat(entry.getKey(), (Float)entry.getValue());
|
||||||
|
if (entry.getValue() instanceof Integer) e3.putInt(entry.getKey(), (Integer)entry.getValue());
|
||||||
|
if (entry.getValue() instanceof Long) e3.putLong(entry.getKey(), (Long)entry.getValue());
|
||||||
|
if (entry.getValue() instanceof Set<?>) e3.putStringSet(entry.getKey(), (Set<String>)entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
e4.clear();
|
||||||
|
for (Map.Entry<String, ?> entry : d4.entrySet())
|
||||||
|
{
|
||||||
|
if (entry.getValue() instanceof String) e4.putString(entry.getKey(), (String)entry.getValue());
|
||||||
|
if (entry.getValue() instanceof Boolean) e4.putBoolean(entry.getKey(), (Boolean)entry.getValue());
|
||||||
|
if (entry.getValue() instanceof Float) e4.putFloat(entry.getKey(), (Float)entry.getValue());
|
||||||
|
if (entry.getValue() instanceof Integer) e4.putInt(entry.getKey(), (Integer)entry.getValue());
|
||||||
|
if (entry.getValue() instanceof Long) e4.putLong(entry.getKey(), (Long)entry.getValue());
|
||||||
|
if (entry.getValue() instanceof Set<?>) e4.putStringSet(entry.getKey(), (Set<String>)entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
e1.apply();
|
||||||
|
e2.apply();
|
||||||
|
e3.apply();
|
||||||
|
e4.apply();
|
||||||
|
|
||||||
|
|
||||||
|
SCNSettings.inst().reloadPrefs();
|
||||||
|
IABService.inst().reloadPrefs();
|
||||||
|
CMessageList.inst().reloadPrefs();
|
||||||
|
QueryLog.inst().reloadPrefs();
|
||||||
|
|
||||||
|
|
||||||
|
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||||
|
setSupportActionBar(toolbar);
|
||||||
|
|
||||||
|
ViewPager viewPager = findViewById(R.id.pager);
|
||||||
|
PagerAdapter adapter = adpTabs = new TabAdapter(getSupportFragmentManager());
|
||||||
|
viewPager.setAdapter(adapter);
|
||||||
|
|
||||||
|
TabLayout tabLayout = findViewById(R.id.tab_layout);
|
||||||
|
tabLayout.setupWithViewPager(viewPager);
|
||||||
|
|
||||||
|
|
||||||
|
SCNSettings.inst().work(this);
|
||||||
|
|
||||||
|
SCNApp.showToast("Backup imported", Toast.LENGTH_LONG);
|
||||||
|
|
||||||
|
finish();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.e("Import:Err", e.toString());
|
||||||
|
SCNApp.showToast("Import failed", Toast.LENGTH_LONG);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
package com.blackforestbytes.simplecloudnotifier.view;
|
package com.blackforestbytes.simplecloudnotifier.view;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.graphics.Color;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@@ -8,8 +10,11 @@ import android.widget.RelativeLayout;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import com.blackforestbytes.simplecloudnotifier.R;
|
import com.blackforestbytes.simplecloudnotifier.R;
|
||||||
|
import com.blackforestbytes.simplecloudnotifier.SCNApp;
|
||||||
import com.blackforestbytes.simplecloudnotifier.model.CMessage;
|
import com.blackforestbytes.simplecloudnotifier.model.CMessage;
|
||||||
import com.blackforestbytes.simplecloudnotifier.model.CMessageList;
|
import com.blackforestbytes.simplecloudnotifier.model.CMessageList;
|
||||||
|
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
|
||||||
|
import com.google.android.material.button.MaterialButton;
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -52,12 +57,11 @@ public class MessageAdapter extends RecyclerView.Adapter
|
|||||||
{
|
{
|
||||||
CMessage msg = CMessageList.inst().tryGetFromBack(position);
|
CMessage msg = CMessageList.inst().tryGetFromBack(position);
|
||||||
MessagePresenter view = (MessagePresenter) holder;
|
MessagePresenter view = (MessagePresenter) holder;
|
||||||
view.setMessage(msg);
|
view.setMessage(msg, position);
|
||||||
|
|
||||||
viewHolders.put(view, true);
|
viewHolders.put(view, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onViewRecycled(@NonNull RecyclerView.ViewHolder holder)
|
public void onViewRecycled(@NonNull RecyclerView.ViewHolder holder)
|
||||||
{
|
{
|
||||||
@@ -87,16 +91,17 @@ public class MessageAdapter extends RecyclerView.Adapter
|
|||||||
manLayout.smoothScrollToPosition(viewRecycler, null, 0);
|
manLayout.smoothScrollToPosition(viewRecycler, null, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeItem(int position)
|
public CMessage removeItem(int position)
|
||||||
{
|
{
|
||||||
CMessageList.inst().remove(position);
|
CMessage i = CMessageList.inst().removeFromBack(position);
|
||||||
notifyItemRemoved(position);
|
notifyDataSetChanged();
|
||||||
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void restoreItem(CMessage item, int position)
|
public void restoreItem(CMessage item, int position)
|
||||||
{
|
{
|
||||||
CMessageList.inst().insert(position, item);
|
CMessageList.inst().insert(position, item);
|
||||||
notifyItemInserted(position);
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class MessagePresenter extends RecyclerView.ViewHolder implements View.OnClickListener
|
public class MessagePresenter extends RecyclerView.ViewHolder implements View.OnClickListener
|
||||||
@@ -109,7 +114,11 @@ public class MessageAdapter extends RecyclerView.Adapter
|
|||||||
public RelativeLayout viewForeground;
|
public RelativeLayout viewForeground;
|
||||||
public RelativeLayout viewBackground;
|
public RelativeLayout viewBackground;
|
||||||
|
|
||||||
|
public MaterialButton btnShare;
|
||||||
|
public MaterialButton btnDelete;
|
||||||
|
|
||||||
private CMessage data;
|
private CMessage data;
|
||||||
|
private int datapos;
|
||||||
|
|
||||||
MessagePresenter(View itemView)
|
MessagePresenter(View itemView)
|
||||||
{
|
{
|
||||||
@@ -120,6 +129,8 @@ public class MessageAdapter extends RecyclerView.Adapter
|
|||||||
ivPriority = itemView.findViewById(R.id.ivPriority);
|
ivPriority = itemView.findViewById(R.id.ivPriority);
|
||||||
viewForeground = itemView.findViewById(R.id.layoutFront);
|
viewForeground = itemView.findViewById(R.id.layoutFront);
|
||||||
viewBackground = itemView.findViewById(R.id.layoutBack);
|
viewBackground = itemView.findViewById(R.id.layoutBack);
|
||||||
|
btnShare = itemView.findViewById(R.id.btnShare);
|
||||||
|
btnDelete = itemView.findViewById(R.id.btnDelete);
|
||||||
|
|
||||||
itemView.setOnClickListener(this);
|
itemView.setOnClickListener(this);
|
||||||
tvTimestamp.setOnClickListener(this);
|
tvTimestamp.setOnClickListener(this);
|
||||||
@@ -127,9 +138,22 @@ public class MessageAdapter extends RecyclerView.Adapter
|
|||||||
tvMessage.setOnClickListener(this);
|
tvMessage.setOnClickListener(this);
|
||||||
ivPriority.setOnClickListener(this);
|
ivPriority.setOnClickListener(this);
|
||||||
viewForeground.setOnClickListener(this);
|
viewForeground.setOnClickListener(this);
|
||||||
|
|
||||||
|
btnShare.setOnClickListener(v ->
|
||||||
|
{
|
||||||
|
if (data == null) return;
|
||||||
|
Intent sharingIntent = new Intent(android.content.Intent.ACTION_SEND);
|
||||||
|
sharingIntent.setType("text/plain");
|
||||||
|
sharingIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, data.Title);
|
||||||
|
sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, data.Content);
|
||||||
|
SCNApp.getMainActivity().startActivity(Intent.createChooser(sharingIntent, "Share message"));
|
||||||
|
|
||||||
|
});
|
||||||
|
btnDelete.setOnClickListener(v -> { if (data != null) SCNApp.getMainActivity().adpTabs.tab1.deleteMessage(datapos); });
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setMessage(CMessage msg)
|
void setMessage(CMessage msg, int pos)
|
||||||
{
|
{
|
||||||
tvTimestamp.setText(msg.formatTimestamp());
|
tvTimestamp.setText(msg.formatTimestamp());
|
||||||
tvTitle.setText(msg.Title);
|
tvTitle.setText(msg.Title);
|
||||||
@@ -140,32 +164,63 @@ public class MessageAdapter extends RecyclerView.Adapter
|
|||||||
case LOW:
|
case LOW:
|
||||||
ivPriority.setVisibility(View.VISIBLE);
|
ivPriority.setVisibility(View.VISIBLE);
|
||||||
ivPriority.setImageResource(R.drawable.priority_low);
|
ivPriority.setImageResource(R.drawable.priority_low);
|
||||||
|
ivPriority.setColorFilter(Color.rgb(176, 176, 176));
|
||||||
break;
|
break;
|
||||||
case NORMAL:
|
case NORMAL:
|
||||||
ivPriority.setVisibility(View.GONE);
|
ivPriority.setVisibility(View.GONE);
|
||||||
|
ivPriority.setColorFilter(Color.rgb(176, 176, 176));
|
||||||
break;
|
break;
|
||||||
case HIGH:
|
case HIGH:
|
||||||
ivPriority.setVisibility(View.VISIBLE);
|
ivPriority.setVisibility(View.VISIBLE);
|
||||||
ivPriority.setImageResource(R.drawable.priority_high);
|
ivPriority.setImageResource(R.drawable.priority_high);
|
||||||
|
ivPriority.setColorFilter(Color.rgb(200, 0, 0));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
data = msg;
|
data = msg;
|
||||||
|
datapos = pos;
|
||||||
|
|
||||||
|
if (msg.IsExpandedInAdapter) expand(true); else collapse(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void expand(boolean force)
|
||||||
|
{
|
||||||
|
if (data != null && data.IsExpandedInAdapter && !force) return;
|
||||||
|
if (data != null) data.IsExpandedInAdapter = true;
|
||||||
|
if (tvMessage != null) tvMessage.setMaxLines(9999);
|
||||||
|
if (btnDelete != null) btnDelete.setVisibility(View.VISIBLE);
|
||||||
|
if (btnShare != null) btnShare.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private int norm(int i) { return (i<=0)?0:((i>9999)?9999:i); }
|
||||||
|
|
||||||
|
private void collapse(boolean force)
|
||||||
|
{
|
||||||
|
if (data != null && !data.IsExpandedInAdapter && !force) return;
|
||||||
|
if (data != null) data.IsExpandedInAdapter = false;
|
||||||
|
if (tvMessage != null) tvMessage.setMaxLines(norm(SCNSettings.inst().PreviewLineCount));
|
||||||
|
if (btnDelete != null) btnDelete.setVisibility(View.GONE);
|
||||||
|
if (btnShare != null) btnShare.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v)
|
public void onClick(View v)
|
||||||
{
|
{
|
||||||
|
if (data.IsExpandedInAdapter)
|
||||||
|
{
|
||||||
|
collapse(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (MessagePresenter holder : MessageAdapter.this.viewHolders.keySet())
|
for (MessagePresenter holder : MessageAdapter.this.viewHolders.keySet())
|
||||||
{
|
{
|
||||||
if (holder == null) continue;
|
if (holder == null) continue;
|
||||||
if (holder == this) continue;
|
if (holder == this) continue;
|
||||||
if (holder.tvMessage == null) continue;
|
holder.collapse(false);
|
||||||
if (holder.tvMessage.getMaxLines() == 6) continue;
|
|
||||||
holder.tvMessage.setMaxLines(6);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tvMessage.setMaxLines(9999);
|
expand(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -9,7 +9,6 @@ import android.view.ViewGroup;
|
|||||||
import com.blackforestbytes.simplecloudnotifier.R;
|
import com.blackforestbytes.simplecloudnotifier.R;
|
||||||
import com.blackforestbytes.simplecloudnotifier.SCNApp;
|
import com.blackforestbytes.simplecloudnotifier.SCNApp;
|
||||||
import com.blackforestbytes.simplecloudnotifier.model.CMessage;
|
import com.blackforestbytes.simplecloudnotifier.model.CMessage;
|
||||||
import com.blackforestbytes.simplecloudnotifier.model.CMessageList;
|
|
||||||
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
|
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
|
||||||
import com.blackforestbytes.simplecloudnotifier.service.IABService;
|
import com.blackforestbytes.simplecloudnotifier.service.IABService;
|
||||||
import com.blackforestbytes.simplecloudnotifier.util.MessageAdapterTouchHelper;
|
import com.blackforestbytes.simplecloudnotifier.util.MessageAdapterTouchHelper;
|
||||||
@@ -28,6 +27,8 @@ public class NotificationsFragment extends Fragment implements MessageAdapterTou
|
|||||||
private PublisherAdView adView;
|
private PublisherAdView adView;
|
||||||
private MessageAdapter adpMessages;
|
private MessageAdapter adpMessages;
|
||||||
|
|
||||||
|
public MessageAdapterTouchHelper touchHelper;
|
||||||
|
|
||||||
public NotificationsFragment()
|
public NotificationsFragment()
|
||||||
{
|
{
|
||||||
// Required empty public constructor
|
// Required empty public constructor
|
||||||
@@ -43,7 +44,7 @@ public class NotificationsFragment extends Fragment implements MessageAdapterTou
|
|||||||
rvMessages.setLayoutManager(lman);
|
rvMessages.setLayoutManager(lman);
|
||||||
rvMessages.setAdapter(adpMessages = new MessageAdapter(v.findViewById(R.id.tvNoElements), lman, rvMessages));
|
rvMessages.setAdapter(adpMessages = new MessageAdapter(v.findViewById(R.id.tvNoElements), lman, rvMessages));
|
||||||
|
|
||||||
ItemTouchHelper.SimpleCallback itemTouchHelperCallback = new MessageAdapterTouchHelper(0, ItemTouchHelper.LEFT, this);
|
ItemTouchHelper.SimpleCallback itemTouchHelperCallback = touchHelper = new MessageAdapterTouchHelper(0, ItemTouchHelper.LEFT, this);
|
||||||
new ItemTouchHelper(itemTouchHelperCallback).attachToRecyclerView(rvMessages);
|
new ItemTouchHelper(itemTouchHelperCallback).attachToRecyclerView(rvMessages);
|
||||||
|
|
||||||
adView = v.findViewById(R.id.adBanner);
|
adView = v.findViewById(R.id.adBanner);
|
||||||
@@ -57,7 +58,7 @@ public class NotificationsFragment extends Fragment implements MessageAdapterTou
|
|||||||
|
|
||||||
public void updateProState()
|
public void updateProState()
|
||||||
{
|
{
|
||||||
if (adView != null) adView.setVisibility(IABService.inst().getPurchaseCached(IABService.IAB_PRO_MODE) != null ? View.GONE : View.VISIBLE);
|
if (adView != null) adView.setVisibility(IABService.inst().getPurchaseCachedSimple(IABService.IAB_PRO_MODE) ? View.GONE : View.VISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -65,17 +66,25 @@ public class NotificationsFragment extends Fragment implements MessageAdapterTou
|
|||||||
{
|
{
|
||||||
if (viewHolder instanceof MessageAdapter.MessagePresenter)
|
if (viewHolder instanceof MessageAdapter.MessagePresenter)
|
||||||
{
|
{
|
||||||
final CMessage deletedItem = CMessageList.inst().tryGet(viewHolder.getAdapterPosition());
|
deleteMessage(viewHolder.getAdapterPosition());
|
||||||
final int deletedIndex = viewHolder.getAdapterPosition();
|
|
||||||
|
|
||||||
String name = deletedItem.Title;
|
|
||||||
|
|
||||||
adpMessages.removeItem(viewHolder.getAdapterPosition());
|
|
||||||
|
|
||||||
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 deleteMessage(int pos)
|
||||||
|
{
|
||||||
|
final int deletedIndex = pos;
|
||||||
|
|
||||||
|
final CMessage deletedItem = adpMessages.removeItem(pos);
|
||||||
|
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,6 +1,9 @@
|
|||||||
package com.blackforestbytes.simplecloudnotifier.view;
|
package com.blackforestbytes.simplecloudnotifier.view;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.media.AudioAttributes;
|
import android.media.AudioAttributes;
|
||||||
import android.media.AudioManager;
|
import android.media.AudioManager;
|
||||||
@@ -10,6 +13,8 @@ import android.media.RingtoneManager;
|
|||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.Environment;
|
||||||
|
import android.text.Editable;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -17,13 +22,16 @@ import android.view.ViewGroup;
|
|||||||
import android.widget.AdapterView;
|
import android.widget.AdapterView;
|
||||||
import android.widget.ArrayAdapter;
|
import android.widget.ArrayAdapter;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.SeekBar;
|
import android.widget.SeekBar;
|
||||||
import android.widget.Spinner;
|
import android.widget.Spinner;
|
||||||
import android.widget.Switch;
|
import android.widget.Switch;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.android.billingclient.api.Purchase;
|
import com.android.billingclient.api.Purchase;
|
||||||
|
import com.blackforestbytes.simplecloudnotifier.BuildConfig;
|
||||||
import com.blackforestbytes.simplecloudnotifier.R;
|
import com.blackforestbytes.simplecloudnotifier.R;
|
||||||
import com.blackforestbytes.simplecloudnotifier.SCNApp;
|
import com.blackforestbytes.simplecloudnotifier.SCNApp;
|
||||||
import com.blackforestbytes.simplecloudnotifier.lib.android.ThreadUtils;
|
import com.blackforestbytes.simplecloudnotifier.lib.android.ThreadUtils;
|
||||||
@@ -31,12 +39,19 @@ import com.blackforestbytes.simplecloudnotifier.lib.lambda.FI;
|
|||||||
import com.blackforestbytes.simplecloudnotifier.lib.string.Str;
|
import com.blackforestbytes.simplecloudnotifier.lib.string.Str;
|
||||||
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
|
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
|
||||||
import com.blackforestbytes.simplecloudnotifier.service.IABService;
|
import com.blackforestbytes.simplecloudnotifier.service.IABService;
|
||||||
|
import com.blackforestbytes.simplecloudnotifier.util.TextChangedListener;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.ObjectOutputStream;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.core.content.FileProvider;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import top.defaults.colorpicker.ColorPickerPopup;
|
import top.defaults.colorpicker.ColorPickerPopup;
|
||||||
import xyz.aprildown.ultimatemusicpicker.MusicPickerListener;
|
import xyz.aprildown.ultimatemusicpicker.MusicPickerListener;
|
||||||
@@ -49,6 +64,8 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
|
|||||||
private Button prefUpgradeAccount;
|
private Button prefUpgradeAccount;
|
||||||
private TextView prefUpgradeAccount_msg;
|
private TextView prefUpgradeAccount_msg;
|
||||||
private TextView prefUpgradeAccount_info;
|
private TextView prefUpgradeAccount_info;
|
||||||
|
private Switch prefEnableDeleteSwipe;
|
||||||
|
private EditText prefPreviewLineCount;
|
||||||
|
|
||||||
private Switch prefMsgLowEnableSound;
|
private Switch prefMsgLowEnableSound;
|
||||||
private TextView prefMsgLowRingtone_value;
|
private TextView prefMsgLowRingtone_value;
|
||||||
@@ -86,6 +103,9 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
|
|||||||
private SeekBar prefMsgHighVolume;
|
private SeekBar prefMsgHighVolume;
|
||||||
private ImageView prefMsgHighVolumeTest;
|
private ImageView prefMsgHighVolumeTest;
|
||||||
|
|
||||||
|
private Button prefBtnImport;
|
||||||
|
private Button prefBtnExport;
|
||||||
|
|
||||||
private int musicPickerSwitch = -1;
|
private int musicPickerSwitch = -1;
|
||||||
|
|
||||||
private MediaPlayer[] mPlayers = new MediaPlayer[3];
|
private MediaPlayer[] mPlayers = new MediaPlayer[3];
|
||||||
@@ -114,6 +134,8 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
|
|||||||
prefUpgradeAccount = v.findViewById(R.id.prefUpgradeAccount);
|
prefUpgradeAccount = v.findViewById(R.id.prefUpgradeAccount);
|
||||||
prefUpgradeAccount_msg = v.findViewById(R.id.prefUpgradeAccount2);
|
prefUpgradeAccount_msg = v.findViewById(R.id.prefUpgradeAccount2);
|
||||||
prefUpgradeAccount_info = v.findViewById(R.id.prefUpgradeAccount_info);
|
prefUpgradeAccount_info = v.findViewById(R.id.prefUpgradeAccount_info);
|
||||||
|
prefEnableDeleteSwipe = v.findViewById(R.id.prefEnableDeleteSwipe);
|
||||||
|
prefPreviewLineCount = v.findViewById(R.id.prefPreviewLineCount);
|
||||||
|
|
||||||
prefMsgLowEnableSound = v.findViewById(R.id.prefMsgLowEnableSound);
|
prefMsgLowEnableSound = v.findViewById(R.id.prefMsgLowEnableSound);
|
||||||
prefMsgLowRingtone_value = v.findViewById(R.id.prefMsgLowRingtone_value);
|
prefMsgLowRingtone_value = v.findViewById(R.id.prefMsgLowRingtone_value);
|
||||||
@@ -147,11 +169,19 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
|
|||||||
prefMsgHighLedColor_value = v.findViewById(R.id.prefMsgHighLedColor_value);
|
prefMsgHighLedColor_value = v.findViewById(R.id.prefMsgHighLedColor_value);
|
||||||
prefMsgHighLedColor_container = v.findViewById(R.id.prefMsgHighLedColor_container);
|
prefMsgHighLedColor_container = v.findViewById(R.id.prefMsgHighLedColor_container);
|
||||||
prefMsgHighEnableVibrations = v.findViewById(R.id.prefMsgHighEnableVibrations);
|
prefMsgHighEnableVibrations = v.findViewById(R.id.prefMsgHighEnableVibrations);
|
||||||
prefMsgHighForceVolume = v.findViewById(R.id.prefMsgHighForceVolume);
|
prefMsgHighForceVolume = v.findViewById(R.id.prefMsgHighForceVolume);
|
||||||
prefMsgHighVolume = v.findViewById(R.id.prefMsgHighVolume);
|
prefMsgHighVolume = v.findViewById(R.id.prefMsgHighVolume);
|
||||||
prefMsgHighVolumeTest = v.findViewById(R.id.btnHighVolumeTest);
|
prefMsgHighVolumeTest = v.findViewById(R.id.btnHighVolumeTest);
|
||||||
|
|
||||||
|
prefBtnExport = v.findViewById(R.id.prefExport);
|
||||||
|
prefBtnImport = v.findViewById(R.id.prefImport);
|
||||||
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("SetTextI18n")
|
||||||
private void updateUI()
|
private void updateUI()
|
||||||
{
|
{
|
||||||
SCNSettings s = SCNSettings.inst();
|
SCNSettings s = SCNSettings.inst();
|
||||||
@@ -159,15 +189,14 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
|
|||||||
if (c == null) return;
|
if (c == null) return;
|
||||||
|
|
||||||
if (prefAppEnabled.isChecked() != s.Enabled) prefAppEnabled.setChecked(s.Enabled);
|
if (prefAppEnabled.isChecked() != s.Enabled) prefAppEnabled.setChecked(s.Enabled);
|
||||||
|
if (prefEnableDeleteSwipe.isChecked() != s.EnableDeleteSwipe) prefEnableDeleteSwipe.setChecked(s.EnableDeleteSwipe);
|
||||||
|
if (!prefPreviewLineCount.getText().toString().equals(Integer.toString(s.PreviewLineCount))) prefPreviewLineCount.setText(Integer.toString(s.PreviewLineCount));
|
||||||
|
|
||||||
prefUpgradeAccount.setVisibility( SCNSettings.inst().promode_local ? View.GONE : View.VISIBLE);
|
prefUpgradeAccount.setVisibility( SCNSettings.inst().promode_local ? View.GONE : View.VISIBLE);
|
||||||
prefUpgradeAccount_info.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 );
|
prefUpgradeAccount_msg.setVisibility( SCNSettings.inst().promode_local ? View.VISIBLE : View.GONE );
|
||||||
|
|
||||||
ArrayAdapter<Integer> plcsa = new ArrayAdapter<>(c, android.R.layout.simple_spinner_item, SCNSettings.CHOOSABLE_CACHE_SIZES);
|
if (prefLocalCacheSize.getSelectedItemPosition() != getCacheSizeIndex(s.LocalCacheSize)) prefLocalCacheSize.setSelection(getCacheSizeIndex(s.LocalCacheSize));
|
||||||
plcsa.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
|
||||||
prefLocalCacheSize.setAdapter(plcsa);
|
|
||||||
prefLocalCacheSize.setSelection(getCacheSizeIndex(s.LocalCacheSize));
|
|
||||||
|
|
||||||
if (prefMsgLowEnableSound.isChecked() != s.PriorityLow.EnableSound) prefMsgLowEnableSound.setChecked(s.PriorityLow.EnableSound);
|
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 (!prefMsgLowRingtone_value.getText().equals(s.PriorityLow.SoundName)) prefMsgLowRingtone_value.setText(s.PriorityLow.SoundName);
|
||||||
@@ -213,7 +242,14 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
|
|||||||
{
|
{
|
||||||
SCNSettings s = SCNSettings.inst();
|
SCNSettings s = SCNSettings.inst();
|
||||||
|
|
||||||
prefAppEnabled.setOnCheckedChangeListener((a,b) -> { s.Enabled=b; saveAndUpdate(); });
|
prefAppEnabled.setOnCheckedChangeListener((a,b) -> { boolean prev=s.Enabled; s.Enabled=b; saveAndUpdate(); updateEnabled(prev, b); });
|
||||||
|
prefEnableDeleteSwipe.setOnCheckedChangeListener((a,b) -> { s.EnableDeleteSwipe=b; saveAndUpdate(); });
|
||||||
|
prefPreviewLineCount.addTextChangedListener(new TextChangedListener<EditText>(prefPreviewLineCount) {
|
||||||
|
@Override
|
||||||
|
public void onTextChanged(EditText target, Editable ed) {
|
||||||
|
if (!ed.toString().isEmpty()) try { s.PreviewLineCount=Integer.parseInt(ed.toString()); saveAndUpdate(); } catch (Exception e) { /* */ }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
prefLocalCacheSize.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener()
|
prefLocalCacheSize.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener()
|
||||||
{
|
{
|
||||||
@@ -226,6 +262,9 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
|
|||||||
|
|
||||||
prefUpgradeAccount.setOnClickListener(a -> onUpgradeAccount());
|
prefUpgradeAccount.setOnClickListener(a -> onUpgradeAccount());
|
||||||
|
|
||||||
|
prefBtnExport.setOnClickListener(a -> onExport());
|
||||||
|
prefBtnImport.setOnClickListener(a -> onImport());
|
||||||
|
|
||||||
prefMsgLowEnableSound.setOnCheckedChangeListener((a,b) -> { s.PriorityLow.EnableSound=b; saveAndUpdate(); });
|
prefMsgLowEnableSound.setOnCheckedChangeListener((a,b) -> { s.PriorityLow.EnableSound=b; saveAndUpdate(); });
|
||||||
prefMsgLowRingtone_container.setOnClickListener(a -> chooseRingtoneLow());
|
prefMsgLowRingtone_container.setOnClickListener(a -> chooseRingtoneLow());
|
||||||
prefMsgLowRepeatSound.setOnCheckedChangeListener((a,b) -> { s.PriorityLow.RepeatSound=b; saveAndUpdate(); });
|
prefMsgLowRepeatSound.setOnCheckedChangeListener((a,b) -> { s.PriorityLow.RepeatSound=b; saveAndUpdate(); });
|
||||||
@@ -257,6 +296,67 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
|
|||||||
prefMsgHighVolumeTest.setOnClickListener((v) -> { if (s.PriorityHigh.ForceVolume) playTestSound(2, prefMsgHighVolumeTest, s.PriorityHigh.SoundSource, s.PriorityHigh.ForceVolumeValue); });
|
prefMsgHighVolumeTest.setOnClickListener((v) -> { if (s.PriorityHigh.ForceVolume) playTestSound(2, prefMsgHighVolumeTest, s.PriorityHigh.SoundSource, s.PriorityHigh.ForceVolumeValue); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onExport()
|
||||||
|
{
|
||||||
|
Context ctxt = getContext();
|
||||||
|
if (ctxt == null) return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
File outputDir = ctxt.getCacheDir(); // context being the Activity pointer
|
||||||
|
File outputFile = File.createTempFile("scn_export_", ".dat", outputDir);
|
||||||
|
|
||||||
|
ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream(outputFile));
|
||||||
|
|
||||||
|
Map<String, ?> d1 = ctxt.getSharedPreferences("Config", Context.MODE_PRIVATE).getAll();
|
||||||
|
Map<String, ?> d2 = ctxt.getSharedPreferences("IAB", Context.MODE_PRIVATE).getAll();
|
||||||
|
Map<String, ?> d3 = ctxt.getSharedPreferences("CMessageList", Context.MODE_PRIVATE).getAll();
|
||||||
|
Map<String, ?> d4 = ctxt.getSharedPreferences("QueryLog", Context.MODE_PRIVATE).getAll();
|
||||||
|
|
||||||
|
output.writeObject(d1);
|
||||||
|
output.writeObject(d2);
|
||||||
|
output.writeObject(d3);
|
||||||
|
output.writeObject(d4);
|
||||||
|
|
||||||
|
Intent intent = new Intent(Intent.ACTION_SEND);
|
||||||
|
|
||||||
|
Uri uri = FileProvider.getUriForFile(ctxt, "com.blackforestbytes.simplecloudnotifier.fileprovider", outputFile);
|
||||||
|
intent.putExtra(Intent.EXTRA_STREAM, uri);
|
||||||
|
intent.setType("*/*");
|
||||||
|
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||||
|
|
||||||
|
startActivity(Intent.createChooser(intent, "Export"));
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
Log.e("Export:Err", e.toString());
|
||||||
|
SCNApp.showToast("Export failed", Toast.LENGTH_LONG);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onImport()
|
||||||
|
{
|
||||||
|
SCNApp.getMainActivity().setContentView(R.layout.activity_main);
|
||||||
|
|
||||||
|
Intent intent = new Intent()
|
||||||
|
.setType("*/*")
|
||||||
|
.setAction(Intent.ACTION_GET_CONTENT);
|
||||||
|
|
||||||
|
((MainActivity)getActivity()).startActivityForResult(Intent.createChooser(intent, "Select a file"), 1991);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateEnabled(boolean prev, boolean now)
|
||||||
|
{
|
||||||
|
if (!prev && now)
|
||||||
|
{
|
||||||
|
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)
|
private void updateVolume(int idx, int volume)
|
||||||
{
|
{
|
||||||
if (mPlayers[idx] != null && mPlayers[idx].isPlaying())
|
if (mPlayers[idx] != null && mPlayers[idx].isPlaying())
|
||||||
@@ -324,6 +424,7 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
|
|||||||
{
|
{
|
||||||
SCNSettings.inst().save();
|
SCNSettings.inst().save();
|
||||||
updateUI();
|
updateUI();
|
||||||
|
SCNApp.getMainActivity().adpTabs.tab1.updateDeleteSwipeEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onUpgradeAccount()
|
private void onUpgradeAccount()
|
||||||
@@ -333,11 +434,11 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
|
|||||||
|
|
||||||
public void updateProState()
|
public void updateProState()
|
||||||
{
|
{
|
||||||
Purchase p = IABService.inst().getPurchaseCached(IABService.IAB_PRO_MODE);
|
boolean pmode = IABService.inst().getPurchaseCachedSimple(IABService.IAB_PRO_MODE);
|
||||||
|
|
||||||
if (prefUpgradeAccount != null) prefUpgradeAccount.setVisibility( p != null ? View.GONE : View.VISIBLE);
|
if (prefUpgradeAccount != null) prefUpgradeAccount.setVisibility( pmode ? View.GONE : View.VISIBLE);
|
||||||
if (prefUpgradeAccount_info != null) prefUpgradeAccount_info.setVisibility(p != null ? View.GONE : View.VISIBLE);
|
if (prefUpgradeAccount_info != null) prefUpgradeAccount_info.setVisibility(pmode ? View.GONE : View.VISIBLE);
|
||||||
if (prefUpgradeAccount_msg != null) prefUpgradeAccount_msg.setVisibility( p != null ? View.VISIBLE : View.GONE );
|
if (prefUpgradeAccount_msg != null) prefUpgradeAccount_msg.setVisibility( pmode ? View.VISIBLE : View.GONE );
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getCacheSizeIndex(int value)
|
private int getCacheSizeIndex(int value)
|
||||||
|
@@ -35,7 +35,7 @@ public class TabAdapter extends FragmentStatePagerAdapter {
|
|||||||
{
|
{
|
||||||
switch (position)
|
switch (position)
|
||||||
{
|
{
|
||||||
case 0: return "Notifications";
|
case 0: return "Messages";
|
||||||
case 1: return "Account";
|
case 1: return "Account";
|
||||||
case 2: return "Settings";
|
case 2: return "Settings";
|
||||||
default: return null;
|
default: return null;
|
||||||
|
@@ -0,0 +1,44 @@
|
|||||||
|
package com.blackforestbytes.simplecloudnotifier.view.debug;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import com.blackforestbytes.simplecloudnotifier.model.QueryLog;
|
||||||
|
import com.blackforestbytes.simplecloudnotifier.model.SingleQuery;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
|
||||||
|
import android.widget.ListView;
|
||||||
|
|
||||||
|
import com.blackforestbytes.simplecloudnotifier.R;
|
||||||
|
|
||||||
|
public class QueryLogActivity extends AppCompatActivity
|
||||||
|
{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState)
|
||||||
|
{
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_querylog);
|
||||||
|
|
||||||
|
ListView lvMain = findViewById(R.id.lvQueryList);
|
||||||
|
SingleQuery[] arr = QueryLog.inst().get().toArray(new SingleQuery[0]);
|
||||||
|
QueryLogAdapter a = new QueryLogAdapter(this, arr);
|
||||||
|
lvMain.setAdapter(a);
|
||||||
|
|
||||||
|
lvMain.setOnItemClickListener((parent, view, position, id) ->
|
||||||
|
{
|
||||||
|
if (position >= 0 && position < arr.length)
|
||||||
|
{
|
||||||
|
Intent i = new Intent(QueryLogActivity.this, SingleQueryLogActivity.class);
|
||||||
|
Bundle b = new Bundle();
|
||||||
|
arr[position].save(b, "data");
|
||||||
|
i.putExtra("query", b);
|
||||||
|
startActivity(i);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,66 @@
|
|||||||
|
package com.blackforestbytes.simplecloudnotifier.view.debug;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.blackforestbytes.simplecloudnotifier.R;
|
||||||
|
import com.blackforestbytes.simplecloudnotifier.model.SingleQuery;
|
||||||
|
|
||||||
|
import org.joda.time.format.DateTimeFormat;
|
||||||
|
import org.joda.time.format.DateTimeFormatter;
|
||||||
|
|
||||||
|
public class QueryLogAdapter extends ArrayAdapter<SingleQuery>
|
||||||
|
{
|
||||||
|
public static DateTimeFormatter UI_FULLTIME_FORMATTER = DateTimeFormat.forPattern("HH:mm:ss");
|
||||||
|
|
||||||
|
public QueryLogAdapter(@NonNull Context context, @NonNull SingleQuery[] objects)
|
||||||
|
{
|
||||||
|
super(context, R.layout.adapter_querylog, objects);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public View getView(int position, View convertView, @NonNull ViewGroup parent)
|
||||||
|
{
|
||||||
|
View v = convertView;
|
||||||
|
|
||||||
|
if (v == null) {
|
||||||
|
LayoutInflater vi;
|
||||||
|
vi = LayoutInflater.from(getContext());
|
||||||
|
v = vi.inflate(R.layout.adapter_querylog, parent, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
SingleQuery p = getItem(position);
|
||||||
|
|
||||||
|
if (p != null)
|
||||||
|
{
|
||||||
|
TextView tt1 = v.findViewById(R.id.list_item_debuglogrow_time);
|
||||||
|
if (tt1 != null) tt1.setText(p.Timestamp.toString(UI_FULLTIME_FORMATTER));
|
||||||
|
if (tt1 != null) tt1.setTextColor(Color.BLACK);
|
||||||
|
|
||||||
|
TextView tt2 = v.findViewById(R.id.list_item_debuglogrow_level);
|
||||||
|
if (tt2 != null) tt2.setText(p.Level.toUIString());
|
||||||
|
if (tt2 != null) tt2.setTextColor(Color.BLACK);
|
||||||
|
|
||||||
|
TextView tt3 = v.findViewById(R.id.list_item_debuglogrow_info);
|
||||||
|
if (tt3 != null) tt3.setText("");
|
||||||
|
if (tt3 != null) tt3.setTextColor(Color.BLUE);
|
||||||
|
|
||||||
|
TextView tt4 = v.findViewById(R.id.list_item_debuglogrow_id);
|
||||||
|
if (tt4 != null) tt4.setText(p.Name);
|
||||||
|
if (tt4 != null) tt4.setTextColor(p.Level.getColor());
|
||||||
|
|
||||||
|
TextView tt5 = v.findViewById(R.id.list_item_debuglogrow_message);
|
||||||
|
if (tt5 != null) tt5.setText(p.ExceptionString.length()> 40 ? p.ExceptionString.substring(0, 40-3)+"..." : p.ExceptionString);
|
||||||
|
if (tt5 != null) tt5.setTextColor(p.Level.getColor());
|
||||||
|
}
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,38 @@
|
|||||||
|
package com.blackforestbytes.simplecloudnotifier.view.debug;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.blackforestbytes.simplecloudnotifier.R;
|
||||||
|
import com.blackforestbytes.simplecloudnotifier.lib.string.CompactJsonFormatter;
|
||||||
|
import com.blackforestbytes.simplecloudnotifier.model.SingleQuery;
|
||||||
|
|
||||||
|
import org.joda.time.format.DateTimeFormat;
|
||||||
|
import org.joda.time.format.DateTimeFormatter;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class SingleQueryLogActivity extends AppCompatActivity
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
@SuppressLint("SetTextI18n")
|
||||||
|
protected void onCreate(Bundle savedInstanceState)
|
||||||
|
{
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_singlequerylog);
|
||||||
|
|
||||||
|
SingleQuery q = SingleQuery.load(getIntent().getBundleExtra("query"), "data");
|
||||||
|
|
||||||
|
this.<TextView>findViewById(R.id.tvQL_Timestamp).setText(q.Timestamp.toString(DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")));
|
||||||
|
this.<TextView>findViewById(R.id.tvQL_Level).setText(q.Level.toUIString());
|
||||||
|
this.<TextView>findViewById(R.id.tvQL_Level).setTextColor(q.Level.getColor());
|
||||||
|
this.<TextView>findViewById(R.id.tvQL_Name).setText(q.Name);
|
||||||
|
this.<TextView>findViewById(R.id.tvQL_URL).setText(q.URL.replace("?", "\r\n?").replace("&", "\r\n&"));
|
||||||
|
this.<TextView>findViewById(R.id.tvQL_Response).setText(CompactJsonFormatter.formatJSON(q.Response, 999));
|
||||||
|
this.<TextView>findViewById(R.id.tvQL_ResponseCode).setText(Integer.toString(q.ResponseCode));
|
||||||
|
this.<TextView>findViewById(R.id.tvQL_ExceptionString).setText(q.ExceptionString);
|
||||||
|
}
|
||||||
|
}
|
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 |
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" >
|
||||||
|
<solid android:color="@android:color/white" />
|
||||||
|
<stroke android:width="1dip" android:color="#888888"/>
|
||||||
|
</shape>
|
After Width: | Height: | Size: 551 B |
After Width: | Height: | Size: 949 B |
After Width: | Height: | Size: 1.0 KiB |
@@ -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>
|
|
10
android/app/src/main/res/drawable/ic_share_small.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<vector
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="12sp"
|
||||||
|
android:height="12sp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z"/>
|
||||||
|
</vector>
|
16
android/app/src/main/res/drawable/ic_trash_small.xml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<vector
|
||||||
|
android:height="12sp"
|
||||||
|
android:width="12sp"
|
||||||
|
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>
|
@@ -1,31 +1,31 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<RelativeLayout
|
<RelativeLayout android:id="@+id/layoutRoot"
|
||||||
android:id="@+id/layoutRoot"
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:showIn="@layout/activity_main">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
<androidx.appcompat.widget.Toolbar
|
<androidx.appcompat.widget.Toolbar
|
||||||
android:id="@+id/toolbar"
|
android:id="@+id/toolbar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentTop="true"
|
android:layout_alignParentTop="true"
|
||||||
|
app:titleTextColor="@color/colorOnPrimary"
|
||||||
android:background="?attr/colorPrimary"
|
android:background="?attr/colorPrimary"
|
||||||
android:elevation="6dp"
|
android:elevation="6dp"
|
||||||
android:minHeight="?attr/actionBarSize"
|
android:minHeight="?attr/actionBarSize" />
|
||||||
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
|
|
||||||
|
|
||||||
<com.google.android.material.tabs.TabLayout
|
<com.google.android.material.tabs.TabLayout
|
||||||
android:id="@+id/tab_layout"
|
android:id="@+id/tab_layout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_below="@+id/toolbar"
|
android:layout_below="@+id/toolbar"
|
||||||
|
app:titleTextColor="@color/colorOnPrimary"
|
||||||
|
app:tabTextColor="@color/colorOnPrimary"
|
||||||
|
app:tabSelectedTextColor="@color/colorSecondary"
|
||||||
android:background="?attr/colorPrimary"
|
android:background="?attr/colorPrimary"
|
||||||
android:elevation="6dp"
|
android:elevation="6dp"
|
||||||
android:minHeight="?attr/actionBarSize"
|
android:minHeight="?attr/actionBarSize" />
|
||||||
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"/>
|
|
||||||
|
|
||||||
<androidx.viewpager.widget.ViewPager
|
<androidx.viewpager.widget.ViewPager
|
||||||
android:id="@+id/pager"
|
android:id="@+id/pager"
|
||||||
|
13
android/app/src/main/res/layout/activity_querylog.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout
|
||||||
|
android:id="@+id/layoutRoot"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<ListView
|
||||||
|
android:id="@+id/lvQueryList"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent" />
|
||||||
|
|
||||||
|
</RelativeLayout>
|
240
android/app/src/main/res/layout/activity_singlequerylog.xml
Normal file
@@ -0,0 +1,240 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:padding="4sp"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:layout_margin="2dip"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:text="Server Query" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_margin="2dip"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="100dp"
|
||||||
|
android:text="Timestamp" />
|
||||||
|
|
||||||
|
<com.blackforestbytes.simplecloudnotifier.util.MaxHeightScrollView
|
||||||
|
app:maxHeightOverride="100"
|
||||||
|
android:background="@drawable/simple_black_border"
|
||||||
|
android:layout_margin="2dip"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:fillViewport="true">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvQL_Timestamp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:padding="1dip"
|
||||||
|
android:textIsSelectable="true"
|
||||||
|
android:text="" />
|
||||||
|
|
||||||
|
</com.blackforestbytes.simplecloudnotifier.util.MaxHeightScrollView>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_margin="2dip"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="100dp"
|
||||||
|
android:text="Level" />
|
||||||
|
|
||||||
|
<com.blackforestbytes.simplecloudnotifier.util.MaxHeightScrollView
|
||||||
|
app:maxHeightOverride="100"
|
||||||
|
android:background="@drawable/simple_black_border"
|
||||||
|
android:layout_margin="2dip"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:fillViewport="true">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvQL_Level"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:padding="1dip"
|
||||||
|
android:textIsSelectable="true"
|
||||||
|
android:text="" />
|
||||||
|
|
||||||
|
</com.blackforestbytes.simplecloudnotifier.util.MaxHeightScrollView>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_margin="2dip"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="100dp"
|
||||||
|
android:text="Name" />
|
||||||
|
|
||||||
|
<com.blackforestbytes.simplecloudnotifier.util.MaxHeightScrollView
|
||||||
|
app:maxHeightOverride="100"
|
||||||
|
android:layout_margin="2dip"
|
||||||
|
android:background="@drawable/simple_black_border"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:fillViewport="true">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvQL_Name"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:padding="1dip"
|
||||||
|
android:textIsSelectable="true"
|
||||||
|
android:text="" />
|
||||||
|
|
||||||
|
</com.blackforestbytes.simplecloudnotifier.util.MaxHeightScrollView>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_margin="2dip"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="100dp"
|
||||||
|
android:text="URL" />
|
||||||
|
|
||||||
|
<com.blackforestbytes.simplecloudnotifier.util.MaxHeightScrollView
|
||||||
|
app:maxHeightOverride="100"
|
||||||
|
android:layout_margin="2dip"
|
||||||
|
android:background="@drawable/simple_black_border"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:fillViewport="true">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvQL_URL"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:padding="1dip"
|
||||||
|
android:textIsSelectable="true"
|
||||||
|
android:text=""/>
|
||||||
|
|
||||||
|
</com.blackforestbytes.simplecloudnotifier.util.MaxHeightScrollView>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_margin="2dip"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="100dp"
|
||||||
|
android:text="ResponeCode" />
|
||||||
|
|
||||||
|
<com.blackforestbytes.simplecloudnotifier.util.MaxHeightScrollView
|
||||||
|
app:maxHeightOverride="64"
|
||||||
|
android:layout_margin="2dip"
|
||||||
|
android:background="@drawable/simple_black_border"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:fillViewport="true">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvQL_ResponseCode"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:padding="1dip"
|
||||||
|
android:textIsSelectable="true"
|
||||||
|
android:text="" />
|
||||||
|
|
||||||
|
</com.blackforestbytes.simplecloudnotifier.util.MaxHeightScrollView>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_margin="2dip"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="100dp"
|
||||||
|
android:text="Response" />
|
||||||
|
|
||||||
|
|
||||||
|
<com.blackforestbytes.simplecloudnotifier.util.MaxHeightScrollView
|
||||||
|
app:maxHeightOverride="100"
|
||||||
|
android:layout_margin="2dip"
|
||||||
|
android:background="@drawable/simple_black_border"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:fillViewport="true">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvQL_Response"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:padding="1dip"
|
||||||
|
android:textIsSelectable="true"
|
||||||
|
android:text="" />
|
||||||
|
|
||||||
|
</com.blackforestbytes.simplecloudnotifier.util.MaxHeightScrollView>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_margin="2dip"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="100dp"
|
||||||
|
android:text="Exception" />
|
||||||
|
|
||||||
|
<com.blackforestbytes.simplecloudnotifier.util.MaxHeightScrollView
|
||||||
|
app:maxHeightOverride="100"
|
||||||
|
android:layout_margin="2dip"
|
||||||
|
android:background="@drawable/simple_black_border"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:fillViewport="true">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvQL_ExceptionString"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:padding="1dip"
|
||||||
|
android:textIsSelectable="true"
|
||||||
|
android:text="" />
|
||||||
|
|
||||||
|
</com.blackforestbytes.simplecloudnotifier.util.MaxHeightScrollView>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</FrameLayout>
|
50
android/app/src/main/res/layout/adapter_querylog.xml
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/list_item_imagerow"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="horizontal" >
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="96dp"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/list_item_debuglogrow_time"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/list_item_debuglogrow_level"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/list_item_debuglogrow_info"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/list_item_debuglogrow_id"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/list_item_debuglogrow_message"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
|
</LinearLayout>
|
@@ -198,19 +198,27 @@
|
|||||||
app:layout_constraintBottom_toTopOf="@+id/btnAccountReset"
|
app:layout_constraintBottom_toTopOf="@+id/btnAccountReset"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/ic_img_quota" />
|
app:layout_constraintTop_toBottomOf="@+id/ic_img_quota" />
|
||||||
|
|
||||||
<Button
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/btnAccountReset"
|
android:id="@+id/btnAccountReset"
|
||||||
|
app:cornerRadius="0dp"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:layout_marginEnd="4dp"
|
||||||
|
android:backgroundTint="#fa315b"
|
||||||
android:text="@string/str_reset_account"
|
android:text="@string/str_reset_account"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/btnClearLocalStorage" />
|
app:layout_constraintBottom_toTopOf="@+id/btnClearLocalStorage" />
|
||||||
|
|
||||||
<Button
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/btnClearLocalStorage"
|
android:id="@+id/btnClearLocalStorage"
|
||||||
|
app:cornerRadius="0dp"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="Clear Messages"
|
android:text="Clear Messages"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:layout_marginEnd="4dp"
|
||||||
|
android:backgroundTint="#607D8B"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
@@ -79,6 +79,62 @@
|
|||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</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"/>
|
||||||
|
|
||||||
|
<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/tvPreviewLineCount"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/str_previewlinecount"
|
||||||
|
android:textColor="#000"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/prefPreviewLineCount"
|
||||||
|
app:layout_constraintHorizontal_bias="0"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:minWidth="64dp"
|
||||||
|
android:id="@+id/prefPreviewLineCount"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="number"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
android:importantForAutofill="no"
|
||||||
|
tools:ignore="LabelFor,UnusedAttribute" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="1dp"
|
android:layout_height="1dp"
|
||||||
@@ -95,8 +151,10 @@
|
|||||||
android:gravity="center|center"
|
android:gravity="center|center"
|
||||||
android:minHeight="48dp">
|
android:minHeight="48dp">
|
||||||
|
|
||||||
<Button
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/prefUpgradeAccount"
|
android:id="@+id/prefUpgradeAccount"
|
||||||
|
app:cornerRadius="0dp"
|
||||||
|
android:backgroundTint="#4CAF50"
|
||||||
android:text="@string/str_upgrade_account"
|
android:text="@string/str_upgrade_account"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content" />
|
||||||
@@ -747,6 +805,24 @@
|
|||||||
|
|
||||||
</androidx.cardview.widget.CardView>
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/prefExport"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="12dp"
|
||||||
|
app:cornerRadius="0dp"
|
||||||
|
android:backgroundTint="#444444"
|
||||||
|
android:text="@string/export_settings" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/prefImport"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="12dp"
|
||||||
|
app:cornerRadius="0dp"
|
||||||
|
android:backgroundTint="#666666"
|
||||||
|
android:text="@string/import_settings" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
@@ -108,6 +108,43 @@
|
|||||||
android:paddingTop="3dp"
|
android:paddingTop="3dp"
|
||||||
android:contentDescription="@string/desc_priority_icon" />
|
android:contentDescription="@string/desc_priority_icon" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnShare"
|
||||||
|
style="@style/Widget.MaterialComponents.Button.Icon"
|
||||||
|
app:cornerRadius="0dp"
|
||||||
|
app:icon="@drawable/ic_share_small"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="25dp"
|
||||||
|
android:layout_marginEnd="8sp"
|
||||||
|
android:insetTop="0dp"
|
||||||
|
android:insetBottom="0dp"
|
||||||
|
android:insetLeft="0dp"
|
||||||
|
android:insetRight="0dp"
|
||||||
|
android:textSize="12sp"
|
||||||
|
card_view:layout_constraintTop_toBottomOf="@id/tvMessage"
|
||||||
|
app:layout_constraintRight_toLeftOf="@id/btnDelete"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
android:backgroundTint="#03A9F4"
|
||||||
|
android:text="Share" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnDelete"
|
||||||
|
style="@style/Widget.MaterialComponents.Button.Icon"
|
||||||
|
app:cornerRadius="0dp"
|
||||||
|
app:icon="@drawable/ic_trash_small"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="25dp"
|
||||||
|
android:insetTop="0dp"
|
||||||
|
android:insetBottom="0dp"
|
||||||
|
android:insetLeft="0dp"
|
||||||
|
android:insetRight="0dp"
|
||||||
|
android:textSize="12sp"
|
||||||
|
card_view:layout_constraintTop_toBottomOf="@id/tvMessage"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
android:backgroundTint="#F44336"
|
||||||
|
android:text="Delete"/>
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
</androidx.cardview.widget.CardView>
|
</androidx.cardview.widget.CardView>
|
||||||
|
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 |
8
android/app/src/main/res/values/attrs.xml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<declare-styleable name="MaxHeightScrollView">
|
||||||
|
<attr name="maxHeightOverride" format="integer" />
|
||||||
|
</declare-styleable>
|
||||||
|
|
||||||
|
</resources>
|
@@ -6,6 +6,9 @@
|
|||||||
<color name="colorHeader">#3F51B5</color>
|
<color name="colorHeader">#3F51B5</color>
|
||||||
<color name="colorHeaderForeground">#FFFFFF</color>
|
<color name="colorHeaderForeground">#FFFFFF</color>
|
||||||
|
|
||||||
|
<color name="colorOnPrimary">#ecf0f1</color>
|
||||||
|
<color name="colorSecondary">#FF5722</color>
|
||||||
|
|
||||||
<color name="colorBlack">#000</color>
|
<color name="colorBlack">#000</color>
|
||||||
|
|
||||||
<color name="bg_row_background">#fa315b</color>
|
<color name="bg_row_background">#fa315b</color>
|
||||||
|
@@ -6,4 +6,5 @@
|
|||||||
<dimen name="padd_10">10dp</dimen>
|
<dimen name="padd_10">10dp</dimen>
|
||||||
<dimen name="ic_delete">30dp</dimen>
|
<dimen name="ic_delete">30dp</dimen>
|
||||||
<dimen name="thumbnail">90dp</dimen>
|
<dimen name="thumbnail">90dp</dimen>
|
||||||
|
<dimen name="fab_margin">16dp</dimen>
|
||||||
</resources>
|
</resources>
|
@@ -11,13 +11,13 @@
|
|||||||
<string name="ic_img_fuel_desc">Icon Fuel</string>
|
<string name="ic_img_fuel_desc">Icon Fuel</string>
|
||||||
<string name="str_qr_code">QR Code</string>
|
<string name="str_qr_code">QR Code</string>
|
||||||
<string name="str_reset_account">Reset Account</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_not_connected">not connected</string>
|
||||||
<string name="str_reload">reload</string>
|
<string name="str_reload">reload</string>
|
||||||
<string name="desc_priority_icon">Priority icon</string>
|
<string name="desc_priority_icon">Priority icon</string>
|
||||||
<string name="str_common_settings">Common Settings</string>
|
<string name="str_common_settings">Common Settings</string>
|
||||||
<string name="str_enabled">Enabled</string>
|
<string name="str_enabled">Enabled</string>
|
||||||
<string name="str_localcachesize">Remember the last x notifications locally</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_prio0">Notifications (priority 0 - Low)</string>
|
||||||
<string name="str_header_prio1">Notifications (priority 1 - Normal)</string>
|
<string name="str_header_prio1">Notifications (priority 1 - Normal)</string>
|
||||||
<string name="str_header_prio2">Notifications (priority 2 - High)</string>
|
<string name="str_header_prio2">Notifications (priority 2 - High)</string>
|
||||||
@@ -29,9 +29,14 @@
|
|||||||
<string name="str_ledcolor">Notification light color</string>
|
<string name="str_ledcolor">Notification light color</string>
|
||||||
<string name="str_enable_vibration">Enable notification vibration</string>
|
<string name="str_enable_vibration">Enable notification vibration</string>
|
||||||
<string name="str_upgrade_account">Upgrade account</string>
|
<string name="str_upgrade_account">Upgrade account</string>
|
||||||
|
<string name="str_deleteswipe">Delete messages by swiping left</string>
|
||||||
|
<string name="str_previewlinecount">Number of visibile lines in collapsed messages</string>
|
||||||
<string name="str_promode">Thank you for supporting the app and using the pro mode</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="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="volume_icon">Volume icon</string>
|
||||||
<string name="play_test_sound">Play test sound</string>
|
<string name="play_test_sound">Play test sound</string>
|
||||||
<string name="delete">DELETE</string>
|
<string name="delete">DELETE</string>
|
||||||
|
<string name="title_activity_query_log">QueryLogActivity</string>
|
||||||
|
<string name="import_settings">Import settings</string>
|
||||||
|
<string name="export_settings">Export settings</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@@ -1,13 +1,20 @@
|
|||||||
<resources>
|
<resources>
|
||||||
|
|
||||||
<!-- Base application theme. -->
|
<!-- Base application theme. -->
|
||||||
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
<style name="AppTheme" parent="Theme.MaterialComponents.Light.NoActionBar">
|
||||||
<!-- your app branding color for the app bar -->
|
<!-- your app branding color for the app bar -->
|
||||||
<item name="colorPrimary">#3F51B5</item>
|
<item name="colorPrimary">#3F51B5</item>
|
||||||
|
|
||||||
<!-- darker variant for the status bar and contextual app bars -->
|
<!-- darker variant for the status bar and contextual app bars -->
|
||||||
<item name="colorPrimaryDark">#303F9F</item>
|
<item name="colorPrimaryDark">#303F9F</item>
|
||||||
|
|
||||||
<!-- theme UI controls like checkboxes and text fields -->
|
<!-- theme UI controls like checkboxes and text fields -->
|
||||||
<item name="colorAccent">#FF4081</item>
|
<item name="colorAccent">#FF5722</item>
|
||||||
|
<item name="colorSecondary">#FF5722</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.MaterialComponents.Dark.ActionBar" />
|
||||||
|
|
||||||
|
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.MaterialComponents.Light" />
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
7
android/app/src/main/res/xml/filepaths.xml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<paths>
|
||||||
|
<files-path path="/" name="files" />
|
||||||
|
<cache-path path="/" name="cache" />
|
||||||
|
<external-path path="/" name="external" />
|
||||||
|
<external-files-path path="/" name="external-files" />
|
||||||
|
<external-cache-path path="/" name="external-cache" />
|
||||||
|
</paths>
|
@@ -1,3 +1,3 @@
|
|||||||
#Mon Nov 12 18:26:41 CET 2018
|
#Sun Jan 05 22:18:36 UTC 2020
|
||||||
VERSION_NAME=0.0.7
|
VERSION_NAME=1.7.0
|
||||||
VERSION_CODE=7
|
VERSION_CODE=22
|
||||||
|
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/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/phone.pdn
Normal file
BIN
data/phone.png
Normal file
After Width: | Height: | Size: 1.0 MiB |
35
examples/scn_send.php
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $title
|
||||||
|
* @param string $content
|
||||||
|
* @param int $priority
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
function sendSCN($title, $content, $priority) {
|
||||||
|
global $config;
|
||||||
|
|
||||||
|
$data =
|
||||||
|
[
|
||||||
|
'user_id' => '', //TODO set your userid
|
||||||
|
'user_key' => '', //TODO set your userkey
|
||||||
|
'title' => $title,
|
||||||
|
'content' => $content,
|
||||||
|
'priority' => $priority,
|
||||||
|
];
|
||||||
|
|
||||||
|
$ch = curl_init();
|
||||||
|
|
||||||
|
curl_setopt($ch, CURLOPT_URL, "https://simplecloudnotifier.blackforestbytes.com/send.php");
|
||||||
|
curl_setopt($ch, CURLOPT_POST, 1);
|
||||||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
|
||||||
|
$result = curl_exec($ch);
|
||||||
|
|
||||||
|
curl_close($ch);
|
||||||
|
if ($result === false) return false;
|
||||||
|
|
||||||
|
$json = json_decode($result, true);
|
||||||
|
return $json['success'];
|
||||||
|
}
|
78
examples/scn_send.sh
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
#!/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=""
|
||||||
|
sendtime=$(date +%s)
|
||||||
|
|
||||||
|
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 "timestamp=$sendtime" \
|
||||||
|
-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.
|
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.
|
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)
|
(see https://simplecloudnotifier.blackforestbytes.com/ for an example with curl)
|
||||||
|
|
||||||
|
|
||||||
Use it to
|
Use it to
|
||||||
- send yourself automated messages from cron jobs
|
- 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
|
- send server error messages directly to your phone
|
||||||
- integrate with other online services
|
- 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 |
10
web/api.php
@@ -1,5 +1,15 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<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>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<link rel="stylesheet" href="/css/mini-default.min.css"> <!-- https://minicss.org/docs -->
|
<link rel="stylesheet" href="/css/mini-default.min.css"> <!-- https://minicss.org/docs -->
|
||||||
|
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',
|
||||||
|
],
|
||||||
|
|
||||||
|
];
|
53
web/api/expand.php
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
include_once 'model.php';
|
||||||
|
|
||||||
|
$INPUT = array_merge($_GET, $_POST);
|
||||||
|
|
||||||
|
|
||||||
|
if (!isset($INPUT['user_id'])) die(json_encode(['success' => false, 'errid'=>101, 'message' => 'Missing parameter [[user_id]]']));
|
||||||
|
if (!isset($INPUT['user_key'])) die(json_encode(['success' => false, 'errid'=>102, 'message' => 'Missing parameter [[user_key]]']));
|
||||||
|
if (!isset($INPUT['scn_msg_id'])) die(json_encode(['success' => false, 'errid'=>103, 'message' => 'Missing parameter [[scn_msg_id]]']));
|
||||||
|
|
||||||
|
$user_id = $INPUT['user_id'];
|
||||||
|
$user_key = $INPUT['user_key'];
|
||||||
|
$scn_msg_id = $INPUT['scn_msg_id'];
|
||||||
|
|
||||||
|
//----------------------
|
||||||
|
|
||||||
|
$pdo = getDatabase();
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare('SELECT user_id, user_key, quota_today, is_pro, quota_day, fcm_token FROM users WHERE user_id = :uid LIMIT 1');
|
||||||
|
$stmt->execute(['uid' => $user_id]);
|
||||||
|
$datas = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (count($datas)<=0) die(json_encode(['success' => false, 'errid'=>201, 'message' => 'User not found']));
|
||||||
|
$data = $datas[0];
|
||||||
|
|
||||||
|
if ($data === null) die(json_encode(['success' => false, 'errid'=>202, 'message' => 'User not found']));
|
||||||
|
if ($data['user_id'] !== (int)$user_id) die(json_encode(['success' => false, 'errid'=>203, 'message' => 'UserID not found']));
|
||||||
|
if ($data['user_key'] !== $user_key) die(json_encode(['success' => false, 'errid'=>204, 'message' => 'Authentification failed']));
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare('SELECT * FROM messages WHERE scn_message_id=:smid AND sender_user_id=:uid LIMIT 1');
|
||||||
|
$stmt->execute(['smid' => $scn_msg_id, 'uid' => $user_id]);
|
||||||
|
$datas = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (count($datas)<=0) die(json_encode(['success' => false, 'errid'=>301, 'message' => 'Message not found']));
|
||||||
|
|
||||||
|
$msg = $datas[0];
|
||||||
|
|
||||||
|
api_return(200,
|
||||||
|
[
|
||||||
|
'success' => true,
|
||||||
|
'data' =>
|
||||||
|
[
|
||||||
|
'title' => $msg['title'],
|
||||||
|
'body' => $msg['content'],
|
||||||
|
'trimmed' => false,
|
||||||
|
'priority' => $msg['priority'],
|
||||||
|
'timestamp' => $msg['sendtime'],
|
||||||
|
'usr_msg_id' => $msg['usr_message_id'],
|
||||||
|
'scn_msg_id' => $msg['scn_message_id'],
|
||||||
|
],
|
||||||
|
'message' => 'ok'
|
||||||
|
]);
|
@@ -30,7 +30,7 @@ if ($data['user_key'] !== $user_key) die(json_encode(['success' => false, 'errid
|
|||||||
|
|
||||||
$stmt = $pdo->prepare('SELECT COUNT(*) FROM messages WHERE ack=0 AND sender_user_id=:uid');
|
$stmt = $pdo->prepare('SELECT COUNT(*) FROM messages WHERE ack=0 AND sender_user_id=:uid');
|
||||||
$stmt->execute(['uid' => $user_id]);
|
$stmt->execute(['uid' => $user_id]);
|
||||||
$nack_count = $stmt->fetch(PDO::FETCH_NUM);
|
$nack_count = $stmt->fetch(PDO::FETCH_NUM)[0];
|
||||||
|
|
||||||
|
|
||||||
$quota = $data['quota_today'];
|
$quota = $data['quota_today'];
|
||||||
|
@@ -4,29 +4,30 @@ include('lib/httpful.phar');
|
|||||||
|
|
||||||
class ERR
|
class ERR
|
||||||
{
|
{
|
||||||
const NO_ERROR = 0000;
|
const NO_ERROR = 0000;
|
||||||
|
|
||||||
const MISSING_UID = 1101;
|
const MISSING_UID = 1101;
|
||||||
const MISSING_TOK = 1102;
|
const MISSING_TOK = 1102;
|
||||||
const MISSING_TITLE = 1103;
|
const MISSING_TITLE = 1103;
|
||||||
const INVALID_PRIO = 1104;
|
const INVALID_PRIO = 1104;
|
||||||
const REQ_METHOD = 1105;
|
const REQ_METHOD = 1105;
|
||||||
|
|
||||||
const NO_TITLE = 1201;
|
const NO_TITLE = 1201;
|
||||||
const TITLE_TOO_LONG = 1202;
|
const TITLE_TOO_LONG = 1202;
|
||||||
const CONTENT_TOO_LONG = 1203;
|
const CONTENT_TOO_LONG = 1203;
|
||||||
const USR_MSG_ID_TOO_LONG = 1204;
|
const USR_MSG_ID_TOO_LONG = 1204;
|
||||||
|
const TIMESTAMP_OUT_OF_RANGE = 1205;
|
||||||
|
|
||||||
const USER_NOT_FOUND = 1301;
|
const USER_NOT_FOUND = 1301;
|
||||||
const USER_AUTH_FAILED = 1302;
|
const USER_AUTH_FAILED = 1302;
|
||||||
|
|
||||||
const NO_DEVICE_LINKED = 1401;
|
const NO_DEVICE_LINKED = 1401;
|
||||||
|
|
||||||
const QUOTA_REACHED = 2101;
|
const QUOTA_REACHED = 2101;
|
||||||
|
|
||||||
const FIREBASE_COM_FAILED = 9901;
|
const FIREBASE_COM_FAILED = 9901;
|
||||||
const FIREBASE_COM_ERRORED = 9902;
|
const FIREBASE_COM_ERRORED = 9902;
|
||||||
const INTERNAL_EXCEPTION = 9903;
|
const INTERNAL_EXCEPTION = 9903;
|
||||||
}
|
}
|
||||||
|
|
||||||
class Statics
|
class Statics
|
||||||
@@ -35,6 +36,14 @@ class Statics
|
|||||||
public static $CFG = NULL;
|
public static $CFG = NULL;
|
||||||
|
|
||||||
public static function quota_max($is_pro) { return $is_pro ? 1000 : 50; }
|
public static function quota_max($is_pro) { return $is_pro ? 1000 : 50; }
|
||||||
|
|
||||||
|
public static function contentlen_max($is_pro) { return $is_pro ? 16384 : 2048; }
|
||||||
|
}
|
||||||
|
|
||||||
|
function str_limit($str, $len)
|
||||||
|
{
|
||||||
|
if (strlen($str)>$len) return substr($str, 0, $len-3)."...";
|
||||||
|
return $str;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getConfig()
|
function getConfig()
|
||||||
@@ -157,6 +166,12 @@ function verifyOrderToken($tok)
|
|||||||
{
|
{
|
||||||
// https://developers.google.com/android-publisher/api-ref/purchases/products/get
|
// https://developers.google.com/android-publisher/api-ref/purchases/products/get
|
||||||
|
|
||||||
|
// if this does no longer work, you probably have to go through the initial OAuth process again
|
||||||
|
// 1. go to Postman do the [ https://accounts.google.com/o/oauth2/auth ] request (in browser) to get a new "code"
|
||||||
|
// 2. go to Postman do the [ Get Tokens ] request to get a new "access_token" and "access_token"
|
||||||
|
// 3. update these tokens in the server config.php
|
||||||
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
$package = getConfig()['verify_api']['package_name'];
|
$package = getConfig()['verify_api']['package_name'];
|
||||||
|
@@ -24,17 +24,20 @@ if ($ispro)
|
|||||||
$pdo->rollBack();
|
$pdo->rollBack();
|
||||||
die(json_encode(['success' => false, 'message' => 'Purchase token could not be verified']));
|
die(json_encode(['success' => false, 'message' => 'Purchase token could not be verified']));
|
||||||
}
|
}
|
||||||
|
|
||||||
$stmt = $pdo->prepare('UPDATE users SET is_pro=0, pro_token=NULL WHERE user_id <> :uid AND pro_token = :ptk');
|
|
||||||
$stmt->execute(['uid' => $user_id, 'ptk' => $pro_token]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$stmt = $pdo->prepare('INSERT INTO users (user_key, fcm_token, is_pro, pro_token, timestamp_accessed) VALUES (:key, :token, :bpro, :spro, NOW())');
|
$stmt = $pdo->prepare('INSERT INTO users (user_key, fcm_token, is_pro, pro_token, timestamp_accessed) VALUES (:key, :token, :bpro, :spro, NOW())');
|
||||||
$stmt->execute(['key' => $user_key, 'token' => $fcmtoken, 'bpro' => $ispro, 'spro' => $ispro ? $pro_token : null]);
|
$stmt->execute(['key' => $user_key, 'token' => $fcmtoken, 'bpro' => ($ispro ? 1 : 0), 'spro' => ($ispro ? $pro_token : null)]);
|
||||||
$user_id = $pdo->lastInsertId('user_id');
|
$user_id = $pdo->lastInsertId('user_id');
|
||||||
|
|
||||||
$stmt = $pdo->prepare('UPDATE users SET fcm_token=NULL WHERE user_id <> :uid AND fcm_token=:ft');
|
$stmt = $pdo->prepare('UPDATE users SET fcm_token=NULL WHERE user_id <> :uid AND fcm_token=:ft');
|
||||||
$stmt->execute(['uid' => $user_id, 'ft' => $fcm_token]);
|
$stmt->execute(['uid' => $user_id, 'ft' => $fcmtoken]);
|
||||||
|
|
||||||
|
if ($ispro)
|
||||||
|
{
|
||||||
|
$stmt = $pdo->prepare('UPDATE users SET is_pro=0, pro_token=NULL WHERE user_id <> :uid AND pro_token = :ptk');
|
||||||
|
$stmt->execute(['uid' => $user_id, 'ptk' => $pro_token]);
|
||||||
|
}
|
||||||
|
|
||||||
$pdo->commit();
|
$pdo->commit();
|
||||||
|
|
||||||
|
@@ -28,7 +28,7 @@ if ($data['user_key'] !== $user_key) die(json_encode(['success' => false, 'errid
|
|||||||
|
|
||||||
//-------------------
|
//-------------------
|
||||||
|
|
||||||
$stmt = $pdo->prepare('SELECT * FROM messages WHERE ack=0 AND sender_user_id=:uid ORDER BY `timestamp` DESC LIMIT 16');
|
$stmt = $pdo->prepare('SELECT * FROM messages WHERE ack=0 AND sender_user_id=:uid ORDER BY `timestamp_real` DESC LIMIT 16');
|
||||||
$stmt->execute(['uid' => $user_id]);
|
$stmt->execute(['uid' => $user_id]);
|
||||||
$nonacks_sql = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
$nonacks_sql = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ foreach ($nonacks_sql as $nack)
|
|||||||
'title' => $nack['title'],
|
'title' => $nack['title'],
|
||||||
'body' => $nack['content'],
|
'body' => $nack['content'],
|
||||||
'priority' => $nack['priority'],
|
'priority' => $nack['priority'],
|
||||||
'timestamp' => $nack['timestamp'],
|
'timestamp' => $nack['sendtime'],
|
||||||
'usr_msg_id' => $nack['usr_message_id'],
|
'usr_msg_id' => $nack['usr_message_id'],
|
||||||
'scn_msg_id' => $nack['scn_message_id'],
|
'scn_msg_id' => $nack['scn_message_id'],
|
||||||
];
|
];
|
||||||
|
@@ -23,12 +23,13 @@ CREATE TABLE `messages`
|
|||||||
`scn_message_id` INT(11) NOT NULL AUTO_INCREMENT,
|
`scn_message_id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||||
`sender_user_id` INT(11) NOT NULL,
|
`sender_user_id` INT(11) NOT NULL,
|
||||||
|
|
||||||
`timestamp` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
`timestamp_real` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
`ack` BIT NOT NULL DEFAULT 0,
|
`ack` TINYINT(1) NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
`title` VARCHAR(256) NOT NULL,
|
`title` VARCHAR(256) NOT NULL,
|
||||||
`content` VARCHAR(12288) NULL,
|
`content` VARCHAR(12288) NULL,
|
||||||
`priority` INT(11) NOT NULL,
|
`priority` INT(11) NOT NULL,
|
||||||
|
`sendtime` BIGINT UNSIGNED NOT NULL,
|
||||||
|
|
||||||
`fcm_message_id` VARCHAR(256) NULL,
|
`fcm_message_id` VARCHAR(256) NULL,
|
||||||
`usr_message_id` VARCHAR(256) NULL,
|
`usr_message_id` VARCHAR(256) NULL,
|
||||||
|
116
web/api_more.php
@@ -1,5 +1,15 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<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 (Long)');
|
||||||
|
$owa->trackPageView();
|
||||||
|
}
|
||||||
|
?>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<link rel="stylesheet" href="/css/mini-default.min.css"> <!-- https://minicss.org/docs -->
|
<link rel="stylesheet" href="/css/mini-default.min.css"> <!-- https://minicss.org/docs -->
|
||||||
@@ -187,6 +197,112 @@
|
|||||||
Be aware that the server only saves send messages for a short amount of time. Because of that you can only use this to prevent duplicates in a short time-frame, older messages with the same ID are probably already deleted and the message will be send again.
|
Be aware that the server only saves send messages for a short amount of time. Because of that you can only use this to prevent duplicates in a short time-frame, older messages with the same ID are probably already deleted and the message will be send again.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<h2>Custom Time</h2>
|
||||||
|
<div class="section">
|
||||||
|
<p>
|
||||||
|
You can modify the displayed timestamp of a message by sending the <code>timestamp</code> parameter. The format must be a valid UNIX timestamp (elapsed seconds since 1970-01-01 GMT)
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The custom timestamp must be within 48 hours of the current time. This parameter is only intended to supply a more precise value in case the message sending was delayed.
|
||||||
|
</p>
|
||||||
|
<pre>curl \
|
||||||
|
--data "user_id={userid}" \
|
||||||
|
--data "user_key={userkey}" \
|
||||||
|
--data "title={message_title}" \
|
||||||
|
--data "timestamp={unix_timestamp}" \
|
||||||
|
https://scn.blackforestbytes.com/send.php</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>Bash script example</h2>
|
||||||
|
<div class="section">
|
||||||
|
<p>
|
||||||
|
Depending on your use case it can be useful to create a bash script that handles things like resending messages if you have connection problems or waiting if there is no quota left.<br/>
|
||||||
|
Here is an example how such a scrippt could look like, you can put it into <code>/usr/local/sbin</code> and call it with <code>scn_send "title" "content"</code>
|
||||||
|
</p>
|
||||||
|
<pre style="color:#000000;" class="yellow-code"><span style="color:#3f7f59; font-weight:bold;">#!/usr/bin/env bash</span>
|
||||||
|
|
||||||
|
<span style="color:#3f7f59; ">#</span>
|
||||||
|
<span style="color:#3f7f59; "># Call with `scn_send title`</span>
|
||||||
|
<span style="color:#3f7f59; "># or `scn_send title content`</span>
|
||||||
|
<span style="color:#3f7f59; "># or `scn_send title content priority`</span>
|
||||||
|
<span style="color:#3f7f59; ">#</span>
|
||||||
|
<span style="color:#3f7f59; ">#</span>
|
||||||
|
|
||||||
|
<span style="color:#7f0055; font-weight:bold; ">if</span> [ <span style="color:#2a00ff; ">"$#"</span> -lt 1 ]; <span style="color:#7f0055; font-weight:bold; ">then</span>
|
||||||
|
<span style="color:#7f0055; font-weight:bold; ">echo</span> <span style="color:#2a00ff; ">"no title supplied via parameter"</span>
|
||||||
|
<span style="color:#7f0055; font-weight:bold; ">exit</span> 1
|
||||||
|
<span style="color:#7f0055; font-weight:bold; ">fi</span>
|
||||||
|
|
||||||
|
<span style="color:#3f7f59; ">################################################################################</span>
|
||||||
|
<span style="color:#3f7f59; "># INSERT YOUR DATA HERE #</span>
|
||||||
|
<span style="color:#3f7f59; ">################################################################################</span>
|
||||||
|
user_id=999
|
||||||
|
user_key=<span style="color:#2a00ff; ">"????????????????????????????????????????????????????????????????"</span>
|
||||||
|
<span style="color:#3f7f59; ">################################################################################</span>
|
||||||
|
|
||||||
|
title=$1
|
||||||
|
content=<span style="color:#2a00ff; ">""</span>
|
||||||
|
sendtime=$(date +%s)
|
||||||
|
|
||||||
|
<span style="color:#7f0055; font-weight:bold; ">if</span> [ <span style="color:#2a00ff; ">"$#"</span> -gt 1 ]; <span style="color:#7f0055; font-weight:bold; ">then</span>
|
||||||
|
content=$2
|
||||||
|
<span style="color:#7f0055; font-weight:bold; ">fi</span>
|
||||||
|
|
||||||
|
priority=1
|
||||||
|
|
||||||
|
<span style="color:#7f0055; font-weight:bold; ">if</span> [ <span style="color:#2a00ff; ">"$#"</span> -gt 2 ]; <span style="color:#7f0055; font-weight:bold; ">then</span>
|
||||||
|
priority=$3
|
||||||
|
<span style="color:#7f0055; font-weight:bold; ">fi</span>
|
||||||
|
|
||||||
|
usr_msg_id=$(uuidgen)
|
||||||
|
|
||||||
|
<span style="color:#7f0055; font-weight:bold; ">while</span> true ; <span style="color:#7f0055; font-weight:bold; ">do</span>
|
||||||
|
|
||||||
|
curlresp=$(curl -s -o <span style="color:#3f3fbf; ">/dev/null</span> -w <span style="color:#2a00ff; ">"%{http_code}"</span> <span style="color:#2a00ff; ">\</span>
|
||||||
|
-d <span style="color:#2a00ff; ">"</span><span style="color:#2a00ff; ">user_id</span><span style="color:#2a00ff; ">=</span><span style="color:#2a00ff; ">$user_id</span><span style="color:#2a00ff; ">"</span> -d <span style="color:#2a00ff; ">"</span><span style="color:#2a00ff; ">user_key</span><span style="color:#2a00ff; ">=</span><span style="color:#2a00ff; ">$user_key</span><span style="color:#2a00ff; ">"</span> -d <span style="color:#2a00ff; ">"</span><span style="color:#2a00ff; ">title</span><span style="color:#2a00ff; ">=</span><span style="color:#2a00ff; ">$title</span><span style="color:#2a00ff; ">"</span> -d <span style="color:#2a00ff; ">"</span><span style="color:#2a00ff; ">timestamp</span><span style="color:#2a00ff; ">=</span><span style="color:#2a00ff; ">$sendtime</span><span style="color:#2a00ff; ">"</span> <span style="color:#2a00ff; ">\</span>
|
||||||
|
-d <span style="color:#2a00ff; ">"</span><span style="color:#2a00ff; ">content</span><span style="color:#2a00ff; ">=</span><span style="color:#2a00ff; ">$content</span><span style="color:#2a00ff; ">"</span> -d <span style="color:#2a00ff; ">"</span><span style="color:#2a00ff; ">priority</span><span style="color:#2a00ff; ">=</span><span style="color:#2a00ff; ">$priority</span><span style="color:#2a00ff; ">"</span> -d <span style="color:#2a00ff; ">"</span><span style="color:#2a00ff; ">msg_id</span><span style="color:#2a00ff; ">=</span><span style="color:#2a00ff; ">$usr_msg_id</span><span style="color:#2a00ff; ">"</span> <span style="color:#2a00ff; ">\</span>
|
||||||
|
https:<span style="color:#3f3fbf; ">/</span><span style="color:#3f3fbf; ">/scn.blackforestbytes.com/send.php</span>)
|
||||||
|
|
||||||
|
<span style="color:#7f0055; font-weight:bold; ">if</span> [ <span style="color:#2a00ff; ">"</span><span style="color:#2a00ff; ">$curlresp</span><span style="color:#2a00ff; ">"</span> == 200 ] ; <span style="color:#7f0055; font-weight:bold; ">then</span>
|
||||||
|
<span style="color:#7f0055; font-weight:bold; ">echo</span> <span style="color:#2a00ff; ">"Successfully send"</span>
|
||||||
|
<span style="color:#7f0055; font-weight:bold; ">exit</span> 0
|
||||||
|
<span style="color:#7f0055; font-weight:bold; ">fi</span>
|
||||||
|
|
||||||
|
<span style="color:#7f0055; font-weight:bold; ">if</span> [ <span style="color:#2a00ff; ">"</span><span style="color:#2a00ff; ">$curlresp</span><span style="color:#2a00ff; ">"</span> == 400 ] ; <span style="color:#7f0055; font-weight:bold; ">then</span>
|
||||||
|
<span style="color:#7f0055; font-weight:bold; ">echo</span> <span style="color:#2a00ff; ">"Bad request - something went wrong"</span>
|
||||||
|
<span style="color:#7f0055; font-weight:bold; ">exit</span> 1
|
||||||
|
<span style="color:#7f0055; font-weight:bold; ">fi</span>
|
||||||
|
|
||||||
|
<span style="color:#7f0055; font-weight:bold; ">if</span> [ <span style="color:#2a00ff; ">"</span><span style="color:#2a00ff; ">$curlresp</span><span style="color:#2a00ff; ">"</span> == 401 ] ; <span style="color:#7f0055; font-weight:bold; ">then</span>
|
||||||
|
<span style="color:#7f0055; font-weight:bold; ">echo</span> <span style="color:#2a00ff; ">"Unauthorized - wrong </span><span style="color:#3f3fbf; ">userid/userkey</span><span style="color:#2a00ff; ">"</span>
|
||||||
|
<span style="color:#7f0055; font-weight:bold; ">exit</span> 1
|
||||||
|
<span style="color:#7f0055; font-weight:bold; ">fi</span>
|
||||||
|
|
||||||
|
<span style="color:#7f0055; font-weight:bold; ">if</span> [ <span style="color:#2a00ff; ">"</span><span style="color:#2a00ff; ">$curlresp</span><span style="color:#2a00ff; ">"</span> == 403 ] ; <span style="color:#7f0055; font-weight:bold; ">then</span>
|
||||||
|
<span style="color:#7f0055; font-weight:bold; ">echo</span> <span style="color:#2a00ff; ">"Quota exceeded - wait one hour before re-try"</span>
|
||||||
|
sleep 3600
|
||||||
|
<span style="color:#7f0055; font-weight:bold; ">fi</span>
|
||||||
|
|
||||||
|
<span style="color:#7f0055; font-weight:bold; ">if</span> [ <span style="color:#2a00ff; ">"</span><span style="color:#2a00ff; ">$curlresp</span><span style="color:#2a00ff; ">"</span> == 412 ] ; <span style="color:#7f0055; font-weight:bold; ">then</span>
|
||||||
|
<span style="color:#7f0055; font-weight:bold; ">echo</span> <span style="color:#2a00ff; ">"Precondition Failed - No device linked"</span>
|
||||||
|
<span style="color:#7f0055; font-weight:bold; ">exit</span> 1
|
||||||
|
<span style="color:#7f0055; font-weight:bold; ">fi</span>
|
||||||
|
|
||||||
|
<span style="color:#7f0055; font-weight:bold; ">if</span> [ <span style="color:#2a00ff; ">"</span><span style="color:#2a00ff; ">$curlresp</span><span style="color:#2a00ff; ">"</span> == 500 ] ; <span style="color:#7f0055; font-weight:bold; ">then</span>
|
||||||
|
<span style="color:#7f0055; font-weight:bold; ">echo</span> <span style="color:#2a00ff; ">"Internal server error - waiting for better times"</span>
|
||||||
|
sleep 60
|
||||||
|
<span style="color:#7f0055; font-weight:bold; ">fi</span>
|
||||||
|
|
||||||
|
<span style="color:#3f7f59; "># if none of the above matched we probably hav no network ...</span>
|
||||||
|
<span style="color:#7f0055; font-weight:bold; ">echo</span> <span style="color:#2a00ff; ">"Send failed (response code </span><span style="color:#2a00ff; ">$curlresp</span><span style="color:#2a00ff; ">) ... try again in 5s"</span>
|
||||||
|
sleep 5
|
||||||
|
<span style="color:#7f0055; font-weight:bold; ">done</span>
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
Be aware that the server only saves send messages for a short amount of time. Because of that you can only use this to prevent duplicates in a short time-frame, older messages with the same ID are probably already deleted and the message will be send again.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
@@ -42,6 +42,11 @@ body
|
|||||||
border-left: .25rem solid #E53935;
|
border-left: .25rem solid #E53935;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.yellow-code
|
||||||
|
{
|
||||||
|
border-left: .25rem solid #FFCB05;
|
||||||
|
}
|
||||||
|
|
||||||
#mainpnl input,
|
#mainpnl input,
|
||||||
#mainpnl textarea
|
#mainpnl textarea
|
||||||
{
|
{
|
||||||
@@ -233,6 +238,13 @@ table.scode_table th:nth-child(2) {
|
|||||||
margin-top: 1.75rem;
|
margin-top: 1.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.linkcaption:hover {
|
.linkcaption:hover,
|
||||||
|
.linkcaption:focus {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pre, pre span
|
||||||
|
{
|
||||||
|
font-family: Menlo, Consolas, monospace;
|
||||||
|
background: #F9F9F9;;
|
||||||
|
}
|
@@ -1,5 +1,15 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<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('Index');
|
||||||
|
$owa->trackPageView();
|
||||||
|
}
|
||||||
|
?>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>Simple Cloud Notifications</title>
|
<title>Simple Cloud Notifications</title>
|
||||||
@@ -54,7 +64,7 @@
|
|||||||
|
|
||||||
<div class="row responsive-label">
|
<div class="row responsive-label">
|
||||||
<div class="col-sm-12 col-md-3"><label for="txt" class="doc">Message Content</label></div>
|
<div class="col-sm-12 col-md-3"><label for="txt" class="doc">Message Content</label></div>
|
||||||
<div class="col-sm-12 col-md"><textarea id="txt" class="doc" <?php echo (isset($_GET['preset_content']) ? (' value="'.$_GET['preset_content'].'" '):(''));?> rows="8"></textarea></div>
|
<div class="col-sm-12 col-md"><textarea id="txt" class="doc" <?php echo (isset($_GET['preset_content']) ? (' value="'.$_GET['preset_content'].'" '):(''));?> rows="8" maxlength="2048"></textarea></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
@@ -34,10 +34,10 @@ function send()
|
|||||||
if (xhr.readyState !== 4) return;
|
if (xhr.readyState !== 4) return;
|
||||||
|
|
||||||
console.log('Status: ' + xhr.status);
|
console.log('Status: ' + xhr.status);
|
||||||
if (xhr.status === 200)
|
if (xhr.status === 200 || xhr.status === 401 || xhr.status === 403 || xhr.status === 412)
|
||||||
{
|
{
|
||||||
let resp = JSON.parse(xhr.responseText);
|
let resp = JSON.parse(xhr.responseText);
|
||||||
if (!resp.success)
|
if (!resp.success || xhr.status !== 200)
|
||||||
{
|
{
|
||||||
if (resp.errhighlight === 101) uid.classList.add('input-invalid');
|
if (resp.errhighlight === 101) uid.classList.add('input-invalid');
|
||||||
if (resp.errhighlight === 102) key.classList.add('input-invalid');
|
if (resp.errhighlight === 102) key.classList.add('input-invalid');
|
||||||
|
47
web/send.php
@@ -5,8 +5,6 @@ include_once 'api/model.php';
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
||||||
//------------------------------------------------------------------
|
|
||||||
//sleep(1);
|
|
||||||
//------------------------------------------------------------------
|
//------------------------------------------------------------------
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') api_return(400, ['success' => false, 'error' => ERR::REQ_METHOD, 'errhighlight' => -1, 'message' => 'Invalid request method (must be POST)']);
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') api_return(400, ['success' => false, 'error' => ERR::REQ_METHOD, 'errhighlight' => -1, 'message' => 'Invalid request method (must be POST)']);
|
||||||
@@ -19,21 +17,21 @@ try
|
|||||||
|
|
||||||
//------------------------------------------------------------------
|
//------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
$user_id = $INPUT['user_id'];
|
$user_id = $INPUT['user_id'];
|
||||||
$user_key = $INPUT['user_key'];
|
$user_key = $INPUT['user_key'];
|
||||||
$message = $INPUT['title'];
|
$message = $INPUT['title'];
|
||||||
$content = isset($INPUT['content']) ? $INPUT['content'] : '';
|
$content = isset($INPUT['content']) ? $INPUT['content'] : '';
|
||||||
$priority = isset($INPUT['priority']) ? $INPUT['priority'] : '1';
|
$priority = isset($INPUT['priority']) ? $INPUT['priority'] : '1';
|
||||||
$usrmsgid = isset($INPUT['msg_id']) ? $INPUT['msg_id'] : null;
|
$usrmsgid = isset($INPUT['msg_id']) ? $INPUT['msg_id'] : null;
|
||||||
|
$time = isset($INPUT['timestamp']) ? $INPUT['timestamp'] : time();
|
||||||
|
|
||||||
//------------------------------------------------------------------
|
//------------------------------------------------------------------
|
||||||
|
|
||||||
|
if (abs($time - time()) > 60*60*24*2) api_return(400, ['success' => false, 'error' => ERR::TIMESTAMP_OUT_OF_RANGE, 'errhighlight' => -1, 'message' => 'The timestamp mus be within 24 hours of now()']);
|
||||||
|
|
||||||
if ($priority !== '0' && $priority !== '1' && $priority !== '2') api_return(400, ['success' => false, 'error' => ERR::INVALID_PRIO, 'errhighlight' => 105, 'message' => 'Invalid priority']);
|
if ($priority !== '0' && $priority !== '1' && $priority !== '2') api_return(400, ['success' => false, 'error' => ERR::INVALID_PRIO, 'errhighlight' => 105, 'message' => 'Invalid priority']);
|
||||||
|
|
||||||
if (strlen(trim($message)) == 0) api_return(400, ['success' => false, 'error' => ERR::NO_TITLE, 'errhighlight' => 103, 'message' => 'No title specified']);
|
if (strlen(trim($message)) == 0) api_return(400, ['success' => false, 'error' => ERR::NO_TITLE, 'errhighlight' => 103, 'message' => 'No title specified']);
|
||||||
if (strlen($message) > 120) api_return(400, ['success' => false, 'error' => ERR::TITLE_TOO_LONG, 'errhighlight' => 103, 'message' => 'Title too long (120 characters)']);
|
|
||||||
if (strlen($content) > 10000) api_return(400, ['success' => false, 'error' => ERR::CONTENT_TOO_LONG, 'errhighlight' => 104, 'message' => 'Content too long (10000 characters)']);
|
|
||||||
if ($usrmsgid != null && strlen($usrmsgid) > 64) api_return(400, ['success' => false, 'error' => ERR::USR_MSG_ID_TOO_LONG, 'errhighlight' => -1, 'message' => 'MessageID too long (64 characters)']);
|
if ($usrmsgid != null && strlen($usrmsgid) > 64) api_return(400, ['success' => false, 'error' => ERR::USR_MSG_ID_TOO_LONG, 'errhighlight' => -1, 'message' => 'MessageID too long (64 characters)']);
|
||||||
|
|
||||||
//------------------------------------------------------------------
|
//------------------------------------------------------------------
|
||||||
@@ -44,13 +42,20 @@ try
|
|||||||
$stmt->execute(['uid' => $user_id]);
|
$stmt->execute(['uid' => $user_id]);
|
||||||
|
|
||||||
$datas = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
$datas = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
if (count($datas)<=0) die(json_encode(['success' => false, 'error' => ERR::USER_NOT_FOUND, 'errhighlight' => 101, 'message' => 'User not found']));
|
if (count($datas)<=0) api_return(401, ['success' => false, 'error' => ERR::USER_NOT_FOUND, 'errhighlight' => 101, 'message' => 'User not found']);
|
||||||
$data = $datas[0];
|
$data = $datas[0];
|
||||||
|
|
||||||
if ($data === null) api_return(401, ['success' => false, 'error' => ERR::USER_NOT_FOUND, 'errhighlight' => 101, 'message' => 'User not found']);
|
if ($data === null) api_return(401, ['success' => false, 'error' => ERR::USER_NOT_FOUND, 'errhighlight' => 101, 'message' => 'User not found']);
|
||||||
if ($data['user_id'] !== (int)$user_id) api_return(401, ['success' => false, 'error' => ERR::USER_NOT_FOUND, 'errhighlight' => 101, 'message' => 'UserID not found']);
|
if ($data['user_id'] !== (int)$user_id) api_return(401, ['success' => false, 'error' => ERR::USER_NOT_FOUND, 'errhighlight' => 101, 'message' => 'UserID not found']);
|
||||||
if ($data['user_key'] !== $user_key) api_return(401, ['success' => false, 'error' => ERR::USER_AUTH_FAILED, 'errhighlight' => 102, 'message' => 'Authentification failed']);
|
if ($data['user_key'] !== $user_key) api_return(401, ['success' => false, 'error' => ERR::USER_AUTH_FAILED, 'errhighlight' => 102, 'message' => 'Authentification failed']);
|
||||||
|
|
||||||
|
//------------------------------------------------------------------
|
||||||
|
|
||||||
|
if (strlen($message) > 120) api_return(400, ['success' => false, 'error' => ERR::TITLE_TOO_LONG, 'errhighlight' => 103, 'message' => 'Title too long (120 characters)']);
|
||||||
|
if (strlen($content) > Statics::contentlen_max($data['is_pro'])) api_return(400, ['success' => false, 'error' => ERR::CONTENT_TOO_LONG, 'errhighlight' => 104, 'message' => 'Content too long ('.Statics::contentlen_max($data['is_pro']).' characters)']);
|
||||||
|
|
||||||
|
//------------------------------------------------------------------
|
||||||
|
|
||||||
$fcm = $data['fcm_token'];
|
$fcm = $data['fcm_token'];
|
||||||
|
|
||||||
$new_quota = $data['quota_today'] + 1;
|
$new_quota = $data['quota_today'] + 1;
|
||||||
@@ -90,13 +95,14 @@ try
|
|||||||
$pdo->beginTransaction();
|
$pdo->beginTransaction();
|
||||||
|
|
||||||
|
|
||||||
$stmt = $pdo->prepare('INSERT INTO messages (sender_user_id, title, content, priority, fcm_message_id, usr_message_id) VALUES (:suid, :t, :c, :p, :fmid, :umid)');
|
$stmt = $pdo->prepare('INSERT INTO messages (sender_user_id, title, content, priority, sendtime, fcm_message_id, usr_message_id) VALUES (:suid, :t, :c, :p, :ts, :fmid, :umid)');
|
||||||
$stmt->execute(
|
$stmt->execute(
|
||||||
[
|
[
|
||||||
'suid' => $user_id,
|
'suid' => $user_id,
|
||||||
't' => $message,
|
't' => $message,
|
||||||
'c' => $content,
|
'c' => $content,
|
||||||
'p' => $priority,
|
'p' => $priority,
|
||||||
|
'ts' => $time,
|
||||||
'fmid' => null,
|
'fmid' => null,
|
||||||
'umid' => $usrmsgid,
|
'umid' => $usrmsgid,
|
||||||
]);
|
]);
|
||||||
@@ -115,14 +121,15 @@ try
|
|||||||
// 'body' => $content,
|
// 'body' => $content,
|
||||||
//],
|
//],
|
||||||
'data' =>
|
'data' =>
|
||||||
[
|
[
|
||||||
'title' => $message,
|
'title' => $message,
|
||||||
'body' => $content,
|
'body' => str_limit($content, 1900),
|
||||||
'priority' => $priority,
|
'trimmed' => (strlen($content) > 1900),
|
||||||
'timestamp' => time(),
|
'priority' => $priority,
|
||||||
'usr_msg_id' => $usrmsgid,
|
'timestamp' => $time,
|
||||||
'scn_msg_id' => $scn_msg_id,
|
'usr_msg_id' => $usrmsgid,
|
||||||
]
|
'scn_msg_id' => $scn_msg_id,
|
||||||
|
]
|
||||||
]);
|
]);
|
||||||
$header=
|
$header=
|
||||||
[
|
[
|
||||||
@@ -148,6 +155,8 @@ try
|
|||||||
api_return(500, ['success' => false, 'error' => ERR::FIREBASE_COM_FAILED, 'errhighlight' => -1, 'message' => 'Communication with firebase service failed.'."\n\n".'Exception: ' . $e->getMessage()]);
|
api_return(500, ['success' => false, 'error' => ERR::FIREBASE_COM_FAILED, 'errhighlight' => -1, 'message' => 'Communication with firebase service failed.'."\n\n".'Exception: ' . $e->getMessage()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------
|
||||||
|
|
||||||
$stmt = $pdo->prepare('UPDATE users SET timestamp_accessed=NOW(), messages_sent=messages_sent+1, quota_today=:q, quota_day=NOW() WHERE user_id = :uid');
|
$stmt = $pdo->prepare('UPDATE users SET timestamp_accessed=NOW(), messages_sent=messages_sent+1, quota_today=:q, quota_day=NOW() WHERE user_id = :uid');
|
||||||
$stmt->execute(['uid' => $user_id, 'q' => $new_quota]);
|
$stmt->execute(['uid' => $user_id, 'q' => $new_quota]);
|
||||||
|
|
||||||
@@ -156,6 +165,8 @@ try
|
|||||||
|
|
||||||
$pdo->commit();
|
$pdo->commit();
|
||||||
|
|
||||||
|
//------------------------------------------------------------------
|
||||||
|
|
||||||
api_return(200,
|
api_return(200,
|
||||||
[
|
[
|
||||||
'success' => true,
|
'success' => true,
|
||||||
|