Compare commits
68 Commits
Author | SHA1 | Date | |
---|---|---|---|
b780ccea1c
|
|||
![]() |
5d8e871871 | ||
4655d688c9
|
|||
8263c0ad95
|
|||
a701afd09b
|
|||
![]() |
1e02d8c01f | ||
e4651375aa
|
|||
![]() |
afce4c6391 | ||
e95d0cb010
|
|||
f717355519
|
|||
3368e514ca
|
|||
9dca27177f
|
|||
c63274f7a9
|
|||
6c2d2d2345
|
|||
a6fbaa192f
|
|||
![]() |
a01f156535 | ||
3b021c09dc
|
|||
287176c881
|
|||
84eeb5a002
|
|||
b892532023
|
|||
![]() |
56cdc52bb4 | ||
9ef6b1dd91
|
|||
6f7585323b
|
|||
92135e64a3
|
|||
28efeea30b
|
|||
a8a074907b
|
|||
6c29ec9820
|
|||
![]() |
8ec23144ca | ||
b703853b28
|
|||
6f7529fc9b
|
|||
27b45098f0
|
|||
21f66d99cd
|
|||
1745051e6e
|
|||
![]() |
b395e054e6 | ||
90f4270b74
|
|||
f68d75c226
|
|||
9cf5133469
|
|||
75929aad48
|
|||
dca149ec4e
|
|||
75a7b97d24
|
|||
eb62873fb6
|
|||
f8effe7d1e
|
|||
87a3d34315
|
|||
0f38ad6e5c
|
|||
0ec6ab71b2
|
|||
b42204c87d
|
|||
53cb259ec1
|
|||
0150cc9e8e
|
|||
20214473f1
|
|||
![]() |
586ac07ef3 | ||
66f82f26d5
|
|||
6cb2aa00fb
|
|||
96a236803e
|
|||
9d24044eb3
|
|||
90248bcb54
|
|||
a3b2a1b14f
|
|||
b21b159b95
|
|||
93ec261dc0
|
|||
ae246e9219
|
|||
3f18fdd35a
|
|||
faf5207478
|
|||
71f003dd66
|
|||
3f85ab514e
|
|||
9eb5a6b1b9
|
|||
8e26cd6078
|
|||
36b9263730
|
|||
3d29fecaec
|
|||
![]() |
92ac05f1e3 |
22
README.md
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
SimpleCloudNotifier [](https://play.google.com/store/apps/details?id=com.blackforestbytes.simplecloudnotifier)
|
||||||
|
===================
|
||||||
|
|
||||||
|
> SimpleCloudNotifier is an app to display messages that you can send to your phone with simple POST requests.
|
||||||
|
>
|
||||||
|
> After you start the app it generates a UserID and a UserSecret.
|
||||||
|
> Now you can send your message to https://simplecloudnotifier.blackforestbytes.com/send.php and a notification will be pushed to your phone.
|
||||||
|
> (see https://simplecloudnotifier.blackforestbytes.com/ for an example with curl)
|
||||||
|
>
|
||||||
|
>
|
||||||
|
> Use it to
|
||||||
|
> - send yourself automated messages from cron jobs
|
||||||
|
> - notify yourself when long-running scripts finish
|
||||||
|
> - send server error messages directly to your phone
|
||||||
|
> - integrate with other online services
|
||||||
|
>
|
||||||
|
> The possibilities are endless*
|
||||||
|
>
|
||||||
|
> \* Disclaimer: Developer does not actually guarantee endless possibilities
|
||||||
|
|
||||||
|
|
||||||
|
  
|
57
android/.idea/assetWizardSettings.xml
generated
@@ -3,6 +3,59 @@
|
|||||||
<component name="WizardSettings">
|
<component name="WizardSettings">
|
||||||
<option name="children">
|
<option name="children">
|
||||||
<map>
|
<map>
|
||||||
|
<entry key="imageWizard">
|
||||||
|
<value>
|
||||||
|
<PersistentState>
|
||||||
|
<option name="children">
|
||||||
|
<map>
|
||||||
|
<entry key="imageAssetPanel">
|
||||||
|
<value>
|
||||||
|
<PersistentState>
|
||||||
|
<option name="children">
|
||||||
|
<map>
|
||||||
|
<entry key="launcherLegacy">
|
||||||
|
<value>
|
||||||
|
<PersistentState>
|
||||||
|
<option name="values">
|
||||||
|
<map>
|
||||||
|
<entry key="assetType" value="IMAGE" />
|
||||||
|
<entry key="cropped" value="true" />
|
||||||
|
<entry key="iconShape" value="NONE" />
|
||||||
|
<entry key="imageAsset" value="F:\Eigene Dateien\Dropbox\Programming\Java\AndroidStudioProjects\SimpleCloudNotifier\data\icon_512_nobox.png" />
|
||||||
|
<entry key="outputName" value="ic_notification_full" />
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</PersistentState>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
<entry key="notification">
|
||||||
|
<value>
|
||||||
|
<PersistentState>
|
||||||
|
<option name="values">
|
||||||
|
<map>
|
||||||
|
<entry key="assetType" value="IMAGE" />
|
||||||
|
<entry key="imageAsset" value="F:\Eigene Dateien\Dropbox\Programming\Java\AndroidStudioProjects\SimpleCloudNotifier\data\icon_512_transparent.png" />
|
||||||
|
<entry key="outputName" value="ic_notification_white" />
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</PersistentState>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
<option name="values">
|
||||||
|
<map>
|
||||||
|
<entry key="outputIconType" value="LAUNCHER_LEGACY" />
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</PersistentState>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</PersistentState>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
<entry key="vectorWizard">
|
<entry key="vectorWizard">
|
||||||
<value>
|
<value>
|
||||||
<PersistentState>
|
<PersistentState>
|
||||||
@@ -14,8 +67,8 @@
|
|||||||
<option name="values">
|
<option name="values">
|
||||||
<map>
|
<map>
|
||||||
<entry key="assetSourceType" value="FILE" />
|
<entry key="assetSourceType" value="FILE" />
|
||||||
<entry key="outputName" value="priority_low" />
|
<entry key="outputName" value="ic_garbage" />
|
||||||
<entry key="sourceFile" value="C:\Users\Mike\Downloads\Low Priority-595b40b75ba036ed117d9842.svg" />
|
<entry key="sourceFile" value="C:\Users\Mike\Downloads\garbage.svg" />
|
||||||
</map>
|
</map>
|
||||||
</option>
|
</option>
|
||||||
</PersistentState>
|
</PersistentState>
|
||||||
|
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
<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="com.android.vending.BILLING" />
|
<uses-permission android:name="com.android.vending.BILLING" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
@@ -35,6 +36,8 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
|
<receiver android:name=".service.BroadcastReceiverService" android:exported="false" />
|
||||||
|
|
||||||
</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 |
@@ -19,7 +19,7 @@ import androidx.lifecycle.ProcessLifecycleOwner;
|
|||||||
public class SCNApp extends Application implements LifecycleObserver
|
public class SCNApp extends Application implements LifecycleObserver
|
||||||
{
|
{
|
||||||
private static SCNApp instance;
|
private static SCNApp instance;
|
||||||
private static WeakReference<MainActivity> mainActivity;
|
private static WeakReference<MainActivity> mainActivity = new WeakReference<>(null);
|
||||||
|
|
||||||
public static final boolean LOCAL_DEBUG = BuildConfig.DEBUG;
|
public static final boolean LOCAL_DEBUG = BuildConfig.DEBUG;
|
||||||
public static final boolean DEBUG = BuildConfig.DEBUG || !BuildConfig.VERSION_NAME.endsWith(".0");
|
public static final boolean DEBUG = BuildConfig.DEBUG || !BuildConfig.VERSION_NAME.endsWith(".0");
|
||||||
@@ -99,32 +99,4 @@ public class SCNApp extends Application implements LifecycleObserver
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
//TODO TabLayout indicator does not corretly animate when directly clicking on tabs
|
||||||
==TODO==
|
|
||||||
|
|
||||||
[X] - Pro mode
|
|
||||||
[X] - no ads
|
|
||||||
[X] - more quota
|
|
||||||
[X] - restore pro mode
|
|
||||||
[X] - send pro state to server
|
|
||||||
|
|
||||||
[X] - prevent duplicate-send
|
|
||||||
[X] - send custom msg-id in API
|
|
||||||
[X] - prevent second ack on same msg-id
|
|
||||||
|
|
||||||
[X] - more in-depth API doc on website (?)
|
|
||||||
|
|
||||||
[X] - perhaps response codes in api (?)
|
|
||||||
|
|
||||||
[ ] - test notification channels
|
|
||||||
|
|
||||||
[ ] - publish (+ HN post ?)
|
|
||||||
|
|
||||||
[ ] - Use for mscom server errrors
|
|
||||||
[ ] - Use for bfb server errors
|
|
||||||
[ ] - Use for transmission state
|
|
||||||
[ ] - Message on connnection lost (seperate process - resend until succ)
|
|
||||||
[ ] - Message on connnection regained
|
|
||||||
[ ] - Message on seed-count changed
|
|
||||||
|
|
||||||
*/
|
|
@@ -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,29 @@
|
|||||||
|
package com.blackforestbytes.simplecloudnotifier.lib.lambda;
|
||||||
|
|
||||||
|
import android.widget.SeekBar;
|
||||||
|
|
||||||
|
public final class FI
|
||||||
|
{
|
||||||
|
private FI() throws InstantiationException { throw new InstantiationException(); }
|
||||||
|
|
||||||
|
public static SeekBar.OnSeekBarChangeListener SeekBarChanged(Func3to0<SeekBar, Integer, Boolean> action)
|
||||||
|
{
|
||||||
|
return new SeekBar.OnSeekBarChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||||
|
action.invoke(seekBar, progress, fromUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,6 @@
|
|||||||
|
package com.blackforestbytes.simplecloudnotifier.lib.lambda;
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface Func3to0<TInput1, TInput2, TInput3> {
|
||||||
|
void invoke(TInput1 value1, TInput2 value2, TInput3 value3);
|
||||||
|
}
|
@@ -0,0 +1,6 @@
|
|||||||
|
package com.blackforestbytes.simplecloudnotifier.lib.lambda;
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface Func4to0<TInput1, TInput2, TInput3, TInput4> {
|
||||||
|
void invoke(TInput1 value1, TInput2 value2, TInput3 value3, TInput4 value4);
|
||||||
|
}
|
@@ -0,0 +1,6 @@
|
|||||||
|
package com.blackforestbytes.simplecloudnotifier.lib.lambda;
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface Func5to0<TInput1, TInput2, TInput3, TInput4, TInput5> {
|
||||||
|
void invoke(TInput1 value1, TInput2 value2, TInput3 value3, TInput4 value4, TInput5 value5);
|
||||||
|
}
|
@@ -9,7 +9,8 @@ import java.util.TimeZone;
|
|||||||
|
|
||||||
public class CMessage
|
public class CMessage
|
||||||
{
|
{
|
||||||
public final long Timestamp ;
|
public final long SCN_ID;
|
||||||
|
public final long Timestamp;
|
||||||
public final String Title;
|
public final String Title;
|
||||||
public final String Content;
|
public final String Content;
|
||||||
public final PriorityEnum Priority;
|
public final PriorityEnum Priority;
|
||||||
@@ -21,8 +22,9 @@ public class CMessage
|
|||||||
_format.setTimeZone(TimeZone.getDefault());
|
_format.setTimeZone(TimeZone.getDefault());
|
||||||
}
|
}
|
||||||
|
|
||||||
public CMessage(long t, String mt, String mc, PriorityEnum p)
|
public CMessage(long id, long t, String mt, String mc, PriorityEnum p)
|
||||||
{
|
{
|
||||||
|
SCN_ID = id;
|
||||||
Timestamp = t;
|
Timestamp = t;
|
||||||
Title = mt;
|
Title = mt;
|
||||||
Content = mc;
|
Content = mc;
|
||||||
|
@@ -3,15 +3,23 @@ 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.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.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;
|
||||||
|
|
||||||
private ArrayList<WeakReference<MessageAdapter>> _listener = new ArrayList<>();
|
private ArrayList<WeakReference<MessageAdapter>> _listener = new ArrayList<>();
|
||||||
|
|
||||||
@@ -28,57 +36,88 @@ public class CMessageList
|
|||||||
|
|
||||||
private CMessageList()
|
private CMessageList()
|
||||||
{
|
{
|
||||||
Messages = new ArrayList<>();
|
synchronized (msg_lock)
|
||||||
|
|
||||||
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);
|
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));
|
|
||||||
|
|
||||||
Messages.add(new CMessage(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<>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public CMessage add(final long time, final String title, final String content, final PriorityEnum pe)
|
public CMessage add(final long scnid, final long time, final String title, final String content, final PriorityEnum pe)
|
||||||
{
|
{
|
||||||
CMessage msg = new CMessage(time, title, content, pe);
|
CMessage msg = new CMessage(scnid, time, title, content, pe);
|
||||||
|
|
||||||
boolean run = SCNApp.runOnUiThread(() ->
|
boolean run = SCNApp.runOnUiThread(() ->
|
||||||
{
|
{
|
||||||
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);
|
||||||
|
}
|
||||||
|
|
||||||
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.putInt( "message_count", count+1);
|
||||||
e.putString("message["+count+"].content", content);
|
e.putLong( "message["+count+"].timestamp", time);
|
||||||
e.putInt("message["+count+"].priority", pe.ID);
|
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();
|
||||||
}
|
}
|
||||||
CleanUpListener();
|
CleanUpListener();
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!run)
|
if (!run)
|
||||||
{
|
{
|
||||||
Messages.add(new CMessage(time, title, content, pe));
|
synchronized (msg_lock)
|
||||||
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;
|
||||||
@@ -100,33 +139,52 @@ 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);
|
|
||||||
e.putInt("message["+i+"].priority", Messages.get(i).Priority.ID);
|
|
||||||
}
|
|
||||||
|
|
||||||
e.apply();
|
SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("CMessageList", Context.MODE_PRIVATE);
|
||||||
|
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);
|
||||||
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
|
{
|
||||||
|
return tryGet(Messages.size() - pos - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
@@ -142,4 +200,33 @@ public class CMessageList
|
|||||||
if (_listener.get(i).get() == null) _listener.remove(i);
|
if (_listener.get(i).get() == null) _listener.remove(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isAck(long id)
|
||||||
|
{
|
||||||
|
synchronized (msg_lock)
|
||||||
|
{
|
||||||
|
return AllAcks.contains(Long.toHexString(id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public CMessage removeFromBack(int pos)
|
||||||
|
{
|
||||||
|
CMessage r;
|
||||||
|
synchronized (msg_lock)
|
||||||
|
{
|
||||||
|
int index = Messages.size() - pos - 1;
|
||||||
|
r = Messages.remove(index);
|
||||||
|
}
|
||||||
|
fullSave(); // does sort in here
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void insert(int index, CMessage item)
|
||||||
|
{
|
||||||
|
synchronized (msg_lock)
|
||||||
|
{
|
||||||
|
Messages.add(index, item);
|
||||||
|
}
|
||||||
|
fullSave(); // does sort in here
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,17 +1,34 @@
|
|||||||
package com.blackforestbytes.simplecloudnotifier.model;
|
package com.blackforestbytes.simplecloudnotifier.model;
|
||||||
|
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
|
import android.media.RingtoneManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
|
||||||
public class NotificationSettings
|
public class NotificationSettings
|
||||||
{
|
{
|
||||||
public boolean EnableSound = false;
|
public boolean EnableSound;
|
||||||
public String SoundName = "";
|
public String SoundName;
|
||||||
public String SoundSource = Uri.EMPTY.toString();
|
public String SoundSource;
|
||||||
public boolean RepeatSound = false;
|
public boolean RepeatSound;
|
||||||
|
|
||||||
public boolean EnableLED = false;
|
public boolean ForceVolume;
|
||||||
public int LEDColor = Color.BLUE;
|
public int ForceVolumeValue;
|
||||||
|
|
||||||
public boolean EnableVibration = false;
|
public boolean EnableLED;
|
||||||
|
public int LEDColor;
|
||||||
|
|
||||||
|
public boolean EnableVibration;
|
||||||
|
|
||||||
|
public NotificationSettings(PriorityEnum p)
|
||||||
|
{
|
||||||
|
EnableSound = (p == PriorityEnum.HIGH);
|
||||||
|
SoundName = "Default";
|
||||||
|
SoundSource = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION).toString();
|
||||||
|
RepeatSound = false;
|
||||||
|
EnableLED = (p == PriorityEnum.HIGH) || (p == PriorityEnum.NORMAL);
|
||||||
|
LEDColor = Color.BLUE;
|
||||||
|
EnableVibration = (p == PriorityEnum.HIGH) || (p == PriorityEnum.NORMAL);
|
||||||
|
ForceVolume = false;
|
||||||
|
ForceVolumeValue = 50;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -8,20 +8,26 @@ import android.view.View;
|
|||||||
|
|
||||||
import com.android.billingclient.api.Purchase;
|
import com.android.billingclient.api.Purchase;
|
||||||
import com.blackforestbytes.simplecloudnotifier.SCNApp;
|
import com.blackforestbytes.simplecloudnotifier.SCNApp;
|
||||||
|
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;
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------
|
// ------------------------------------------------------------
|
||||||
@@ -46,10 +52,11 @@ public class SCNSettings
|
|||||||
|
|
||||||
public boolean Enabled = true;
|
public boolean Enabled = true;
|
||||||
public int LocalCacheSize = 500;
|
public int LocalCacheSize = 500;
|
||||||
|
public boolean EnableDeleteSwipe = true;
|
||||||
|
|
||||||
public final NotificationSettings PriorityLow = new NotificationSettings();
|
public final NotificationSettings PriorityLow = new NotificationSettings(PriorityEnum.LOW);
|
||||||
public final NotificationSettings PriorityNorm = new NotificationSettings();
|
public final NotificationSettings PriorityNorm = new NotificationSettings(PriorityEnum.NORMAL);
|
||||||
public final NotificationSettings PriorityHigh = new NotificationSettings();
|
public final NotificationSettings PriorityHigh = new NotificationSettings(PriorityEnum.HIGH);
|
||||||
|
|
||||||
// ------------------------------------------------------------
|
// ------------------------------------------------------------
|
||||||
|
|
||||||
@@ -69,30 +76,37 @@ 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);
|
||||||
|
|
||||||
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);
|
||||||
PriorityLow.EnableVibration = sharedPref.getBoolean("priority_low:enabled_vibration", PriorityLow.EnableVibration);
|
PriorityLow.EnableVibration = sharedPref.getBoolean("priority_low:enabled_vibration", PriorityLow.EnableVibration);
|
||||||
PriorityLow.RepeatSound = sharedPref.getBoolean("priority_low:repeat_sound", PriorityLow.RepeatSound);
|
PriorityLow.RepeatSound = sharedPref.getBoolean("priority_low:repeat_sound", PriorityLow.RepeatSound);
|
||||||
PriorityLow.SoundName = sharedPref.getString( "priority_low:sound_name", PriorityLow.SoundName);
|
PriorityLow.SoundName = sharedPref.getString( "priority_low:sound_name", PriorityLow.SoundName);
|
||||||
PriorityLow.SoundSource = sharedPref.getString( "priority_low:sound_source", PriorityLow.SoundSource);
|
PriorityLow.SoundSource = sharedPref.getString( "priority_low:sound_source", PriorityLow.SoundSource);
|
||||||
PriorityLow.LEDColor = sharedPref.getInt( "priority_low:led_color", PriorityLow.LEDColor);
|
PriorityLow.LEDColor = sharedPref.getInt( "priority_low:led_color", PriorityLow.LEDColor);
|
||||||
|
PriorityLow.ForceVolume = sharedPref.getBoolean("priority_low:force_volume", PriorityLow.ForceVolume);
|
||||||
|
PriorityLow.ForceVolumeValue = sharedPref.getInt( "priority_low:force_volume_value", PriorityLow.ForceVolumeValue);
|
||||||
|
|
||||||
PriorityNorm.EnableLED = sharedPref.getBoolean("priority_norm:enabled_led", PriorityNorm.EnableLED);
|
PriorityNorm.EnableLED = sharedPref.getBoolean("priority_norm:enabled_led", PriorityNorm.EnableLED);
|
||||||
PriorityNorm.EnableSound = sharedPref.getBoolean("priority_norm:enabled_sound", PriorityNorm.EnableSound);
|
PriorityNorm.EnableSound = sharedPref.getBoolean("priority_norm:enabled_sound", PriorityNorm.EnableSound);
|
||||||
PriorityNorm.EnableVibration = sharedPref.getBoolean("priority_norm:enabled_vibration", PriorityNorm.EnableVibration);
|
PriorityNorm.EnableVibration = sharedPref.getBoolean("priority_norm:enabled_vibration", PriorityNorm.EnableVibration);
|
||||||
PriorityNorm.RepeatSound = sharedPref.getBoolean("priority_norm:repeat_sound", PriorityNorm.RepeatSound);
|
PriorityNorm.RepeatSound = sharedPref.getBoolean("priority_norm:repeat_sound", PriorityNorm.RepeatSound);
|
||||||
PriorityNorm.SoundName = sharedPref.getString( "priority_norm:sound_name", PriorityNorm.SoundName);
|
PriorityNorm.SoundName = sharedPref.getString( "priority_norm:sound_name", PriorityNorm.SoundName);
|
||||||
PriorityNorm.SoundSource = sharedPref.getString( "priority_norm:sound_source", PriorityNorm.SoundSource);
|
PriorityNorm.SoundSource = sharedPref.getString( "priority_norm:sound_source", PriorityNorm.SoundSource);
|
||||||
PriorityNorm.LEDColor = sharedPref.getInt( "priority_norm:led_color", PriorityNorm.LEDColor);
|
PriorityNorm.LEDColor = sharedPref.getInt( "priority_norm:led_color", PriorityNorm.LEDColor);
|
||||||
|
PriorityNorm.ForceVolume = sharedPref.getBoolean("priority_norm:force_volume", PriorityNorm.ForceVolume);
|
||||||
|
PriorityNorm.ForceVolumeValue = sharedPref.getInt( "priority_norm:force_volume_value", PriorityNorm.ForceVolumeValue);
|
||||||
|
|
||||||
PriorityHigh.EnableLED = sharedPref.getBoolean("priority_high:enabled_led", PriorityHigh.EnableLED);
|
PriorityHigh.EnableLED = sharedPref.getBoolean("priority_high:enabled_led", PriorityHigh.EnableLED);
|
||||||
PriorityHigh.EnableSound = sharedPref.getBoolean("priority_high:enabled_sound", PriorityHigh.EnableSound);
|
PriorityHigh.EnableSound = sharedPref.getBoolean("priority_high:enabled_sound", PriorityHigh.EnableSound);
|
||||||
PriorityHigh.EnableVibration = sharedPref.getBoolean("priority_high:enabled_vibration", PriorityHigh.EnableVibration);
|
PriorityHigh.EnableVibration = sharedPref.getBoolean("priority_high:enabled_vibration", PriorityHigh.EnableVibration);
|
||||||
PriorityHigh.RepeatSound = sharedPref.getBoolean("priority_high:repeat_sound", PriorityHigh.RepeatSound);
|
PriorityHigh.RepeatSound = sharedPref.getBoolean("priority_high:repeat_sound", PriorityHigh.RepeatSound);
|
||||||
PriorityHigh.SoundName = sharedPref.getString( "priority_high:sound_name", PriorityHigh.SoundName);
|
PriorityHigh.SoundName = sharedPref.getString( "priority_high:sound_name", PriorityHigh.SoundName);
|
||||||
PriorityHigh.SoundSource = sharedPref.getString( "priority_high:sound_source", PriorityHigh.SoundSource);
|
PriorityHigh.SoundSource = sharedPref.getString( "priority_high:sound_source", PriorityHigh.SoundSource);
|
||||||
PriorityHigh.LEDColor = sharedPref.getInt( "priority_high:led_color", PriorityHigh.LEDColor);
|
PriorityHigh.LEDColor = sharedPref.getInt( "priority_high:led_color", PriorityHigh.LEDColor);
|
||||||
|
PriorityHigh.ForceVolume = sharedPref.getBoolean("priority_high:force_volume", PriorityHigh.ForceVolume);
|
||||||
|
PriorityHigh.ForceVolumeValue = sharedPref.getInt( "priority_high:force_volume_value", PriorityHigh.ForceVolumeValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void save()
|
public void save()
|
||||||
@@ -100,39 +114,46 @@ public class SCNSettings
|
|||||||
SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("Config", Context.MODE_PRIVATE);
|
SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("Config", Context.MODE_PRIVATE);
|
||||||
SharedPreferences.Editor e = sharedPref.edit();
|
SharedPreferences.Editor e = sharedPref.edit();
|
||||||
|
|
||||||
e.putInt( "quota_curr", quota_curr);
|
e.putInt( "quota_curr", quota_curr);
|
||||||
e.putInt( "quota_max", quota_max);
|
e.putInt( "quota_max", quota_max);
|
||||||
e.putInt( "user_id", user_id);
|
e.putInt( "user_id", user_id);
|
||||||
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("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.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);
|
||||||
e.putBoolean("priority_low:enabled_vibration", PriorityLow.EnableVibration);
|
e.putBoolean("priority_low:enabled_vibration", PriorityLow.EnableVibration);
|
||||||
e.putBoolean("priority_low:repeat_sound", PriorityLow.RepeatSound);
|
e.putBoolean("priority_low:repeat_sound", PriorityLow.RepeatSound);
|
||||||
e.putString( "priority_low:sound_name", PriorityLow.SoundName);
|
e.putString( "priority_low:sound_name", PriorityLow.SoundName);
|
||||||
e.putString( "priority_low:sound_source", PriorityLow.SoundSource);
|
e.putString( "priority_low:sound_source", PriorityLow.SoundSource);
|
||||||
e.putInt( "priority_low:led_color", PriorityLow.LEDColor);
|
e.putInt( "priority_low:led_color", PriorityLow.LEDColor);
|
||||||
|
e.putBoolean("priority_low:force_volume", PriorityLow.ForceVolume);
|
||||||
|
e.putInt( "priority_low:force_volume_value", PriorityLow.ForceVolumeValue);
|
||||||
|
|
||||||
e.putBoolean("priority_norm:enabled_led", PriorityNorm.EnableLED);
|
e.putBoolean("priority_norm:enabled_led", PriorityNorm.EnableLED);
|
||||||
e.putBoolean("priority_norm:enabled_sound", PriorityNorm.EnableSound);
|
e.putBoolean("priority_norm:enabled_sound", PriorityNorm.EnableSound);
|
||||||
e.putBoolean("priority_norm:enabled_vibration", PriorityNorm.EnableVibration);
|
e.putBoolean("priority_norm:enabled_vibration", PriorityNorm.EnableVibration);
|
||||||
e.putBoolean("priority_norm:repeat_sound", PriorityNorm.RepeatSound);
|
e.putBoolean("priority_norm:repeat_sound", PriorityNorm.RepeatSound);
|
||||||
e.putString( "priority_norm:sound_name", PriorityNorm.SoundName);
|
e.putString( "priority_norm:sound_name", PriorityNorm.SoundName);
|
||||||
e.putString( "priority_norm:sound_source", PriorityNorm.SoundSource);
|
e.putString( "priority_norm:sound_source", PriorityNorm.SoundSource);
|
||||||
e.putInt( "priority_norm:led_color", PriorityNorm.LEDColor);
|
e.putInt( "priority_norm:led_color", PriorityNorm.LEDColor);
|
||||||
|
e.putBoolean("priority_norm:force_volume", PriorityNorm.ForceVolume);
|
||||||
|
e.putInt( "priority_norm:force_volume_value", PriorityNorm.ForceVolumeValue);
|
||||||
|
|
||||||
e.putBoolean("priority_high:enabled_led", PriorityHigh.EnableLED);
|
e.putBoolean("priority_high:enabled_led", PriorityHigh.EnableLED);
|
||||||
e.putBoolean("priority_high:enabled_sound", PriorityHigh.EnableSound);
|
e.putBoolean("priority_high:enabled_sound", PriorityHigh.EnableSound);
|
||||||
e.putBoolean("priority_high:enabled_vibration", PriorityHigh.EnableVibration);
|
e.putBoolean("priority_high:enabled_vibration", PriorityHigh.EnableVibration);
|
||||||
e.putBoolean("priority_high:repeat_sound", PriorityHigh.RepeatSound);
|
e.putBoolean("priority_high:repeat_sound", PriorityHigh.RepeatSound);
|
||||||
e.putString( "priority_high:sound_name", PriorityHigh.SoundName);
|
e.putString( "priority_high:sound_name", PriorityHigh.SoundName);
|
||||||
e.putString( "priority_high:sound_source", PriorityHigh.SoundSource);
|
e.putString( "priority_high:sound_source", PriorityHigh.SoundSource);
|
||||||
e.putInt( "priority_high:led_color", PriorityHigh.LEDColor);
|
e.putInt( "priority_high:led_color", PriorityHigh.LEDColor);
|
||||||
|
e.putBoolean("priority_high:force_volume", PriorityHigh.ForceVolume);
|
||||||
|
e.putInt( "priority_high:force_volume_value", PriorityHigh.ForceVolumeValue);
|
||||||
|
|
||||||
e.apply();
|
e.apply();
|
||||||
}
|
}
|
||||||
@@ -142,10 +163,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)
|
||||||
@@ -154,7 +177,7 @@ public class SCNSettings
|
|||||||
{
|
{
|
||||||
fcm_token_local = token;
|
fcm_token_local = token;
|
||||||
save();
|
save();
|
||||||
if (!fcm_token_local.equals(fcm_token_server)) ServerCommunication.update(user_id, user_key, fcm_token_local, loader);
|
if (!fcm_token_local.equals(fcm_token_server)) ServerCommunication.updateFCMToken(user_id, user_key, fcm_token_local, loader);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -171,7 +194,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 ->
|
||||||
{
|
{
|
||||||
@@ -186,7 +209,7 @@ public class SCNSettings
|
|||||||
{
|
{
|
||||||
if (!isConnected()) return;
|
if (!isConnected()) return;
|
||||||
|
|
||||||
ServerCommunication.update(user_id, user_key, loader);
|
ServerCommunication.resetSecret(user_id, user_key, loader);
|
||||||
}
|
}
|
||||||
|
|
||||||
// refresh account data
|
// refresh account data
|
||||||
@@ -195,10 +218,10 @@ public class SCNSettings
|
|||||||
if (isConnected())
|
if (isConnected())
|
||||||
{
|
{
|
||||||
ServerCommunication.info(user_id, user_key, loader);
|
ServerCommunication.info(user_id, user_key, loader);
|
||||||
if (promode_server != promode_local)
|
|
||||||
{
|
if (promode_server != promode_local) updateProState(loader);
|
||||||
updateProState(loader);
|
|
||||||
}
|
if (!Str.equals(fcm_token_local, fcm_token_server)) work(a);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -206,7 +229,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 ->
|
||||||
{
|
{
|
||||||
|
@@ -4,7 +4,14 @@ 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.datatypes.Tuple5;
|
||||||
|
import com.blackforestbytes.simplecloudnotifier.lib.lambda.Func1to0;
|
||||||
|
import com.blackforestbytes.simplecloudnotifier.lib.lambda.Func5to0;
|
||||||
|
import com.blackforestbytes.simplecloudnotifier.lib.string.Str;
|
||||||
|
import com.blackforestbytes.simplecloudnotifier.service.FBMService;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
import org.json.JSONTokener;
|
import org.json.JSONTokener;
|
||||||
|
|
||||||
@@ -20,7 +27,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/";
|
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();
|
||||||
|
|
||||||
@@ -39,7 +48,7 @@ public class ServerCommunication
|
|||||||
@Override
|
@Override
|
||||||
public void onFailure(Call call, IOException e)
|
public void onFailure(Call call, IOException e)
|
||||||
{
|
{
|
||||||
e.printStackTrace();
|
Log.e("SC:register", e.toString());
|
||||||
SCNApp.showToast("Communication with server failed", 4000);
|
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); });
|
||||||
}
|
}
|
||||||
@@ -53,29 +62,29 @@ public class ServerCommunication
|
|||||||
if (responseBody == null) throw new IOException("No response");
|
if (responseBody == null) throw new IOException("No response");
|
||||||
|
|
||||||
String r = responseBody.string();
|
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.getBoolean("success"))
|
if (!json_bool(json, "success"))
|
||||||
{
|
{
|
||||||
SCNApp.showToast(json.getString("message"), 4000);
|
SCNApp.showToast(json_str(json, "message"), 4000);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SCNSettings.inst().user_id = json.getInt("user_id");
|
SCNSettings.inst().user_id = json_int(json, "user_id");
|
||||||
SCNSettings.inst().user_key = json.getString("user_key");
|
SCNSettings.inst().user_key = json_str(json, "user_key");
|
||||||
SCNSettings.inst().fcm_token_server = token;
|
SCNSettings.inst().fcm_token_server = token;
|
||||||
SCNSettings.inst().quota_curr = json.getInt("quota");
|
SCNSettings.inst().quota_curr = json_int(json, "quota");
|
||||||
SCNSettings.inst().quota_max = json.getInt("quota_max");
|
SCNSettings.inst().quota_max = json_int(json, "quota_max");
|
||||||
SCNSettings.inst().promode_server = json.getBoolean("is_pro");
|
SCNSettings.inst().promode_server = json_bool(json, "is_pro");
|
||||||
SCNSettings.inst().save();
|
SCNSettings.inst().save();
|
||||||
|
|
||||||
SCNApp.refreshAccountTab();
|
SCNApp.refreshAccountTab();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
e.printStackTrace();
|
Log.e("SC:register", e.toString());
|
||||||
SCNApp.showToast("Communication with server failed", 4000);
|
SCNApp.showToast("Communication with server failed", 4000);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@@ -87,17 +96,17 @@ public class ServerCommunication
|
|||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
e.printStackTrace();
|
Log.e("SC:register", e.toString());
|
||||||
SCNApp.showToast("Communication with server failed", 4000);
|
SCNApp.showToast("Communication with server failed", 4000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void update(int id, String key, String token, View loader)
|
public static void updateFCMToken(int id, String key, String token, View loader)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Request request = new Request.Builder()
|
Request request = new Request.Builder()
|
||||||
.url(BASE_URL + "update.php?user_id="+id+"&user_key="+key+"&fcm_token="+token)
|
.url(BASE_URL + "updateFCMToken.php?user_id="+id+"&user_key="+key+"&fcm_token="+token)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
client.newCall(request).enqueue(new Callback()
|
client.newCall(request).enqueue(new Callback()
|
||||||
@@ -105,7 +114,7 @@ public class ServerCommunication
|
|||||||
@Override
|
@Override
|
||||||
public void onFailure(Call call, IOException e)
|
public void onFailure(Call call, IOException e)
|
||||||
{
|
{
|
||||||
e.printStackTrace();
|
Log.e("SC:update_1", e.toString());
|
||||||
SCNApp.showToast("Communication with server failed", 4000);
|
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); });
|
||||||
}
|
}
|
||||||
@@ -119,29 +128,29 @@ public class ServerCommunication
|
|||||||
if (responseBody == null) throw new IOException("No response");
|
if (responseBody == null) throw new IOException("No response");
|
||||||
|
|
||||||
String r = responseBody.string();
|
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.getBoolean("success"))
|
if (!json_bool(json, "success"))
|
||||||
{
|
{
|
||||||
SCNApp.showToast(json.getString("message"), 4000);
|
SCNApp.showToast(json_str(json, "message"), 4000);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SCNSettings.inst().user_id = json.getInt("user_id");
|
SCNSettings.inst().user_id = json_int(json, "user_id");
|
||||||
SCNSettings.inst().user_key = json.getString("user_key");
|
SCNSettings.inst().user_key = json_str(json, "user_key");
|
||||||
SCNSettings.inst().fcm_token_server = token;
|
SCNSettings.inst().fcm_token_server = token;
|
||||||
SCNSettings.inst().quota_curr = json.getInt("quota");
|
SCNSettings.inst().quota_curr = json_int(json, "quota");
|
||||||
SCNSettings.inst().quota_max = json.getInt("quota_max");
|
SCNSettings.inst().quota_max = json_int(json, "quota_max");
|
||||||
SCNSettings.inst().promode_server = json.getBoolean("is_pro");
|
SCNSettings.inst().promode_server = json_bool(json, "is_pro");
|
||||||
SCNSettings.inst().save();
|
SCNSettings.inst().save();
|
||||||
|
|
||||||
SCNApp.refreshAccountTab();
|
SCNApp.refreshAccountTab();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
e.printStackTrace();
|
Log.e("SC:update_1", e.toString());
|
||||||
SCNApp.showToast("Communication with server failed", 4000);
|
SCNApp.showToast("Communication with server failed", 4000);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@@ -153,23 +162,23 @@ public class ServerCommunication
|
|||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
e.printStackTrace();
|
Log.e("SC:update_1", e.toString());
|
||||||
SCNApp.showToast("Communication with server failed", 4000);
|
SCNApp.showToast("Communication with server failed", 4000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void update(int id, String key, View loader)
|
public static void resetSecret(int id, String key, View loader)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Request request = new Request.Builder()
|
Request request = new Request.Builder()
|
||||||
.url(BASE_URL + "update.php?user_id=" + id + "&user_key=" + key)
|
.url(BASE_URL + "updateFCMToken.php?user_id=" + id + "&user_key=" + key)
|
||||||
.build();
|
.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) {
|
||||||
e.printStackTrace();
|
Log.e("SC:update_2", e.toString());
|
||||||
SCNApp.showToast("Communication with server failed", 4000);
|
SCNApp.showToast("Communication with server failed", 4000);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,25 +190,25 @@ public class ServerCommunication
|
|||||||
if (responseBody == null) throw new IOException("No response");
|
if (responseBody == null) throw new IOException("No response");
|
||||||
|
|
||||||
String r = responseBody.string();
|
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.getBoolean("success")) {
|
if (!json_bool(json, "success")) {
|
||||||
SCNApp.showToast(json.getString("message"), 4000);
|
SCNApp.showToast(json_str(json, "message"), 4000);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SCNSettings.inst().user_id = json.getInt("user_id");
|
SCNSettings.inst().user_id = json_int(json, "user_id");
|
||||||
SCNSettings.inst().user_key = json.getString("user_key");
|
SCNSettings.inst().user_key = json_str(json, "user_key");
|
||||||
SCNSettings.inst().quota_curr = json.getInt("quota");
|
SCNSettings.inst().quota_curr = json_int(json, "quota");
|
||||||
SCNSettings.inst().quota_max = json.getInt("quota_max");
|
SCNSettings.inst().quota_max = json_int(json, "quota_max");
|
||||||
SCNSettings.inst().promode_server = json.getBoolean("is_pro");
|
SCNSettings.inst().promode_server = json_bool(json, "is_pro");
|
||||||
SCNSettings.inst().save();
|
SCNSettings.inst().save();
|
||||||
|
|
||||||
SCNApp.refreshAccountTab();
|
SCNApp.refreshAccountTab();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
Log.e("SC:update_2", e.toString());
|
||||||
SCNApp.showToast("Communication with server failed", 4000);
|
SCNApp.showToast("Communication with server failed", 4000);
|
||||||
} finally {
|
} finally {
|
||||||
SCNApp.runOnUiThread(() -> {
|
SCNApp.runOnUiThread(() -> {
|
||||||
@@ -211,7 +220,7 @@ public class ServerCommunication
|
|||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
e.printStackTrace();
|
Log.e("SC:update_2", e.toString());
|
||||||
SCNApp.showToast("Communication with server failed", 4000);
|
SCNApp.showToast("Communication with server failed", 4000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -227,7 +236,7 @@ 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) {
|
||||||
e.printStackTrace();
|
Log.e("SC:info", e.toString());
|
||||||
SCNApp.showToast("Communication with server failed", 4000);
|
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);
|
||||||
@@ -242,24 +251,47 @@ public class ServerCommunication
|
|||||||
if (responseBody == null) throw new IOException("No response");
|
if (responseBody == null) throw new IOException("No response");
|
||||||
|
|
||||||
String r = responseBody.string();
|
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.getBoolean("success")) {
|
if (!json_bool(json, "success"))
|
||||||
SCNApp.showToast(json.getString("message"), 4000);
|
{
|
||||||
|
SCNApp.showToast(json_str(json, "message"), 4000);
|
||||||
|
|
||||||
|
int errid = json.optInt("errid", 0);
|
||||||
|
|
||||||
|
if (errid == 201 || errid == 202 || errid == 203 || errid == 204)
|
||||||
|
{
|
||||||
|
// user not found or auth failed
|
||||||
|
|
||||||
|
SCNSettings.inst().user_id = -1;
|
||||||
|
SCNSettings.inst().user_key = "";
|
||||||
|
SCNSettings.inst().quota_curr = 0;
|
||||||
|
SCNSettings.inst().quota_max = 0;
|
||||||
|
SCNSettings.inst().promode_server = false;
|
||||||
|
SCNSettings.inst().fcm_token_server = "";
|
||||||
|
SCNSettings.inst().save();
|
||||||
|
|
||||||
|
SCNApp.refreshAccountTab();
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SCNSettings.inst().user_id = json.getInt("user_id");
|
SCNSettings.inst().user_id = json_int(json, "user_id");
|
||||||
SCNSettings.inst().quota_curr = json.getInt("quota");
|
SCNSettings.inst().quota_curr = json_int(json, "quota");
|
||||||
SCNSettings.inst().quota_max = json.getInt("quota_max");
|
SCNSettings.inst().quota_max = json_int(json, "quota_max");
|
||||||
SCNSettings.inst().promode_server = json.getBoolean("is_pro");
|
SCNSettings.inst().promode_server = json_bool(json, "is_pro");
|
||||||
|
if (!json_bool(json, "fcm_token_set")) SCNSettings.inst().fcm_token_server = "";
|
||||||
SCNSettings.inst().save();
|
SCNSettings.inst().save();
|
||||||
|
|
||||||
SCNApp.refreshAccountTab();
|
SCNApp.refreshAccountTab();
|
||||||
|
|
||||||
|
if (json_int(json, "unack_count")>0) ServerCommunication.requery(id, key, loader);
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
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(() -> {
|
||||||
@@ -271,7 +303,76 @@ public class ServerCommunication
|
|||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
e.printStackTrace();
|
Log.e("SC:info", e.toString());
|
||||||
|
SCNApp.showToast("Communication with server failed", 4000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void requery(int id, String key, View loader)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Request request = new Request.Builder()
|
||||||
|
.url(BASE_URL + "requery.php?user_id=" + id + "&user_key=" + key)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
client.newCall(request).enqueue(new Callback() {
|
||||||
|
@Override
|
||||||
|
public void onFailure(Call call, IOException e) {
|
||||||
|
Log.e("SC:requery", e.toString());
|
||||||
|
SCNApp.showToast("Communication with server failed", 4000);
|
||||||
|
SCNApp.runOnUiThread(() -> {
|
||||||
|
if (loader != null) loader.setVisibility(View.GONE);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResponse(Call call, Response response) {
|
||||||
|
try (ResponseBody responseBody = response.body()) {
|
||||||
|
if (!response.isSuccessful())
|
||||||
|
throw new IOException("Unexpected code " + response);
|
||||||
|
if (responseBody == null) throw new IOException("No response");
|
||||||
|
|
||||||
|
String r = responseBody.string();
|
||||||
|
Log.d("Server::Response", request.url().toString()+"\n"+r);
|
||||||
|
|
||||||
|
JSONObject json = (JSONObject) new JSONTokener(r).nextValue();
|
||||||
|
|
||||||
|
if (!json_bool(json, "success"))
|
||||||
|
{
|
||||||
|
SCNApp.showToast(json_str(json, "message"), 4000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int count = json_int(json, "count");
|
||||||
|
JSONArray arr = json.getJSONArray("data");
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
JSONObject o = arr.getJSONObject(i);
|
||||||
|
|
||||||
|
long time = json_lng(o, "timestamp");
|
||||||
|
String title = json_str(o, "title");
|
||||||
|
String content = json_str(o, "body");
|
||||||
|
PriorityEnum prio = PriorityEnum.parseAPI(json_int(o, "priority"));
|
||||||
|
long scn_id = json_lng(o, "scn_msg_id");
|
||||||
|
|
||||||
|
FBMService.recieveData(time, title, content, prio, scn_id, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e("SC:info", e.toString());
|
||||||
|
SCNApp.showToast("Communication with server failed", 4000);
|
||||||
|
} finally {
|
||||||
|
SCNApp.runOnUiThread(() -> {
|
||||||
|
if (loader != null) loader.setVisibility(View.GONE);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.e("SC:requery", e.toString());
|
||||||
SCNApp.showToast("Communication with server failed", 4000);
|
SCNApp.showToast("Communication with server failed", 4000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -289,7 +390,7 @@ 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) {
|
||||||
e.printStackTrace();
|
Log.e("SC:upgrade", e.toString());
|
||||||
SCNApp.showToast("Communication with server failed", 4000);
|
SCNApp.showToast("Communication with server failed", 4000);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -301,25 +402,24 @@ public class ServerCommunication
|
|||||||
if (responseBody == null) throw new IOException("No response");
|
if (responseBody == null) throw new IOException("No response");
|
||||||
|
|
||||||
String r = responseBody.string();
|
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.getBoolean("success")) {
|
if (!json_bool(json, "success")) {
|
||||||
SCNApp.showToast(json.getString("message"), 4000);
|
SCNApp.showToast(json_str(json, "message"), 4000);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SCNSettings.inst().user_id = json.getInt("user_id");
|
SCNSettings.inst().user_id = json_int(json, "user_id");
|
||||||
SCNSettings.inst().user_key = json.getString("user_key");
|
SCNSettings.inst().quota_curr = json_int(json, "quota");
|
||||||
SCNSettings.inst().quota_curr = json.getInt("quota");
|
SCNSettings.inst().quota_max = json_int(json, "quota_max");
|
||||||
SCNSettings.inst().quota_max = json.getInt("quota_max");
|
SCNSettings.inst().promode_server = json_bool(json, "is_pro");
|
||||||
SCNSettings.inst().promode_server = json.getBoolean("is_pro");
|
|
||||||
SCNSettings.inst().save();
|
SCNSettings.inst().save();
|
||||||
|
|
||||||
SCNApp.refreshAccountTab();
|
SCNApp.refreshAccountTab();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
Log.e("SC:upgrade", e.toString());
|
||||||
SCNApp.showToast("Communication with server failed", 4000);
|
SCNApp.showToast("Communication with server failed", 4000);
|
||||||
} finally {
|
} finally {
|
||||||
SCNApp.runOnUiThread(() -> { if (loader != null) loader.setVisibility(View.GONE); });
|
SCNApp.runOnUiThread(() -> { if (loader != null) loader.setVisibility(View.GONE); });
|
||||||
@@ -334,4 +434,134 @@ public class ServerCommunication
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void ack(int id, String key, long msg_scn_id)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Request request = new Request.Builder()
|
||||||
|
.url(BASE_URL + "ack.php?user_id=" + id + "&user_key=" + key + "&scn_msg_id=" + msg_scn_id)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
client.newCall(request).enqueue(new Callback() {
|
||||||
|
@Override
|
||||||
|
public void onFailure(Call call, IOException e) {
|
||||||
|
Log.e("SC:ack", e.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResponse(Call call, Response response)
|
||||||
|
{
|
||||||
|
try (ResponseBody responseBody = response.body()) {
|
||||||
|
if (!response.isSuccessful())
|
||||||
|
throw new IOException("Unexpected code " + response);
|
||||||
|
if (responseBody == null) throw new IOException("No response");
|
||||||
|
|
||||||
|
String r = responseBody.string();
|
||||||
|
Log.d("Server::Response", request.url().toString()+"\n"+r);
|
||||||
|
|
||||||
|
JSONObject json = (JSONObject) new JSONTokener(r).nextValue();
|
||||||
|
|
||||||
|
if (!json_bool(json, "success")) SCNApp.showToast(json_str(json, "message"), 4000);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e("SC:ack", e.toString());
|
||||||
|
SCNApp.showToast("Communication with server failed", 4000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.e("SC:ack", e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void expand(int id, String key, long scn_msg_id, View loader, Func5to0<String, String, PriorityEnum, Long, Long> okResult)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Request request = new Request.Builder()
|
||||||
|
.url(BASE_URL + "expand.php?user_id=" + id + "&user_key=" + key + "&scn_msg_id=" + scn_msg_id)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
client.newCall(request).enqueue(new Callback() {
|
||||||
|
@Override
|
||||||
|
public void onFailure(Call call, IOException e) {
|
||||||
|
Log.e("SC:expand", e.toString());
|
||||||
|
SCNApp.showToast("Communication with server failed", 4000);
|
||||||
|
SCNApp.runOnUiThread(() -> {
|
||||||
|
if (loader != null) loader.setVisibility(View.GONE);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResponse(Call call, Response response) {
|
||||||
|
try (ResponseBody responseBody = response.body()) {
|
||||||
|
if (!response.isSuccessful())
|
||||||
|
throw new IOException("Unexpected code " + response);
|
||||||
|
if (responseBody == null) throw new IOException("No response");
|
||||||
|
|
||||||
|
String r = responseBody.string();
|
||||||
|
Log.d("Server::Response", request.url().toString()+"\n"+r);
|
||||||
|
|
||||||
|
JSONObject json = (JSONObject) new JSONTokener(r).nextValue();
|
||||||
|
|
||||||
|
if (!json_bool(json, "success"))
|
||||||
|
{
|
||||||
|
SCNApp.showToast(json_str(json, "message"), 4000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONObject o = json.getJSONObject("data");
|
||||||
|
|
||||||
|
long time = json_lng(o, "timestamp");
|
||||||
|
String title = json_str(o, "title");
|
||||||
|
String content = json_str(o, "body");
|
||||||
|
PriorityEnum prio = PriorityEnum.parseAPI(json_int(o, "priority"));
|
||||||
|
long scn_id = json_lng(o, "scn_msg_id");
|
||||||
|
|
||||||
|
okResult.invoke(title, content, prio, time, scn_id);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e("SC:expand", e.toString());
|
||||||
|
SCNApp.showToast("Communication with server failed", 4000);
|
||||||
|
} finally {
|
||||||
|
SCNApp.runOnUiThread(() -> {
|
||||||
|
if (loader != null) loader.setVisibility(View.GONE);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.e("SC:expand", e.toString());
|
||||||
|
SCNApp.showToast("Communication with server failed", 4000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean json_bool(JSONObject o, String key) throws JSONException
|
||||||
|
{
|
||||||
|
Object v = o.get(key);
|
||||||
|
if (v instanceof Integer) return ((int)v) != 0;
|
||||||
|
if (v instanceof Boolean) return ((boolean)v);
|
||||||
|
if (v instanceof String) return !Str.equals(((String)v), "0") && !Str.equals(((String)v), "false");
|
||||||
|
|
||||||
|
return o.getBoolean(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int json_int(JSONObject o, String key) throws JSONException
|
||||||
|
{
|
||||||
|
return o.getInt(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long json_lng(JSONObject o, String key) throws JSONException
|
||||||
|
{
|
||||||
|
return o.getLong(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String json_str(JSONObject o, String key) throws JSONException
|
||||||
|
{
|
||||||
|
return o.getString(key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,42 @@
|
|||||||
|
package com.blackforestbytes.simplecloudnotifier.service;
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import com.blackforestbytes.simplecloudnotifier.view.MainActivity;
|
||||||
|
|
||||||
|
public class BroadcastReceiverService extends BroadcastReceiver
|
||||||
|
{
|
||||||
|
public static final int NOTIF_SHOW_MAIN = 10021;
|
||||||
|
public static final int NOTIF_STOP_SOUND = 10022;
|
||||||
|
public static final String ID_KEY = "com.blackforestbytes.simplecloudnotifier.BroadcastID";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent)
|
||||||
|
{
|
||||||
|
if (intent == null) return;
|
||||||
|
Bundle extras = intent.getExtras();
|
||||||
|
if (extras == null) return;
|
||||||
|
int notificationId = extras.getInt(ID_KEY, 0);
|
||||||
|
|
||||||
|
if (notificationId == 0) return;
|
||||||
|
else if (notificationId == NOTIF_SHOW_MAIN) showMain(context);
|
||||||
|
else if (notificationId == NOTIF_STOP_SOUND) stopNotificationSound();
|
||||||
|
else return;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stopNotificationSound()
|
||||||
|
{
|
||||||
|
SoundService.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showMain(Context ctxt)
|
||||||
|
{
|
||||||
|
SoundService.stop();
|
||||||
|
|
||||||
|
Intent intent = new Intent(ctxt, MainActivity.class);
|
||||||
|
ctxt.startActivity(intent);
|
||||||
|
}
|
||||||
|
}
|
@@ -4,10 +4,13 @@ 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.datatypes.Tuple4;
|
||||||
|
import com.blackforestbytes.simplecloudnotifier.lib.datatypes.Tuple5;
|
||||||
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.PriorityEnum;
|
import com.blackforestbytes.simplecloudnotifier.model.PriorityEnum;
|
||||||
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
|
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
|
||||||
|
import com.blackforestbytes.simplecloudnotifier.model.ServerCommunication;
|
||||||
import com.google.firebase.messaging.FirebaseMessagingService;
|
import com.google.firebase.messaging.FirebaseMessagingService;
|
||||||
import com.google.firebase.messaging.RemoteMessage;
|
import com.google.firebase.messaging.RemoteMessage;
|
||||||
|
|
||||||
@@ -36,17 +39,16 @@ public class FBMService extends FirebaseMessagingService
|
|||||||
String title = remoteMessage.getData().get("title");
|
String title = remoteMessage.getData().get("title");
|
||||||
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"));
|
||||||
|
boolean trimmed = Boolean.parseBoolean(remoteMessage.getData().get("trimmed"));
|
||||||
|
|
||||||
CMessage msg = CMessageList.inst().add(time, title, content, prio);
|
if (trimmed)
|
||||||
|
|
||||||
|
|
||||||
if (SCNApp.isBackground())
|
|
||||||
{
|
{
|
||||||
NotificationService.inst().showBackground(msg);
|
ServerCommunication.expand(SCNSettings.inst().user_id, SCNSettings.inst().user_key, scn_id, null, (i1, i2, i3, i4, i5) -> recieveData(i4, i1, i2, i3, i5, false));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
NotificationService.inst().showForeground(msg);
|
recieveData(time, title, content, prio, scn_id, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
@@ -55,4 +57,27 @@ public class FBMService extends FirebaseMessagingService
|
|||||||
SCNApp.showToast("Recieved invalid message from server", Toast.LENGTH_LONG);
|
SCNApp.showToast("Recieved invalid message from server", Toast.LENGTH_LONG);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void recieveData(long time, String title, String content, PriorityEnum prio, long scn_id, boolean alwaysAck)
|
||||||
|
{
|
||||||
|
if (CMessageList.inst().isAck(scn_id))
|
||||||
|
{
|
||||||
|
Log.w("FB::MessageReceived", "Recieved ack-ed message: " + scn_id);
|
||||||
|
if (alwaysAck) ServerCommunication.ack(SCNSettings.inst().user_id, SCNSettings.inst().user_key, scn_id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CMessage msg = CMessageList.inst().add(scn_id, time, title, content, prio);
|
||||||
|
|
||||||
|
if (SCNApp.isBackground())
|
||||||
|
{
|
||||||
|
NotificationService.inst().showBackground(msg);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NotificationService.inst().showForeground(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerCommunication.ack(SCNSettings.inst().user_id, SCNSettings.inst().user_key, scn_id);
|
||||||
|
}
|
||||||
}
|
}
|
@@ -6,26 +6,32 @@ 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.media.AudioManager;
|
import android.media.AudioManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import android.os.VibrationEffect;
|
||||||
|
import android.os.Vibrator;
|
||||||
import android.widget.Toast;
|
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.lib.string.Str;
|
||||||
import com.blackforestbytes.simplecloudnotifier.model.CMessage;
|
import com.blackforestbytes.simplecloudnotifier.model.CMessage;
|
||||||
import com.blackforestbytes.simplecloudnotifier.model.NotificationSettings;
|
import com.blackforestbytes.simplecloudnotifier.model.NotificationSettings;
|
||||||
import com.blackforestbytes.simplecloudnotifier.model.PriorityEnum;
|
import com.blackforestbytes.simplecloudnotifier.model.PriorityEnum;
|
||||||
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 androidx.annotation.RequiresApi;
|
||||||
import androidx.core.app.NotificationCompat;
|
import androidx.core.app.NotificationCompat;
|
||||||
|
|
||||||
public class NotificationService
|
public class NotificationService
|
||||||
{
|
{
|
||||||
private final static String CHANNEL_ID_LOW = "CHAN_BFB_SCN_MESSAGES_LOW";
|
private final static String CHANNEL_P0_ID = "CHAN_BFB_SCN_MESSAGES_P0";
|
||||||
private final static String CHANNEL_ID_NORM = "CHAN_BFB_SCN_MESSAGES_NORM";
|
private final static String CHANNEL_P1_ID = "CHAN_BFB_SCN_MESSAGES_P1";
|
||||||
private final static String CHANNEL_ID_HIGH = "CHAN_BFB_SCN_MESSAGES_HIGH";
|
private final static String CHANNEL_P2_ID = "CHAN_BFB_SCN_MESSAGES_P2";
|
||||||
|
|
||||||
private final static Object _lock = new Object();
|
private final static Object _lock = new Object();
|
||||||
private static NotificationService _inst = null;
|
private static NotificationService _inst = null;
|
||||||
@@ -40,10 +46,10 @@ public class NotificationService
|
|||||||
|
|
||||||
private NotificationService()
|
private NotificationService()
|
||||||
{
|
{
|
||||||
updateChannels();
|
createChannels();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateChannels()
|
private void createChannels()
|
||||||
{
|
{
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return;
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return;
|
||||||
|
|
||||||
@@ -51,81 +57,207 @@ public class NotificationService
|
|||||||
NotificationManager notifman = ctxt.getSystemService(NotificationManager.class);
|
NotificationManager notifman = ctxt.getSystemService(NotificationManager.class);
|
||||||
if (notifman == null) return;
|
if (notifman == null) return;
|
||||||
|
|
||||||
NotificationChannel channelLow = notifman.getNotificationChannel(CHANNEL_ID_LOW);
|
{
|
||||||
if (channelLow == null) notifman.createNotificationChannel(channelLow = new NotificationChannel(CHANNEL_ID_LOW, "Push notifications (low priority)", NotificationManager.IMPORTANCE_LOW));
|
NotificationChannel channel0 = notifman.getNotificationChannel(CHANNEL_P0_ID);
|
||||||
NotificationChannel channelNorm = notifman.getNotificationChannel(CHANNEL_ID_NORM);
|
if (channel0 == null)
|
||||||
if (channelNorm == null) notifman.createNotificationChannel(channelNorm = new NotificationChannel(CHANNEL_ID_NORM, "Push notifications (normal priority)", NotificationManager.IMPORTANCE_DEFAULT));
|
{
|
||||||
NotificationChannel channelHigh = notifman.getNotificationChannel(CHANNEL_ID_HIGH);
|
channel0 = new NotificationChannel(CHANNEL_P0_ID, "Push notifications (low priority)", NotificationManager.IMPORTANCE_DEFAULT);
|
||||||
if (channelHigh == null) notifman.createNotificationChannel(channelHigh = new NotificationChannel(CHANNEL_ID_HIGH, "Push notifications (high priority)", NotificationManager.IMPORTANCE_HIGH));
|
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);
|
||||||
channelLow.setDescription("Messages from the API with priority set to low");
|
channel0.setVibrationPattern(null);
|
||||||
channelLow.enableLights(SCNSettings.inst().PriorityLow.EnableLED);
|
channel0.setLightColor(Color.BLUE);
|
||||||
channelLow.setLightColor(SCNSettings.inst().PriorityLow.LEDColor);
|
notifman.createNotificationChannel(channel0);
|
||||||
channelLow.enableVibration(SCNSettings.inst().PriorityLow.EnableVibration);
|
}
|
||||||
channelLow.setVibrationPattern(new long[]{200});
|
}
|
||||||
|
{
|
||||||
channelNorm.setDescription("Messages from the API with priority set to normal");
|
NotificationChannel channel1 = notifman.getNotificationChannel(CHANNEL_P1_ID);
|
||||||
channelNorm.enableLights(SCNSettings.inst().PriorityNorm.EnableLED);
|
if (channel1 == null)
|
||||||
channelNorm.setLightColor(SCNSettings.inst().PriorityNorm.LEDColor);
|
{
|
||||||
channelNorm.enableVibration(SCNSettings.inst().PriorityNorm.EnableVibration);
|
channel1 = new NotificationChannel(CHANNEL_P1_ID, "Push notifications (normal priority)", NotificationManager.IMPORTANCE_DEFAULT);
|
||||||
channelNorm.setVibrationPattern(new long[]{200});
|
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);
|
||||||
channelHigh.setDescription("Messages from the API with priority set to high");
|
channel1.setVibrationPattern(null);
|
||||||
channelHigh.enableLights(SCNSettings.inst().PriorityHigh.EnableLED);
|
channel1.setLightColor(Color.BLUE);
|
||||||
channelHigh.setLightColor(SCNSettings.inst().PriorityHigh.LEDColor);
|
notifman.createNotificationChannel(channel1);
|
||||||
channelHigh.enableVibration(SCNSettings.inst().PriorityHigh.EnableVibration);
|
}
|
||||||
channelHigh.setVibrationPattern(new long[]{200});
|
}
|
||||||
channelLow.setBypassDnd(true);
|
{
|
||||||
channelLow.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
|
NotificationChannel channel2 = notifman.getNotificationChannel(CHANNEL_P2_ID);
|
||||||
|
if (channel2 == null)
|
||||||
|
{
|
||||||
|
channel2 = new NotificationChannel(CHANNEL_P2_ID, "Push notifications (high priority)", NotificationManager.IMPORTANCE_DEFAULT);
|
||||||
|
channel2.setDescription("Push notifications from the server with low priority.\nGo to the in-app settings to configure ringtone, volume and vibrations");
|
||||||
|
channel2.setSound(null, null);
|
||||||
|
channel2.setVibrationPattern(null);
|
||||||
|
channel2.setLightColor(Color.BLUE);
|
||||||
|
notifman.createNotificationChannel(channel2);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void showForeground(CMessage msg)
|
public void showForeground(CMessage msg)
|
||||||
{
|
{
|
||||||
SCNApp.showToast("Message recieved: " + msg.Title, Toast.LENGTH_LONG);
|
SCNApp.showToast("Message recieved: " + msg.Title, Toast.LENGTH_LONG);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
NotificationSettings ns = SCNSettings.inst().PriorityNorm;
|
||||||
|
switch (msg.Priority)
|
||||||
|
{
|
||||||
|
case LOW: ns = SCNSettings.inst().PriorityLow; break;
|
||||||
|
case NORMAL: ns = SCNSettings.inst().PriorityNorm; break;
|
||||||
|
case HIGH: ns = SCNSettings.inst().PriorityHigh; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
SoundService.play(ns.EnableSound, ns.SoundSource, ns.ForceVolume, ns.ForceVolumeValue, false);
|
||||||
|
|
||||||
|
if (ns.EnableVibration)
|
||||||
|
{
|
||||||
|
Vibrator v = (Vibrator) SCNApp.getContext().getSystemService(Context.VIBRATOR_SERVICE);
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
v.vibrate(VibrationEffect.createOneShot(1500, VibrationEffect.DEFAULT_AMPLITUDE));
|
||||||
|
} else {
|
||||||
|
v.vibrate(1500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void showBackground(CMessage msg)
|
public void showBackground(CMessage msg)
|
||||||
{
|
{
|
||||||
Context ctxt = SCNApp.getContext();
|
Context ctxt = SCNApp.getContext();
|
||||||
|
|
||||||
String channel = CHANNEL_ID_NORM;
|
|
||||||
NotificationSettings ns = SCNSettings.inst().PriorityNorm;
|
NotificationSettings ns = SCNSettings.inst().PriorityNorm;
|
||||||
switch (msg.Priority)
|
switch (msg.Priority)
|
||||||
{
|
{
|
||||||
case LOW: ns = SCNSettings.inst().PriorityLow; channel = CHANNEL_ID_LOW; break;
|
case LOW: ns = SCNSettings.inst().PriorityLow; break;
|
||||||
case NORMAL: ns = SCNSettings.inst().PriorityNorm; channel = CHANNEL_ID_NORM; break;
|
case NORMAL: ns = SCNSettings.inst().PriorityNorm; break;
|
||||||
case HIGH: ns = SCNSettings.inst().PriorityHigh; channel = CHANNEL_ID_HIGH; break;
|
case HIGH: ns = SCNSettings.inst().PriorityHigh; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(ctxt, channel);
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
|
||||||
mBuilder.setSmallIcon(R.drawable.ic_bfb);
|
{
|
||||||
|
// old
|
||||||
|
showBackground_old(msg, ctxt, ns, msg.Priority);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// new
|
||||||
|
showBackground_new(msg, ctxt, ns, msg.Priority);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getChannel(PriorityEnum p)
|
||||||
|
{
|
||||||
|
switch (p)
|
||||||
|
{
|
||||||
|
case LOW: return CHANNEL_P0_ID;
|
||||||
|
case NORMAL: return CHANNEL_P1_ID;
|
||||||
|
case HIGH: return CHANNEL_P2_ID;
|
||||||
|
|
||||||
|
default: return CHANNEL_P0_ID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showBackground_old(CMessage msg, Context ctxt, NotificationSettings ns, PriorityEnum prio)
|
||||||
|
{
|
||||||
|
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(ctxt, getChannel(prio));
|
||||||
|
mBuilder.setSmallIcon(R.drawable.ic_notification_white);
|
||||||
|
mBuilder.setLargeIcon(BitmapFactory.decodeResource(ctxt.getResources(), R.mipmap.ic_notification_full));
|
||||||
mBuilder.setContentTitle(msg.Title);
|
mBuilder.setContentTitle(msg.Title);
|
||||||
mBuilder.setContentText(msg.Content);
|
mBuilder.setContentText(msg.Content);
|
||||||
mBuilder.setShowWhen(true);
|
mBuilder.setShowWhen(true);
|
||||||
mBuilder.setWhen(msg.Timestamp);
|
mBuilder.setWhen(msg.Timestamp * 1000);
|
||||||
mBuilder.setAutoCancel(true);
|
mBuilder.setAutoCancel(true);
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
|
mBuilder.setCategory(Notification.CATEGORY_MESSAGE);
|
||||||
{
|
mBuilder.setGroup("com.blackforestbytes.simplecloudnotifier.notifications.group."+prio.toString());
|
||||||
if (msg.Priority == PriorityEnum.LOW) mBuilder.setPriority(NotificationCompat.PRIORITY_LOW);
|
|
||||||
if (msg.Priority == PriorityEnum.NORMAL) mBuilder.setPriority(NotificationCompat.PRIORITY_DEFAULT);
|
|
||||||
if (msg.Priority == PriorityEnum.HIGH) mBuilder.setPriority(NotificationCompat.PRIORITY_HIGH);
|
|
||||||
if (ns.EnableVibration) mBuilder.setVibrate(new long[]{200});
|
|
||||||
if (ns.EnableLED) mBuilder.setLights(ns.LEDColor, 500, 500);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ns.EnableSound && !ns.SoundSource.isEmpty())
|
if (msg.Priority == PriorityEnum.LOW) mBuilder.setPriority(NotificationCompat.PRIORITY_LOW);
|
||||||
{
|
if (msg.Priority == PriorityEnum.NORMAL) mBuilder.setPriority(NotificationCompat.PRIORITY_DEFAULT);
|
||||||
mBuilder.setSound(Uri.parse(ns.SoundSource), AudioManager.STREAM_ALARM);
|
if (msg.Priority == PriorityEnum.HIGH) mBuilder.setPriority(NotificationCompat.PRIORITY_HIGH);
|
||||||
}
|
if (ns.EnableVibration) mBuilder.setVibrate(new long[]{500});
|
||||||
|
if (ns.EnableLED) mBuilder.setLights(ns.LEDColor, 500, 500);
|
||||||
|
|
||||||
|
if (ns.EnableSound && !ns.SoundSource.isEmpty() && !ns.RepeatSound) mBuilder.setSound(Uri.parse(ns.SoundSource), AudioManager.STREAM_NOTIFICATION);
|
||||||
|
|
||||||
Intent intent = new Intent(ctxt, MainActivity.class);
|
Intent intent = new Intent(ctxt, MainActivity.class);
|
||||||
PendingIntent pi = PendingIntent.getActivity(ctxt, 0, intent, 0);
|
PendingIntent pi = PendingIntent.getActivity(ctxt, 0, intent, 0);
|
||||||
mBuilder.setContentIntent(pi);
|
mBuilder.setContentIntent(pi);
|
||||||
NotificationManager mNotificationManager = (NotificationManager) ctxt.getSystemService(Context.NOTIFICATION_SERVICE);
|
NotificationManager mNotificationManager = (NotificationManager) ctxt.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||||
|
|
||||||
Notification n = mBuilder.build();
|
if (ns.EnableSound && !ns.SoundSource.isEmpty() && ns.RepeatSound)
|
||||||
if (ns.EnableSound && !ns.SoundSource.isEmpty() && ns.RepeatSound) n.flags |= Notification.FLAG_INSISTENT;
|
{
|
||||||
|
Intent intnt_stop = new Intent(SCNApp.getContext(), BroadcastReceiverService.class);
|
||||||
|
intnt_stop.putExtra(BroadcastReceiverService.ID_KEY, BroadcastReceiverService.NOTIF_STOP_SOUND);
|
||||||
|
PendingIntent pi_stop = PendingIntent.getBroadcast(SCNApp.getContext().getApplicationContext(), BroadcastReceiverService.NOTIF_STOP_SOUND, intnt_stop, 0);
|
||||||
|
mBuilder.addAction(new NotificationCompat.Action(-1, "Stop", pi_stop));
|
||||||
|
mBuilder.setDeleteIntent(pi_stop);
|
||||||
|
|
||||||
if (mNotificationManager != null) mNotificationManager.notify(0, n);
|
SoundService.play(ns.EnableSound, ns.SoundSource, ns.ForceVolume, ns.ForceVolumeValue, ns.RepeatSound);
|
||||||
|
}
|
||||||
|
|
||||||
|
Notification n = mBuilder.build();
|
||||||
|
|
||||||
|
if (mNotificationManager != null) mNotificationManager.notify((int)msg.SCN_ID, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||||
|
private void showBackground_new(CMessage msg, Context ctxt, NotificationSettings ns, PriorityEnum prio)
|
||||||
|
{
|
||||||
|
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(ctxt, getChannel(prio));
|
||||||
|
mBuilder.setSmallIcon(R.drawable.ic_notification_white);
|
||||||
|
mBuilder.setLargeIcon(BitmapFactory.decodeResource(ctxt.getResources(), R.mipmap.ic_notification_full));
|
||||||
|
mBuilder.setContentTitle(msg.Title);
|
||||||
|
mBuilder.setContentText(msg.Content);
|
||||||
|
mBuilder.setShowWhen(true);
|
||||||
|
mBuilder.setWhen(msg.Timestamp * 1000);
|
||||||
|
mBuilder.setAutoCancel(true);
|
||||||
|
mBuilder.setCategory(Notification.CATEGORY_MESSAGE);
|
||||||
|
mBuilder.setGroup("com.blackforestbytes.simplecloudnotifier.notifications.group."+prio.toString());
|
||||||
|
|
||||||
|
if (ns.EnableLED) mBuilder.setLights(ns.LEDColor, 500, 500);
|
||||||
|
|
||||||
|
if (msg.Priority == PriorityEnum.LOW) mBuilder.setPriority(NotificationCompat.PRIORITY_LOW);
|
||||||
|
if (msg.Priority == PriorityEnum.NORMAL) mBuilder.setPriority(NotificationCompat.PRIORITY_DEFAULT);
|
||||||
|
if (msg.Priority == PriorityEnum.HIGH) mBuilder.setPriority(NotificationCompat.PRIORITY_HIGH);
|
||||||
|
|
||||||
|
Intent intnt_click = new Intent(SCNApp.getContext(), BroadcastReceiverService.class);
|
||||||
|
intnt_click.putExtra(BroadcastReceiverService.ID_KEY, BroadcastReceiverService.NOTIF_SHOW_MAIN);
|
||||||
|
PendingIntent pi = PendingIntent.getBroadcast(ctxt, 0, intnt_click, 0);
|
||||||
|
mBuilder.setContentIntent(pi);
|
||||||
|
NotificationManager mNotificationManager = (NotificationManager) ctxt.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||||
|
if (mNotificationManager == null) return;
|
||||||
|
|
||||||
|
if (ns.EnableSound && !Str.isNullOrWhitespace(ns.SoundSource))
|
||||||
|
{
|
||||||
|
if (ns.RepeatSound)
|
||||||
|
{
|
||||||
|
Intent intnt_stop = new Intent(SCNApp.getContext(), BroadcastReceiverService.class);
|
||||||
|
intnt_stop.putExtra(BroadcastReceiverService.ID_KEY, BroadcastReceiverService.NOTIF_STOP_SOUND);
|
||||||
|
PendingIntent pi_stop = PendingIntent.getBroadcast(ctxt, BroadcastReceiverService.NOTIF_STOP_SOUND, intnt_stop, 0);
|
||||||
|
mBuilder.addAction(new NotificationCompat.Action(-1, "Stop", pi_stop));
|
||||||
|
mBuilder.setDeleteIntent(pi_stop);
|
||||||
|
}
|
||||||
|
|
||||||
|
SoundService.play(ns.EnableSound, ns.SoundSource, ns.ForceVolume, ns.ForceVolumeValue, ns.RepeatSound);
|
||||||
|
}
|
||||||
|
|
||||||
|
Notification n = mBuilder.build();
|
||||||
|
n.flags |= Notification.FLAG_AUTO_CANCEL;
|
||||||
|
|
||||||
|
mNotificationManager.notify((int)msg.SCN_ID, n);
|
||||||
|
|
||||||
|
if (ns.EnableVibration)
|
||||||
|
{
|
||||||
|
Vibrator v = (Vibrator) SCNApp.getContext().getSystemService(Context.VIBRATOR_SERVICE);
|
||||||
|
v.vibrate(VibrationEffect.createOneShot(1500, VibrationEffect.DEFAULT_AMPLITUDE));
|
||||||
|
}
|
||||||
|
|
||||||
|
//if (ns.EnableLED) { } // no LED in Android-O -- configure via Channel
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,56 @@
|
|||||||
|
package com.blackforestbytes.simplecloudnotifier.service;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.media.AudioAttributes;
|
||||||
|
import android.media.AudioManager;
|
||||||
|
import android.media.MediaPlayer;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.blackforestbytes.simplecloudnotifier.SCNApp;
|
||||||
|
import com.blackforestbytes.simplecloudnotifier.lib.string.Str;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class SoundService
|
||||||
|
{
|
||||||
|
private static MediaPlayer mpLast = null;
|
||||||
|
|
||||||
|
public static void play(boolean enableSound, String soundSource, boolean forceVolume, int forceVolumeValue, boolean loop)
|
||||||
|
{
|
||||||
|
if (!enableSound) return;
|
||||||
|
if (Str.isNullOrWhitespace(soundSource)) return;
|
||||||
|
|
||||||
|
stop();
|
||||||
|
|
||||||
|
if (forceVolume)
|
||||||
|
{
|
||||||
|
AudioManager aman = (AudioManager) SCNApp.getContext().getSystemService(Context.AUDIO_SERVICE);
|
||||||
|
int maxVolume = aman.getStreamMaxVolume(AudioManager.STREAM_NOTIFICATION);
|
||||||
|
aman.setStreamVolume(AudioManager.STREAM_NOTIFICATION, (int)(maxVolume * (forceVolumeValue / 100.0)), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
MediaPlayer player = new MediaPlayer();
|
||||||
|
player.setAudioAttributes(new AudioAttributes.Builder().setLegacyStreamType(AudioManager.STREAM_NOTIFICATION).build());
|
||||||
|
player.setAudioStreamType(AudioManager.STREAM_NOTIFICATION);
|
||||||
|
player.setDataSource(SCNApp.getContext(), Uri.parse(soundSource));
|
||||||
|
player.setLooping(loop);
|
||||||
|
player.setOnCompletionListener( mp -> { mp.stop(); mp.release(); });
|
||||||
|
player.setOnSeekCompleteListener(mp -> { mp.stop(); mp.release(); });
|
||||||
|
player.prepare();
|
||||||
|
player.start();
|
||||||
|
mpLast = player;
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
Log.e("Sound::play", e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void stop()
|
||||||
|
{
|
||||||
|
if (mpLast != null && mpLast.isPlaying()) { mpLast.stop(); mpLast.release(); mpLast = null; }
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,89 @@
|
|||||||
|
package com.blackforestbytes.simplecloudnotifier.util;
|
||||||
|
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
|
||||||
|
import com.blackforestbytes.simplecloudnotifier.view.MessageAdapter;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
public class MessageAdapterTouchHelper extends ItemTouchHelper.SimpleCallback
|
||||||
|
{
|
||||||
|
private MessageAdapterTouchHelperListener listener;
|
||||||
|
|
||||||
|
private int dir = 0;
|
||||||
|
|
||||||
|
public MessageAdapterTouchHelper(int dragDirs, int swipeDirs, MessageAdapterTouchHelperListener listener)
|
||||||
|
{
|
||||||
|
super(dragDirs, swipeDirs);
|
||||||
|
this.dir = swipeDirs;
|
||||||
|
this.listener = listener;
|
||||||
|
updateEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateEnabled()
|
||||||
|
{
|
||||||
|
int sdir = SCNSettings.inst().EnableDeleteSwipe ? ItemTouchHelper.LEFT : 0;
|
||||||
|
if (dir == sdir) return;
|
||||||
|
setDefaultSwipeDirs(dir = sdir);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState)
|
||||||
|
{
|
||||||
|
if (viewHolder != null)
|
||||||
|
{
|
||||||
|
final View foregroundView = ((MessageAdapter.MessagePresenter) viewHolder).viewForeground;
|
||||||
|
|
||||||
|
getDefaultUIUtil().onSelected(foregroundView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChildDrawOver(@NonNull Canvas c, @NonNull RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive)
|
||||||
|
{
|
||||||
|
final View foregroundView = ((MessageAdapter.MessagePresenter) viewHolder).viewForeground;
|
||||||
|
getDefaultUIUtil().onDrawOver(c, recyclerView, foregroundView, dX, dY, actionState, isCurrentlyActive);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder)
|
||||||
|
{
|
||||||
|
final View foregroundView = ((MessageAdapter.MessagePresenter) viewHolder).viewForeground;
|
||||||
|
getDefaultUIUtil().clearView(foregroundView);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive)
|
||||||
|
{
|
||||||
|
final View foregroundView = ((MessageAdapter.MessagePresenter) viewHolder).viewForeground;
|
||||||
|
|
||||||
|
getDefaultUIUtil().onDraw(c, recyclerView, foregroundView, dX, dY, actionState, isCurrentlyActive);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction)
|
||||||
|
{
|
||||||
|
listener.onSwiped(viewHolder, direction, viewHolder.getAdapterPosition());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int convertToAbsoluteDirection(int flags, int layoutDirection)
|
||||||
|
{
|
||||||
|
return super.convertToAbsoluteDirection(flags, layoutDirection);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface MessageAdapterTouchHelperListener
|
||||||
|
{
|
||||||
|
void onSwiped(RecyclerView.ViewHolder viewHolder, int direction, int position);
|
||||||
|
}
|
||||||
|
}
|
@@ -1,15 +1,19 @@
|
|||||||
package com.blackforestbytes.simplecloudnotifier.view;
|
package com.blackforestbytes.simplecloudnotifier.view;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
|
import android.app.Activity;
|
||||||
import android.content.ClipData;
|
import android.content.ClipData;
|
||||||
import android.content.ClipboardManager;
|
import android.content.ClipboardManager;
|
||||||
|
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;
|
||||||
@@ -21,6 +25,7 @@ import net.glxn.qrgen.android.QRCode;
|
|||||||
import net.glxn.qrgen.core.image.ImageType;
|
import net.glxn.qrgen.core.image.ImageType;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
import static android.content.Context.CLIPBOARD_SERVICE;
|
import static android.content.Context.CLIPBOARD_SERVICE;
|
||||||
@@ -55,20 +60,52 @@ public class AccountFragment extends Fragment
|
|||||||
|
|
||||||
v.findViewById(R.id.btnAccountReset).setOnClickListener(cv ->
|
v.findViewById(R.id.btnAccountReset).setOnClickListener(cv ->
|
||||||
{
|
{
|
||||||
View lpnl = v.findViewById(R.id.loadingPanel);
|
Activity a = getActivity();
|
||||||
lpnl.setVisibility(View.VISIBLE);
|
if (a == null) return;
|
||||||
SCNSettings.inst().reset(lpnl);
|
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(a);
|
||||||
|
|
||||||
|
builder.setTitle("Confirm");
|
||||||
|
builder.setMessage("Reset account key?");
|
||||||
|
|
||||||
|
builder.setPositiveButton("YES", (dialog, which) -> {
|
||||||
|
View lpnl = v.findViewById(R.id.loadingPanel);
|
||||||
|
lpnl.setVisibility(View.VISIBLE);
|
||||||
|
SCNSettings.inst().reset(lpnl);
|
||||||
|
dialog.dismiss();
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.setNegativeButton("NO", (dialog, which) -> dialog.dismiss());
|
||||||
|
|
||||||
|
AlertDialog alert = builder.create();
|
||||||
|
alert.show();
|
||||||
});
|
});
|
||||||
|
|
||||||
v.findViewById(R.id.btnClearLocalStorage).setOnClickListener(cv ->
|
v.findViewById(R.id.btnClearLocalStorage).setOnClickListener(cv ->
|
||||||
{
|
{
|
||||||
CMessageList.inst().clear();
|
Activity a = getActivity();
|
||||||
SCNApp.showToast("Notifications cleared", 1000);
|
if (a == null) return;
|
||||||
|
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(a);
|
||||||
|
|
||||||
|
builder.setTitle("Confirm");
|
||||||
|
builder.setMessage("Clear local messages?");
|
||||||
|
|
||||||
|
builder.setPositiveButton("YES", (dialog, which) -> {
|
||||||
|
CMessageList.inst().clear();
|
||||||
|
SCNApp.showToast("Messages cleared", 1000);
|
||||||
|
dialog.dismiss();
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.setNegativeButton("NO", (dialog, which) -> dialog.dismiss());
|
||||||
|
|
||||||
|
AlertDialog alert = builder.create();
|
||||||
|
alert.show();
|
||||||
});
|
});
|
||||||
|
|
||||||
v.findViewById(R.id.btnQR).setOnClickListener(cv ->
|
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);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -91,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();
|
||||||
|
|
||||||
@@ -103,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
|
||||||
{
|
{
|
||||||
@@ -111,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,6 +1,7 @@
|
|||||||
package com.blackforestbytes.simplecloudnotifier.view;
|
package com.blackforestbytes.simplecloudnotifier.view;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.widget.RelativeLayout;
|
||||||
|
|
||||||
import com.blackforestbytes.simplecloudnotifier.R;
|
import com.blackforestbytes.simplecloudnotifier.R;
|
||||||
import com.blackforestbytes.simplecloudnotifier.SCNApp;
|
import com.blackforestbytes.simplecloudnotifier.SCNApp;
|
||||||
@@ -18,6 +19,7 @@ import androidx.viewpager.widget.ViewPager;
|
|||||||
public class MainActivity extends AppCompatActivity
|
public class MainActivity extends AppCompatActivity
|
||||||
{
|
{
|
||||||
public TabAdapter adpTabs;
|
public TabAdapter adpTabs;
|
||||||
|
public RelativeLayout layoutRoot;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState)
|
protected void onCreate(Bundle savedInstanceState)
|
||||||
@@ -28,6 +30,8 @@ public class MainActivity extends AppCompatActivity
|
|||||||
NotificationService.inst();
|
NotificationService.inst();
|
||||||
CMessageList.inst();
|
CMessageList.inst();
|
||||||
|
|
||||||
|
layoutRoot = findViewById(R.id.layoutRoot);
|
||||||
|
|
||||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||||
setSupportActionBar(toolbar);
|
setSupportActionBar(toolbar);
|
||||||
|
|
||||||
@@ -39,6 +43,22 @@ public class MainActivity extends AppCompatActivity
|
|||||||
tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
|
tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
|
||||||
tabLayout.setupWithViewPager(viewPager);
|
tabLayout.setupWithViewPager(viewPager);
|
||||||
|
|
||||||
|
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { /* */ }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPageSelected(int position)
|
||||||
|
{
|
||||||
|
if (position != 2) adpTabs.tab3.onViewpagerHide();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPageScrollStateChanged(int state) {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
SCNApp.register(this);
|
SCNApp.register(this);
|
||||||
IABService.startup(this);
|
IABService.startup(this);
|
||||||
SCNSettings.inst().work(this);
|
SCNSettings.inst().work(this);
|
||||||
|
@@ -1,25 +1,40 @@
|
|||||||
package com.blackforestbytes.simplecloudnotifier.view;
|
package com.blackforestbytes.simplecloudnotifier.view;
|
||||||
|
|
||||||
|
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;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
|
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.model.CMessage;
|
import com.blackforestbytes.simplecloudnotifier.model.CMessage;
|
||||||
import com.blackforestbytes.simplecloudnotifier.model.CMessageList;
|
import com.blackforestbytes.simplecloudnotifier.model.CMessageList;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.WeakHashMap;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
public class MessageAdapter extends RecyclerView.Adapter
|
public class MessageAdapter extends RecyclerView.Adapter
|
||||||
{
|
{
|
||||||
private final View vNoElements;
|
private final View vNoElements;
|
||||||
|
private final LinearLayoutManager manLayout;
|
||||||
|
private final RecyclerView viewRecycler;
|
||||||
|
|
||||||
public MessageAdapter(View noElementsView)
|
private WeakHashMap<MessagePresenter, Boolean> viewHolders = new WeakHashMap<>();
|
||||||
|
|
||||||
|
public MessageAdapter(View noElementsView, LinearLayoutManager layout, RecyclerView recycler)
|
||||||
{
|
{
|
||||||
vNoElements = noElementsView;
|
vNoElements = noElementsView;
|
||||||
|
manLayout = layout;
|
||||||
|
viewRecycler = recycler;
|
||||||
CMessageList.inst().register(this);
|
CMessageList.inst().register(this);
|
||||||
|
|
||||||
vNoElements.setVisibility(getItemCount()>0 ? View.GONE : View.VISIBLE);
|
vNoElements.setVisibility(getItemCount()>0 ? View.GONE : View.VISIBLE);
|
||||||
@@ -36,9 +51,17 @@ public class MessageAdapter extends RecyclerView.Adapter
|
|||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position)
|
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position)
|
||||||
{
|
{
|
||||||
CMessage msg = CMessageList.inst().tryGet(position);
|
CMessage msg = CMessageList.inst().tryGetFromBack(position);
|
||||||
MessagePresenter view = (MessagePresenter) holder;
|
MessagePresenter view = (MessagePresenter) holder;
|
||||||
view.setMessage(msg);
|
view.setMessage(msg);
|
||||||
|
|
||||||
|
viewHolders.put(view, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewRecycled(@NonNull RecyclerView.ViewHolder holder)
|
||||||
|
{
|
||||||
|
if (holder instanceof MessagePresenter) viewHolders.remove(holder);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -59,23 +82,52 @@ public class MessageAdapter extends RecyclerView.Adapter
|
|||||||
vNoElements.setVisibility(getItemCount()>0 ? View.GONE : View.VISIBLE);
|
vNoElements.setVisibility(getItemCount()>0 ? View.GONE : View.VISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class MessagePresenter extends RecyclerView.ViewHolder implements View.OnClickListener
|
public void scrollToTop()
|
||||||
|
{
|
||||||
|
manLayout.smoothScrollToPosition(viewRecycler, null, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CMessage removeItem(int position)
|
||||||
|
{
|
||||||
|
CMessage i = CMessageList.inst().removeFromBack(position);
|
||||||
|
notifyDataSetChanged();
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void restoreItem(CMessage item, int position)
|
||||||
|
{
|
||||||
|
CMessageList.inst().insert(position, item);
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MessagePresenter extends RecyclerView.ViewHolder implements View.OnClickListener
|
||||||
{
|
{
|
||||||
private TextView tvTimestamp;
|
private TextView tvTimestamp;
|
||||||
private TextView tvTitle;
|
private TextView tvTitle;
|
||||||
private TextView tvMessage;
|
private TextView tvMessage;
|
||||||
private ImageView ivPriority;
|
private ImageView ivPriority;
|
||||||
|
|
||||||
|
public RelativeLayout viewForeground;
|
||||||
|
public RelativeLayout viewBackground;
|
||||||
|
|
||||||
private CMessage data;
|
private CMessage data;
|
||||||
|
|
||||||
MessagePresenter(View itemView)
|
MessagePresenter(View itemView)
|
||||||
{
|
{
|
||||||
super(itemView);
|
super(itemView);
|
||||||
tvTimestamp = itemView.findViewById(R.id.tvTimestamp);
|
tvTimestamp = itemView.findViewById(R.id.tvTimestamp);
|
||||||
tvTitle = itemView.findViewById(R.id.tvTitle);
|
tvTitle = itemView.findViewById(R.id.tvTitle);
|
||||||
tvMessage = itemView.findViewById(R.id.tvMessage);
|
tvMessage = itemView.findViewById(R.id.tvMessage);
|
||||||
ivPriority = itemView.findViewById(R.id.ivPriority);
|
ivPriority = itemView.findViewById(R.id.ivPriority);
|
||||||
|
viewForeground = itemView.findViewById(R.id.layoutFront);
|
||||||
|
viewBackground = itemView.findViewById(R.id.layoutBack);
|
||||||
|
|
||||||
itemView.setOnClickListener(this);
|
itemView.setOnClickListener(this);
|
||||||
|
tvTimestamp.setOnClickListener(this);
|
||||||
|
tvTitle.setOnClickListener(this);
|
||||||
|
tvMessage.setOnClickListener(this);
|
||||||
|
ivPriority.setOnClickListener(this);
|
||||||
|
viewForeground.setOnClickListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setMessage(CMessage msg)
|
void setMessage(CMessage msg)
|
||||||
@@ -89,13 +141,16 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +160,16 @@ public class MessageAdapter extends RecyclerView.Adapter
|
|||||||
@Override
|
@Override
|
||||||
public void onClick(View v)
|
public void onClick(View v)
|
||||||
{
|
{
|
||||||
//SCNApp.showToast(data.Title, Toast.LENGTH_LONG);
|
for (MessagePresenter holder : MessageAdapter.this.viewHolders.keySet())
|
||||||
|
{
|
||||||
|
if (holder == null) continue;
|
||||||
|
if (holder == this) continue;
|
||||||
|
if (holder.tvMessage == null) continue;
|
||||||
|
if (holder.tvMessage.getMaxLines() == 6) continue;
|
||||||
|
holder.tvMessage.setMaxLines(6);
|
||||||
|
}
|
||||||
|
|
||||||
|
tvMessage.setMaxLines(9999);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,24 +1,33 @@
|
|||||||
package com.blackforestbytes.simplecloudnotifier.view;
|
package com.blackforestbytes.simplecloudnotifier.view;
|
||||||
|
|
||||||
|
import android.graphics.Color;
|
||||||
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 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.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.google.android.gms.ads.doubleclick.PublisherAdRequest;
|
import com.google.android.gms.ads.doubleclick.PublisherAdRequest;
|
||||||
import com.google.android.gms.ads.doubleclick.PublisherAdView;
|
import com.google.android.gms.ads.doubleclick.PublisherAdView;
|
||||||
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
public class NotificationsFragment extends Fragment
|
public class NotificationsFragment extends Fragment implements MessageAdapterTouchHelper.MessageAdapterTouchHelperListener
|
||||||
{
|
{
|
||||||
private PublisherAdView adView;
|
private PublisherAdView adView;
|
||||||
|
private MessageAdapter adpMessages;
|
||||||
|
|
||||||
|
public MessageAdapterTouchHelper touchHelper;
|
||||||
|
|
||||||
public NotificationsFragment()
|
public NotificationsFragment()
|
||||||
{
|
{
|
||||||
@@ -31,8 +40,12 @@ public class NotificationsFragment extends Fragment
|
|||||||
View v = inflater.inflate(R.layout.fragment_notifications, container, false);
|
View v = inflater.inflate(R.layout.fragment_notifications, container, false);
|
||||||
|
|
||||||
RecyclerView rvMessages = v.findViewById(R.id.rvMessages);
|
RecyclerView rvMessages = v.findViewById(R.id.rvMessages);
|
||||||
rvMessages.setLayoutManager(new LinearLayoutManager(this.getContext(), RecyclerView.VERTICAL, true));
|
LinearLayoutManager lman = new LinearLayoutManager(this.getContext(), RecyclerView.VERTICAL, false);
|
||||||
rvMessages.setAdapter(new MessageAdapter(v.findViewById(R.id.tvNoElements)));
|
rvMessages.setLayoutManager(lman);
|
||||||
|
rvMessages.setAdapter(adpMessages = new MessageAdapter(v.findViewById(R.id.tvNoElements), lman, rvMessages));
|
||||||
|
|
||||||
|
ItemTouchHelper.SimpleCallback itemTouchHelperCallback = touchHelper = new MessageAdapterTouchHelper(0, ItemTouchHelper.LEFT, this);
|
||||||
|
new ItemTouchHelper(itemTouchHelperCallback).attachToRecyclerView(rvMessages);
|
||||||
|
|
||||||
adView = v.findViewById(R.id.adBanner);
|
adView = v.findViewById(R.id.adBanner);
|
||||||
PublisherAdRequest adRequest = new PublisherAdRequest.Builder().build();
|
PublisherAdRequest adRequest = new PublisherAdRequest.Builder().build();
|
||||||
@@ -45,6 +58,23 @@ public class NotificationsFragment extends Fragment
|
|||||||
|
|
||||||
public void updateProState()
|
public void updateProState()
|
||||||
{
|
{
|
||||||
adView.setVisibility(IABService.inst().getPurchaseCached(IABService.IAB_PRO_MODE) != null ? View.GONE : View.VISIBLE);
|
if (adView != null) adView.setVisibility(IABService.inst().getPurchaseCached(IABService.IAB_PRO_MODE) != null ? View.GONE : View.VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction, int position)
|
||||||
|
{
|
||||||
|
if (viewHolder instanceof MessageAdapter.MessagePresenter)
|
||||||
|
{
|
||||||
|
final int deletedIndex = viewHolder.getAdapterPosition();
|
||||||
|
|
||||||
|
final CMessage deletedItem = adpMessages.removeItem(viewHolder.getAdapterPosition());
|
||||||
|
String name = deletedItem.Title;
|
||||||
|
|
||||||
|
Snackbar snackbar = Snackbar.make(SCNApp.getMainActivity().layoutRoot, name + " removed", Snackbar.LENGTH_LONG);
|
||||||
|
snackbar.setAction("UNDO", view -> adpMessages.restoreItem(deletedItem, deletedIndex));
|
||||||
|
snackbar.setActionTextColor(Color.YELLOW);
|
||||||
|
snackbar.show();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,9 +1,16 @@
|
|||||||
package com.blackforestbytes.simplecloudnotifier.view;
|
package com.blackforestbytes.simplecloudnotifier.view;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.media.AudioAttributes;
|
||||||
import android.media.AudioManager;
|
import android.media.AudioManager;
|
||||||
|
import android.media.MediaPlayer;
|
||||||
|
import android.media.Ringtone;
|
||||||
|
import android.media.RingtoneManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@@ -11,18 +18,25 @@ import android.widget.AdapterView;
|
|||||||
import android.widget.ArrayAdapter;
|
import android.widget.ArrayAdapter;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
|
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.R;
|
import com.blackforestbytes.simplecloudnotifier.R;
|
||||||
|
import com.blackforestbytes.simplecloudnotifier.SCNApp;
|
||||||
|
import com.blackforestbytes.simplecloudnotifier.lib.android.ThreadUtils;
|
||||||
|
import com.blackforestbytes.simplecloudnotifier.lib.lambda.FI;
|
||||||
|
import com.blackforestbytes.simplecloudnotifier.lib.string.Str;
|
||||||
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
|
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
|
||||||
import com.blackforestbytes.simplecloudnotifier.service.IABService;
|
import com.blackforestbytes.simplecloudnotifier.service.IABService;
|
||||||
import com.blackforestbytes.simplecloudnotifier.service.NotificationService;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import top.defaults.colorpicker.ColorPickerPopup;
|
import top.defaults.colorpicker.ColorPickerPopup;
|
||||||
@@ -36,6 +50,7 @@ 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 Switch prefMsgLowEnableSound;
|
private Switch prefMsgLowEnableSound;
|
||||||
private TextView prefMsgLowRingtone_value;
|
private TextView prefMsgLowRingtone_value;
|
||||||
@@ -45,6 +60,9 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
|
|||||||
private View prefMsgLowLedColor_container;
|
private View prefMsgLowLedColor_container;
|
||||||
private ImageView prefMsgLowLedColor_value;
|
private ImageView prefMsgLowLedColor_value;
|
||||||
private Switch prefMsgLowEnableVibrations;
|
private Switch prefMsgLowEnableVibrations;
|
||||||
|
private Switch prefMsgLowForceVolume;
|
||||||
|
private SeekBar prefMsgLowVolume;
|
||||||
|
private ImageView prefMsgLowVolumeTest;
|
||||||
|
|
||||||
private Switch prefMsgNormEnableSound;
|
private Switch prefMsgNormEnableSound;
|
||||||
private TextView prefMsgNormRingtone_value;
|
private TextView prefMsgNormRingtone_value;
|
||||||
@@ -54,6 +72,9 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
|
|||||||
private View prefMsgNormLedColor_container;
|
private View prefMsgNormLedColor_container;
|
||||||
private ImageView prefMsgNormLedColor_value;
|
private ImageView prefMsgNormLedColor_value;
|
||||||
private Switch prefMsgNormEnableVibrations;
|
private Switch prefMsgNormEnableVibrations;
|
||||||
|
private Switch prefMsgNormForceVolume;
|
||||||
|
private SeekBar prefMsgNormVolume;
|
||||||
|
private ImageView prefMsgNormVolumeTest;
|
||||||
|
|
||||||
private Switch prefMsgHighEnableSound;
|
private Switch prefMsgHighEnableSound;
|
||||||
private TextView prefMsgHighRingtone_value;
|
private TextView prefMsgHighRingtone_value;
|
||||||
@@ -63,9 +84,14 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
|
|||||||
private View prefMsgHighLedColor_container;
|
private View prefMsgHighLedColor_container;
|
||||||
private ImageView prefMsgHighLedColor_value;
|
private ImageView prefMsgHighLedColor_value;
|
||||||
private Switch prefMsgHighEnableVibrations;
|
private Switch prefMsgHighEnableVibrations;
|
||||||
|
private Switch prefMsgHighForceVolume;
|
||||||
|
private SeekBar prefMsgHighVolume;
|
||||||
|
private ImageView prefMsgHighVolumeTest;
|
||||||
|
|
||||||
private int musicPickerSwitch = -1;
|
private int musicPickerSwitch = -1;
|
||||||
|
|
||||||
|
private MediaPlayer[] mPlayers = new MediaPlayer[3];
|
||||||
|
|
||||||
public SettingsFragment()
|
public SettingsFragment()
|
||||||
{
|
{
|
||||||
// Required empty public constructor
|
// Required empty public constructor
|
||||||
@@ -90,6 +116,7 @@ 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);
|
||||||
|
|
||||||
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);
|
||||||
@@ -99,6 +126,9 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
|
|||||||
prefMsgLowLedColor_value = v.findViewById(R.id.prefMsgLowLedColor_value);
|
prefMsgLowLedColor_value = v.findViewById(R.id.prefMsgLowLedColor_value);
|
||||||
prefMsgLowLedColor_container = v.findViewById(R.id.prefMsgLowLedColor_container);
|
prefMsgLowLedColor_container = v.findViewById(R.id.prefMsgLowLedColor_container);
|
||||||
prefMsgLowEnableVibrations = v.findViewById(R.id.prefMsgLowEnableVibrations);
|
prefMsgLowEnableVibrations = v.findViewById(R.id.prefMsgLowEnableVibrations);
|
||||||
|
prefMsgLowForceVolume = v.findViewById(R.id.prefMsgLowForceVolume);
|
||||||
|
prefMsgLowVolume = v.findViewById(R.id.prefMsgLowVolume);
|
||||||
|
prefMsgLowVolumeTest = v.findViewById(R.id.btnLowVolumeTest);
|
||||||
|
|
||||||
prefMsgNormEnableSound = v.findViewById(R.id.prefMsgNormEnableSound);
|
prefMsgNormEnableSound = v.findViewById(R.id.prefMsgNormEnableSound);
|
||||||
prefMsgNormRingtone_value = v.findViewById(R.id.prefMsgNormRingtone_value);
|
prefMsgNormRingtone_value = v.findViewById(R.id.prefMsgNormRingtone_value);
|
||||||
@@ -108,6 +138,9 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
|
|||||||
prefMsgNormLedColor_value = v.findViewById(R.id.prefMsgNormLedColor_value);
|
prefMsgNormLedColor_value = v.findViewById(R.id.prefMsgNormLedColor_value);
|
||||||
prefMsgNormLedColor_container = v.findViewById(R.id.prefMsgNormLedColor_container);
|
prefMsgNormLedColor_container = v.findViewById(R.id.prefMsgNormLedColor_container);
|
||||||
prefMsgNormEnableVibrations = v.findViewById(R.id.prefMsgNormEnableVibrations);
|
prefMsgNormEnableVibrations = v.findViewById(R.id.prefMsgNormEnableVibrations);
|
||||||
|
prefMsgNormForceVolume = v.findViewById(R.id.prefMsgNormForceVolume);
|
||||||
|
prefMsgNormVolume = v.findViewById(R.id.prefMsgNormVolume);
|
||||||
|
prefMsgNormVolumeTest = v.findViewById(R.id.btnNormVolumeTest);
|
||||||
|
|
||||||
prefMsgHighEnableSound = v.findViewById(R.id.prefMsgHighEnableSound);
|
prefMsgHighEnableSound = v.findViewById(R.id.prefMsgHighEnableSound);
|
||||||
prefMsgHighRingtone_value = v.findViewById(R.id.prefMsgHighRingtone_value);
|
prefMsgHighRingtone_value = v.findViewById(R.id.prefMsgHighRingtone_value);
|
||||||
@@ -117,6 +150,9 @@ 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);
|
||||||
|
prefMsgHighVolume = v.findViewById(R.id.prefMsgHighVolume);
|
||||||
|
prefMsgHighVolumeTest = v.findViewById(R.id.btnHighVolumeTest);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateUI()
|
private void updateUI()
|
||||||
@@ -126,6 +162,7 @@ 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);
|
||||||
|
|
||||||
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);
|
||||||
@@ -142,6 +179,12 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
|
|||||||
if (prefMsgLowEnableLED.isChecked() != s.PriorityLow.EnableLED) prefMsgLowEnableLED.setChecked(s.PriorityLow.EnableLED);
|
if (prefMsgLowEnableLED.isChecked() != s.PriorityLow.EnableLED) prefMsgLowEnableLED.setChecked(s.PriorityLow.EnableLED);
|
||||||
prefMsgLowLedColor_value.setColorFilter(s.PriorityLow.LEDColor);
|
prefMsgLowLedColor_value.setColorFilter(s.PriorityLow.LEDColor);
|
||||||
if (prefMsgLowEnableVibrations.isChecked() != s.PriorityLow.EnableVibration) prefMsgLowEnableVibrations.setChecked(s.PriorityLow.EnableVibration);
|
if (prefMsgLowEnableVibrations.isChecked() != s.PriorityLow.EnableVibration) prefMsgLowEnableVibrations.setChecked(s.PriorityLow.EnableVibration);
|
||||||
|
if (prefMsgLowForceVolume.isChecked() != s.PriorityLow.ForceVolume) prefMsgLowForceVolume.setChecked(s.PriorityLow.ForceVolume);
|
||||||
|
if (prefMsgLowVolume.getMax() != 100) prefMsgLowVolume.setMax(100);
|
||||||
|
if (prefMsgLowVolume.getProgress() != s.PriorityLow.ForceVolumeValue) prefMsgLowVolume.setProgress(s.PriorityLow.ForceVolumeValue);
|
||||||
|
if (prefMsgLowVolume.isEnabled() != s.PriorityLow.ForceVolume) prefMsgLowVolume.setEnabled(s.PriorityLow.ForceVolume);
|
||||||
|
if (prefMsgLowVolumeTest.isEnabled() != s.PriorityLow.ForceVolume) prefMsgLowVolumeTest.setEnabled(s.PriorityLow.ForceVolume);
|
||||||
|
if (s.PriorityLow.ForceVolume) prefMsgLowVolumeTest.setColorFilter(null); else prefMsgLowVolumeTest.setColorFilter(Color.argb(150,200,200,200));
|
||||||
|
|
||||||
if (prefMsgNormEnableSound.isChecked() != s.PriorityNorm.EnableSound) prefMsgNormEnableSound.setChecked(s.PriorityNorm.EnableSound);
|
if (prefMsgNormEnableSound.isChecked() != s.PriorityNorm.EnableSound) prefMsgNormEnableSound.setChecked(s.PriorityNorm.EnableSound);
|
||||||
if (!prefMsgNormRingtone_value.getText().equals(s.PriorityNorm.SoundName)) prefMsgNormRingtone_value.setText(s.PriorityNorm.SoundName);
|
if (!prefMsgNormRingtone_value.getText().equals(s.PriorityNorm.SoundName)) prefMsgNormRingtone_value.setText(s.PriorityNorm.SoundName);
|
||||||
@@ -149,6 +192,12 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
|
|||||||
if (prefMsgNormEnableLED.isChecked() != s.PriorityNorm.EnableLED) prefMsgNormEnableLED.setChecked(s.PriorityNorm.EnableLED);
|
if (prefMsgNormEnableLED.isChecked() != s.PriorityNorm.EnableLED) prefMsgNormEnableLED.setChecked(s.PriorityNorm.EnableLED);
|
||||||
prefMsgNormLedColor_value.setColorFilter(s.PriorityNorm.LEDColor);
|
prefMsgNormLedColor_value.setColorFilter(s.PriorityNorm.LEDColor);
|
||||||
if (prefMsgNormEnableVibrations.isChecked() != s.PriorityNorm.EnableVibration) prefMsgNormEnableVibrations.setChecked(s.PriorityNorm.EnableVibration);
|
if (prefMsgNormEnableVibrations.isChecked() != s.PriorityNorm.EnableVibration) prefMsgNormEnableVibrations.setChecked(s.PriorityNorm.EnableVibration);
|
||||||
|
if (prefMsgNormForceVolume.isChecked() != s.PriorityNorm.ForceVolume) prefMsgNormForceVolume.setChecked(s.PriorityNorm.ForceVolume);
|
||||||
|
if (prefMsgNormVolume.getMax() != 100) prefMsgNormVolume.setMax(100);
|
||||||
|
if (prefMsgNormVolume.getProgress() != s.PriorityNorm.ForceVolumeValue) prefMsgNormVolume.setProgress(s.PriorityNorm.ForceVolumeValue);
|
||||||
|
if (prefMsgNormVolume.isEnabled() != s.PriorityNorm.ForceVolume) prefMsgNormVolume.setEnabled(s.PriorityNorm.ForceVolume);
|
||||||
|
if (prefMsgNormVolumeTest.isEnabled() != s.PriorityNorm.ForceVolume) prefMsgNormVolumeTest.setEnabled(s.PriorityNorm.ForceVolume);
|
||||||
|
if (s.PriorityNorm.ForceVolume) prefMsgNormVolumeTest.setColorFilter(null); else prefMsgNormVolumeTest.setColorFilter(Color.argb(150,200,200,200));
|
||||||
|
|
||||||
if (prefMsgHighEnableSound.isChecked() != s.PriorityHigh.EnableSound) prefMsgHighEnableSound.setChecked(s.PriorityHigh.EnableSound);
|
if (prefMsgHighEnableSound.isChecked() != s.PriorityHigh.EnableSound) prefMsgHighEnableSound.setChecked(s.PriorityHigh.EnableSound);
|
||||||
if (!prefMsgHighRingtone_value.getText().equals(s.PriorityHigh.SoundName)) prefMsgHighRingtone_value.setText(s.PriorityHigh.SoundName);
|
if (!prefMsgHighRingtone_value.getText().equals(s.PriorityHigh.SoundName)) prefMsgHighRingtone_value.setText(s.PriorityHigh.SoundName);
|
||||||
@@ -156,13 +205,20 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
|
|||||||
if (prefMsgHighEnableLED.isChecked() != s.PriorityHigh.EnableLED) prefMsgHighEnableLED.setChecked(s.PriorityHigh.EnableLED);
|
if (prefMsgHighEnableLED.isChecked() != s.PriorityHigh.EnableLED) prefMsgHighEnableLED.setChecked(s.PriorityHigh.EnableLED);
|
||||||
prefMsgHighLedColor_value.setColorFilter(s.PriorityHigh.LEDColor);
|
prefMsgHighLedColor_value.setColorFilter(s.PriorityHigh.LEDColor);
|
||||||
if (prefMsgHighEnableVibrations.isChecked() != s.PriorityHigh.EnableVibration) prefMsgHighEnableVibrations.setChecked(s.PriorityHigh.EnableVibration);
|
if (prefMsgHighEnableVibrations.isChecked() != s.PriorityHigh.EnableVibration) prefMsgHighEnableVibrations.setChecked(s.PriorityHigh.EnableVibration);
|
||||||
|
if (prefMsgHighForceVolume.isChecked() != s.PriorityHigh.ForceVolume) prefMsgHighForceVolume.setChecked(s.PriorityHigh.ForceVolume);
|
||||||
|
if (prefMsgHighVolume.getMax() != 100) prefMsgHighVolume.setMax(100);
|
||||||
|
if (prefMsgHighVolume.getProgress() != s.PriorityHigh.ForceVolumeValue) prefMsgHighVolume.setProgress(s.PriorityHigh.ForceVolumeValue);
|
||||||
|
if (prefMsgHighVolume.isEnabled() != s.PriorityHigh.ForceVolume) prefMsgHighVolume.setEnabled(s.PriorityHigh.ForceVolume);
|
||||||
|
if (prefMsgHighVolumeTest.isEnabled() != s.PriorityHigh.ForceVolume) prefMsgHighVolumeTest.setEnabled(s.PriorityHigh.ForceVolume);
|
||||||
|
if (s.PriorityHigh.ForceVolume) prefMsgHighVolumeTest.setColorFilter(null); else prefMsgHighVolumeTest.setColorFilter(Color.argb(150,200,200,200));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initListener()
|
private void initListener()
|
||||||
{
|
{
|
||||||
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(); });
|
||||||
|
|
||||||
prefLocalCacheSize.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener()
|
prefLocalCacheSize.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener()
|
||||||
{
|
{
|
||||||
@@ -181,6 +237,9 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
|
|||||||
prefMsgLowEnableLED.setOnCheckedChangeListener((a,b) -> { s.PriorityLow.EnableLED=b; saveAndUpdate(); });
|
prefMsgLowEnableLED.setOnCheckedChangeListener((a,b) -> { s.PriorityLow.EnableLED=b; saveAndUpdate(); });
|
||||||
prefMsgLowLedColor_container.setOnClickListener(a -> chooseLEDColorLow());
|
prefMsgLowLedColor_container.setOnClickListener(a -> chooseLEDColorLow());
|
||||||
prefMsgLowEnableVibrations.setOnCheckedChangeListener((a,b) -> { s.PriorityLow.EnableVibration=b; saveAndUpdate(); });
|
prefMsgLowEnableVibrations.setOnCheckedChangeListener((a,b) -> { s.PriorityLow.EnableVibration=b; saveAndUpdate(); });
|
||||||
|
prefMsgLowForceVolume.setOnCheckedChangeListener((a,b) -> { s.PriorityLow.ForceVolume=b; saveAndUpdate(); });
|
||||||
|
prefMsgLowVolume.setOnSeekBarChangeListener(FI.SeekBarChanged((a,b,c) -> { if (c) { s.PriorityLow.ForceVolumeValue=b; saveAndUpdate(); updateVolume(0, b); } }));
|
||||||
|
prefMsgLowVolumeTest.setOnClickListener((v) -> { if (s.PriorityLow.ForceVolume) playTestSound(0, prefMsgLowVolumeTest, s.PriorityLow.SoundSource, s.PriorityLow.ForceVolumeValue); });
|
||||||
|
|
||||||
prefMsgNormEnableSound.setOnCheckedChangeListener((a,b) -> { s.PriorityNorm.EnableSound=b; saveAndUpdate(); });
|
prefMsgNormEnableSound.setOnCheckedChangeListener((a,b) -> { s.PriorityNorm.EnableSound=b; saveAndUpdate(); });
|
||||||
prefMsgNormRingtone_container.setOnClickListener(a -> chooseRingtoneNorm());
|
prefMsgNormRingtone_container.setOnClickListener(a -> chooseRingtoneNorm());
|
||||||
@@ -188,6 +247,9 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
|
|||||||
prefMsgNormEnableLED.setOnCheckedChangeListener((a,b) -> { s.PriorityNorm.EnableLED=b; saveAndUpdate(); });
|
prefMsgNormEnableLED.setOnCheckedChangeListener((a,b) -> { s.PriorityNorm.EnableLED=b; saveAndUpdate(); });
|
||||||
prefMsgNormLedColor_container.setOnClickListener(a -> chooseLEDColorNorm());
|
prefMsgNormLedColor_container.setOnClickListener(a -> chooseLEDColorNorm());
|
||||||
prefMsgNormEnableVibrations.setOnCheckedChangeListener((a,b) -> { s.PriorityNorm.EnableVibration=b; saveAndUpdate(); });
|
prefMsgNormEnableVibrations.setOnCheckedChangeListener((a,b) -> { s.PriorityNorm.EnableVibration=b; saveAndUpdate(); });
|
||||||
|
prefMsgNormForceVolume.setOnCheckedChangeListener((a,b) -> { s.PriorityNorm.ForceVolume=b; saveAndUpdate(); });
|
||||||
|
prefMsgNormVolume.setOnSeekBarChangeListener(FI.SeekBarChanged((a,b,c) -> { if (c) { s.PriorityNorm.ForceVolumeValue=b; saveAndUpdate(); updateVolume(1, b); } }));
|
||||||
|
prefMsgNormVolumeTest.setOnClickListener((v) -> { if (s.PriorityNorm.ForceVolume) playTestSound(1, prefMsgNormVolumeTest, s.PriorityNorm.SoundSource, s.PriorityNorm.ForceVolumeValue); });
|
||||||
|
|
||||||
prefMsgHighEnableSound.setOnCheckedChangeListener((a,b) -> { s.PriorityHigh.EnableSound=b; saveAndUpdate(); });
|
prefMsgHighEnableSound.setOnCheckedChangeListener((a,b) -> { s.PriorityHigh.EnableSound=b; saveAndUpdate(); });
|
||||||
prefMsgHighRingtone_container.setOnClickListener(a -> chooseRingtoneHigh());
|
prefMsgHighRingtone_container.setOnClickListener(a -> chooseRingtoneHigh());
|
||||||
@@ -195,13 +257,91 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
|
|||||||
prefMsgHighEnableLED.setOnCheckedChangeListener((a,b) -> { s.PriorityHigh.EnableLED=b; saveAndUpdate(); });
|
prefMsgHighEnableLED.setOnCheckedChangeListener((a,b) -> { s.PriorityHigh.EnableLED=b; saveAndUpdate(); });
|
||||||
prefMsgHighLedColor_container.setOnClickListener(a -> chooseLEDColorHigh());
|
prefMsgHighLedColor_container.setOnClickListener(a -> chooseLEDColorHigh());
|
||||||
prefMsgHighEnableVibrations.setOnCheckedChangeListener((a,b) -> { s.PriorityHigh.EnableVibration=b; saveAndUpdate(); });
|
prefMsgHighEnableVibrations.setOnCheckedChangeListener((a,b) -> { s.PriorityHigh.EnableVibration=b; saveAndUpdate(); });
|
||||||
|
prefMsgHighForceVolume.setOnCheckedChangeListener((a,b) -> { s.PriorityHigh.ForceVolume=b; saveAndUpdate(); });
|
||||||
|
prefMsgHighVolume.setOnSeekBarChangeListener(FI.SeekBarChanged((a,b,c) -> { if (c) { s.PriorityHigh.ForceVolumeValue=b; saveAndUpdate(); updateVolume(2, b); } }));
|
||||||
|
prefMsgHighVolumeTest.setOnClickListener((v) -> { if (s.PriorityHigh.ForceVolume) playTestSound(2, prefMsgHighVolumeTest, s.PriorityHigh.SoundSource, s.PriorityHigh.ForceVolumeValue); });
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateEnabled(boolean prev, boolean now)
|
||||||
|
{
|
||||||
|
if (!prev && now)
|
||||||
|
{
|
||||||
|
SCNApp.showToast("SimpleCloudNotifier is now enabled", Toast.LENGTH_SHORT);
|
||||||
|
}
|
||||||
|
else if (prev && !now)
|
||||||
|
{
|
||||||
|
SCNApp.showToast("SimpleCloudNotifier is now disabled\nYou won't recieve new messages.", Toast.LENGTH_LONG);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateVolume(int idx, int volume)
|
||||||
|
{
|
||||||
|
if (mPlayers[idx] != null && mPlayers[idx].isPlaying())
|
||||||
|
{
|
||||||
|
AudioManager aman = (AudioManager) SCNApp.getContext().getSystemService(Context.AUDIO_SERVICE);
|
||||||
|
int maxVolume = aman.getStreamMaxVolume(AudioManager.STREAM_NOTIFICATION);
|
||||||
|
aman.setStreamVolume(AudioManager.STREAM_NOTIFICATION, (int)(maxVolume * (volume / 100.0)), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stopSound(final int idx, final ImageView iv)
|
||||||
|
{
|
||||||
|
if (mPlayers[idx] != null && mPlayers[idx].isPlaying())
|
||||||
|
{
|
||||||
|
mPlayers[idx].stop();
|
||||||
|
mPlayers[idx].release();
|
||||||
|
iv.setImageResource(R.drawable.ic_play);
|
||||||
|
mPlayers[idx] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void playTestSound(final int idx, final ImageView iv, String src, int volume)
|
||||||
|
{
|
||||||
|
if (mPlayers[idx] != null && mPlayers[idx].isPlaying())
|
||||||
|
{
|
||||||
|
mPlayers[idx].stop();
|
||||||
|
mPlayers[idx].release();
|
||||||
|
iv.setImageResource(R.drawable.ic_play);
|
||||||
|
mPlayers[idx] = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Str.isNullOrWhitespace(src)) return;
|
||||||
|
if (volume == 0) return;
|
||||||
|
|
||||||
|
Context ctxt = getContext();
|
||||||
|
if (ctxt == null) return;
|
||||||
|
|
||||||
|
iv.setImageResource(R.drawable.ic_pause);
|
||||||
|
|
||||||
|
AudioManager aman = (AudioManager) SCNApp.getContext().getSystemService(Context.AUDIO_SERVICE);
|
||||||
|
int maxVolume = aman.getStreamMaxVolume(AudioManager.STREAM_NOTIFICATION);
|
||||||
|
aman.setStreamVolume(AudioManager.STREAM_NOTIFICATION, (int)(maxVolume * (volume / 100.0)), 0);
|
||||||
|
|
||||||
|
MediaPlayer player = mPlayers[idx] = new MediaPlayer();
|
||||||
|
player.setAudioAttributes(new AudioAttributes.Builder().setLegacyStreamType(AudioManager.STREAM_NOTIFICATION).build());
|
||||||
|
player.setAudioStreamType(AudioManager.STREAM_NOTIFICATION);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
player.setDataSource(ctxt, Uri.parse(src));
|
||||||
|
player.setLooping(false);
|
||||||
|
player.setOnCompletionListener( mp -> SCNApp.runOnUiThread(() -> { mp.stop(); iv.setImageResource(R.drawable.ic_play); mPlayers[idx]=null; mp.release(); }));
|
||||||
|
player.setOnSeekCompleteListener(mp -> SCNApp.runOnUiThread(() -> { mp.stop(); iv.setImageResource(R.drawable.ic_play); mPlayers[idx]=null; mp.release(); }));
|
||||||
|
player.prepare();
|
||||||
|
player.start();
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
Log.e("SFRAG:play", e.toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveAndUpdate()
|
private void saveAndUpdate()
|
||||||
{
|
{
|
||||||
SCNSettings.inst().save();
|
SCNSettings.inst().save();
|
||||||
updateUI();
|
updateUI();
|
||||||
NotificationService.inst().updateChannels();
|
SCNApp.getMainActivity().adpTabs.tab1.touchHelper.updateEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onUpgradeAccount()
|
private void onUpgradeAccount()
|
||||||
@@ -213,9 +353,9 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
|
|||||||
{
|
{
|
||||||
Purchase p = IABService.inst().getPurchaseCached(IABService.IAB_PRO_MODE);
|
Purchase p = IABService.inst().getPurchaseCached(IABService.IAB_PRO_MODE);
|
||||||
|
|
||||||
prefUpgradeAccount.setVisibility( p != null ? View.GONE : View.VISIBLE);
|
if (prefUpgradeAccount != null) prefUpgradeAccount.setVisibility( p != null ? View.GONE : View.VISIBLE);
|
||||||
prefUpgradeAccount_info.setVisibility(p != null ? View.GONE : View.VISIBLE);
|
if (prefUpgradeAccount_info != null) prefUpgradeAccount_info.setVisibility(p != null ? View.GONE : View.VISIBLE);
|
||||||
prefUpgradeAccount_msg.setVisibility( p != null ? View.VISIBLE : View.GONE );
|
if (prefUpgradeAccount_msg != null) prefUpgradeAccount_msg.setVisibility( p != null ? View.VISIBLE : View.GONE );
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getCacheSizeIndex(int value)
|
private int getCacheSizeIndex(int value)
|
||||||
@@ -233,7 +373,7 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
|
|||||||
UltimateMusicPicker ump = new UltimateMusicPicker();
|
UltimateMusicPicker ump = new UltimateMusicPicker();
|
||||||
ump.windowTitle("Choose notification sound");
|
ump.windowTitle("Choose notification sound");
|
||||||
ump.removeSilent();
|
ump.removeSilent();
|
||||||
ump.streamType(AudioManager.STREAM_ALARM);
|
ump.streamType(AudioManager.STREAM_NOTIFICATION);
|
||||||
ump.ringtone();
|
ump.ringtone();
|
||||||
ump.notification();
|
ump.notification();
|
||||||
ump.alarm();
|
ump.alarm();
|
||||||
@@ -248,7 +388,7 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
|
|||||||
UltimateMusicPicker ump = new UltimateMusicPicker();
|
UltimateMusicPicker ump = new UltimateMusicPicker();
|
||||||
ump.windowTitle("Choose notification sound");
|
ump.windowTitle("Choose notification sound");
|
||||||
ump.removeSilent();
|
ump.removeSilent();
|
||||||
ump.streamType(AudioManager.STREAM_ALARM);
|
ump.streamType(AudioManager.STREAM_NOTIFICATION);
|
||||||
ump.ringtone();
|
ump.ringtone();
|
||||||
ump.notification();
|
ump.notification();
|
||||||
ump.alarm();
|
ump.alarm();
|
||||||
@@ -263,7 +403,7 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
|
|||||||
UltimateMusicPicker ump = new UltimateMusicPicker();
|
UltimateMusicPicker ump = new UltimateMusicPicker();
|
||||||
ump.windowTitle("Choose notification sound");
|
ump.windowTitle("Choose notification sound");
|
||||||
ump.removeSilent();
|
ump.removeSilent();
|
||||||
ump.streamType(AudioManager.STREAM_ALARM);
|
ump.streamType(AudioManager.STREAM_NOTIFICATION);
|
||||||
ump.ringtone();
|
ump.ringtone();
|
||||||
ump.notification();
|
ump.notification();
|
||||||
ump.alarm();
|
ump.alarm();
|
||||||
@@ -356,4 +496,11 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
|
|||||||
{
|
{
|
||||||
musicPickerSwitch = -1;
|
musicPickerSwitch = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void onViewpagerHide()
|
||||||
|
{
|
||||||
|
stopSound(0, prefMsgLowVolumeTest);
|
||||||
|
stopSound(1, prefMsgNormVolumeTest);
|
||||||
|
stopSound(2, prefMsgHighVolumeTest);
|
||||||
|
}
|
||||||
}
|
}
|
@@ -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;
|
||||||
|
BIN
android/app/src/main/res/drawable-hdpi/ic_notification_white.png
Normal file
After Width: | Height: | Size: 472 B |
BIN
android/app/src/main/res/drawable-mdpi/ic_notification_white.png
Normal file
After Width: | Height: | Size: 320 B |
After Width: | Height: | Size: 551 B |
After Width: | Height: | Size: 949 B |
After Width: | Height: | Size: 1.0 KiB |
@@ -1,15 +0,0 @@
|
|||||||
<vector android:height="24dp" android:viewportHeight="1000"
|
|
||||||
android:viewportWidth="1000" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<path android:fillColor="#FF000000"
|
|
||||||
android:pathData="M500,500m-500,0a500,500 0,1 1,1000 0a500,500 0,1 1,-1000 0"
|
|
||||||
android:strokeColor="#000000" android:strokeWidth="1"/>
|
|
||||||
<path android:fillColor="#ffffff" android:pathData="M300,694L700,694L500,136L300,694"/>
|
|
||||||
<path android:fillColor="#ffffff" android:pathData="M473,559L527,559L527,774L473,774"/>
|
|
||||||
<path android:fillColor="#000000" android:pathData="M376,640L624,640L500,295L376,640"/>
|
|
||||||
<path android:fillColor="#ffffff" android:pathData="M100,730L500,730L300,172L100,730"/>
|
|
||||||
<path android:fillColor="#000000" android:pathData="M176,676L424,676L300,331L176,676"/>
|
|
||||||
<path android:fillColor="#ffffff" android:pathData="M273,595L327,595L327,810L273,810"/>
|
|
||||||
<path android:fillColor="#ffffff" android:pathData="M500,730L900,730L700,172L500,730"/>
|
|
||||||
<path android:fillColor="#000000" android:pathData="M576,676L824,676L700,331L576,676"/>
|
|
||||||
<path android:fillColor="#ffffff" android:pathData="M673,595L727,595L727,810L673,810"/>
|
|
||||||
</vector>
|
|
16
android/app/src/main/res/drawable/ic_pause.xml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<vector
|
||||||
|
android:height="24dp"
|
||||||
|
android:width="24dp"
|
||||||
|
android:viewportHeight="438.536"
|
||||||
|
android:viewportWidth="438.536"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF3F51B5"
|
||||||
|
android:pathData="M164.453,0H18.276C13.324,0 9.041,1.807 5.425,5.424C1.808,9.04 0.001,13.322 0.001,18.271v401.991c0,4.948 1.807,9.233 5.424,12.847c3.619,3.617 7.902,5.428 12.851,5.428h146.181c4.949,0 9.231,-1.811 12.847,-5.428c3.617,-3.613 5.424,-7.898 5.424,-12.847V18.271c0,-4.952 -1.807,-9.231 -5.428,-12.847C173.685,1.807 169.402,0 164.453,0z"/>
|
||||||
|
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF3F51B5"
|
||||||
|
android:pathData="M433.113,5.424C429.496,1.807 425.215,0 420.267,0H274.086c-4.949,0 -9.237,1.807 -12.847,5.424c-3.621,3.615 -5.432,7.898 -5.432,12.847v401.991c0,4.948 1.811,9.233 5.432,12.847c3.609,3.617 7.897,5.428 12.847,5.428h146.181c4.948,0 9.229,-1.811 12.847,-5.428c3.614,-3.613 5.421,-7.898 5.421,-12.847V18.271C438.534,13.319 436.73,9.04 433.113,5.424z"/>
|
||||||
|
|
||||||
|
</vector>
|
12
android/app/src/main/res/drawable/ic_play.xml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<vector
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportHeight="41.999"
|
||||||
|
android:viewportWidth="41.999"
|
||||||
|
android:width="24dp"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF3F51B5"
|
||||||
|
android:pathData="M36.068,20.176l-29,-20C6.761,-0.035 6.363,-0.057 6.035,0.114C5.706,0.287 5.5,0.627 5.5,0.999v40c0,0.372 0.206,0.713 0.535,0.886c0.146,0.076 0.306,0.114 0.465,0.114c0.199,0 0.397,-0.06 0.568,-0.177l29,-20c0.271,-0.187 0.432,-0.494 0.432,-0.823S36.338,20.363 36.068,20.176z"/>
|
||||||
|
|
||||||
|
</vector>
|
16
android/app/src/main/res/drawable/ic_trash.xml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<vector
|
||||||
|
android:height="24dp"
|
||||||
|
android:width="24dp"
|
||||||
|
android:viewportHeight="53"
|
||||||
|
android:viewportWidth="53"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:pathData="M42.943,6H33.5V3c0,-1.654 -1.346,-3 -3,-3h-8c-1.654,0 -3,1.346 -3,3v3h-9.443C8.096,6 6.5,7.596 6.5,9.557V14h2h36h2V9.557C46.5,7.596 44.904,6 42.943,6zM31.5,6h-10V3c0,-0.552 0.449,-1 1,-1h8c0.551,0 1,0.448 1,1V6z"/>
|
||||||
|
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:pathData="M8.5,49.271C8.5,51.327 10.173,53 12.229,53h28.541c2.057,0 3.729,-1.673 3.729,-3.729V16h-36V49.271z"/>
|
||||||
|
|
||||||
|
</vector>
|
12
android/app/src/main/res/drawable/ic_volume.xml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<vector
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportHeight="459"
|
||||||
|
android:viewportWidth="459"
|
||||||
|
android:width="24dp"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF757575"
|
||||||
|
android:pathData="M0,153v153h102l127.5,127.5v-408L102,153H0zM344.25,229.5c0,-45.9 -25.5,-84.15 -63.75,-102v204C318.75,313.65 344.25,275.4 344.25,229.5zM280.5,5.1v53.55C354.45,81.6 408,147.899 408,229.5S354.45,377.4 280.5,400.35V453.9C382.5,430.949 459,339.15 459,229.5C459,119.85 382.5,28.049 280.5,5.1z"/>
|
||||||
|
|
||||||
|
</vector>
|
@@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
|
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"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@@ -4,7 +4,6 @@
|
|||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
tools:ignore="TooManyViews"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:context=".view.SettingsFragment">
|
tools:context=".view.SettingsFragment">
|
||||||
@@ -33,23 +32,14 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<Switch
|
||||||
android:layout_marginStart="4dp"
|
android:layout_marginStart="4dp"
|
||||||
android:layout_marginEnd="4dp"
|
android:layout_marginEnd="4dp"
|
||||||
|
android:id="@+id/prefAppEnabled"
|
||||||
|
android:text="@string/str_enabled"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:minHeight="48dp">
|
android:minHeight="48dp" />
|
||||||
|
|
||||||
<Switch
|
|
||||||
android:id="@+id/prefAppEnabled"
|
|
||||||
android:text="@string/str_enabled"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -67,7 +57,7 @@
|
|||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tvLocalCacheSize"
|
android:id="@+id/tvLocalCacheSize"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/str_localcachesize"
|
android:text="@string/str_localcachesize"
|
||||||
android:textColor="#000"
|
android:textColor="#000"
|
||||||
@@ -89,6 +79,22 @@
|
|||||||
|
|
||||||
</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
|
<View
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="1dp"
|
android:layout_height="1dp"
|
||||||
@@ -102,6 +108,7 @@
|
|||||||
android:layout_marginEnd="4dp"
|
android:layout_marginEnd="4dp"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center|center"
|
||||||
android:minHeight="48dp">
|
android:minHeight="48dp">
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
@@ -151,22 +158,118 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<Switch
|
||||||
|
android:id="@+id/prefMsgLowEnableSound"
|
||||||
|
android:text="@string/str_msg_enablesound"
|
||||||
android:layout_marginStart="4dp"
|
android:layout_marginStart="4dp"
|
||||||
android:layout_marginEnd="4dp"
|
android:layout_marginEnd="4dp"
|
||||||
|
android:minHeight="48dp"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:layout_marginBottom="2dp"
|
||||||
|
android:layout_marginTop="2dp"
|
||||||
|
android:background="#c0c0c0"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/prefMsgLowRingtone_container"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:minHeight="48dp">
|
android:orientation="vertical"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:layout_marginEnd="4dp"
|
||||||
|
android:minHeight="48dp"
|
||||||
|
android:gravity="center">
|
||||||
|
|
||||||
<Switch
|
<TextView
|
||||||
android:id="@+id/prefMsgLowEnableSound"
|
android:id="@+id/tvMsgLowRingtone"
|
||||||
android:text="@string/str_msg_enablesound"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/str_notificationsound"
|
||||||
|
android:textColor="#000" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/prefMsgLowRingtone_value"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:minWidth="64dp"
|
||||||
|
android:spinnerMode="dialog"
|
||||||
|
android:text="Whatever"
|
||||||
|
tools:ignore="HardcodedText" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:layout_marginBottom="2dp"
|
||||||
|
android:layout_marginTop="2dp"
|
||||||
|
android:background="#c0c0c0"/>
|
||||||
|
|
||||||
|
<Switch
|
||||||
|
android:id="@+id/prefMsgLowRepeatSound"
|
||||||
|
android:text="@string/str_repeatnotificationsound"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:layout_marginEnd="4dp"
|
||||||
|
android:minHeight="48dp" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:layout_marginBottom="2dp"
|
||||||
|
android:layout_marginTop="2dp"
|
||||||
|
android:background="#c0c0c0"/>
|
||||||
|
|
||||||
|
<Switch
|
||||||
|
android:id="@+id/prefMsgLowForceVolume"
|
||||||
|
android:text="@string/str_forcevolume"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:layout_marginEnd="4dp"
|
||||||
|
android:minHeight="48dp" />
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:columnCount="3"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:layout_marginEnd="4dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/icnLowVolume"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:contentDescription="@string/volume_icon"
|
||||||
|
android:src="@drawable/ic_volume"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
|
|
||||||
|
<SeekBar
|
||||||
|
android:id="@+id/prefMsgLowVolume"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toStartOf="@+id/btnLowVolumeTest"
|
||||||
|
app:layout_constraintHorizontal_bias="0.5"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/icnLowVolume"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/btnLowVolumeTest"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:src="@drawable/ic_play"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
android:contentDescription="@string/play_test_sound" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
<View
|
<View
|
||||||
@@ -176,89 +279,14 @@
|
|||||||
android:layout_marginTop="2dp"
|
android:layout_marginTop="2dp"
|
||||||
android:background="#c0c0c0"/>
|
android:background="#c0c0c0"/>
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<Switch
|
||||||
|
android:id="@+id/prefMsgLowEnableLED"
|
||||||
|
android:text="@string/str_enable_led"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="4dp"
|
android:layout_marginStart="4dp"
|
||||||
android:layout_marginEnd="4dp"
|
android:layout_marginEnd="4dp"
|
||||||
android:minHeight="48dp">
|
android:minHeight="48dp" />
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/prefMsgLowRingtone_container"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/tvMsgLowRingtone"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/str_notificationsound"
|
|
||||||
android:textColor="#000" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/prefMsgLowRingtone_value"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:minWidth="64dp"
|
|
||||||
android:spinnerMode="dialog"
|
|
||||||
android:text="Whatever"
|
|
||||||
tools:ignore="HardcodedText" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</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"/>
|
|
||||||
|
|
||||||
<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">
|
|
||||||
|
|
||||||
<Switch
|
|
||||||
android:id="@+id/prefMsgLowRepeatSound"
|
|
||||||
android:text="@string/str_repeatnotificationsound"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
<View
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="1dp"
|
|
||||||
android:layout_marginBottom="2dp"
|
|
||||||
android:layout_marginTop="2dp"
|
|
||||||
android:background="#c0c0c0"/>
|
|
||||||
|
|
||||||
<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">
|
|
||||||
|
|
||||||
<Switch
|
|
||||||
android:id="@+id/prefMsgLowEnableLED"
|
|
||||||
android:text="@string/str_enable_led"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -308,23 +336,15 @@
|
|||||||
android:layout_marginTop="2dp"
|
android:layout_marginTop="2dp"
|
||||||
android:background="#c0c0c0"/>
|
android:background="#c0c0c0"/>
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<Switch
|
||||||
android:layout_marginStart="4dp"
|
android:id="@+id/prefMsgLowEnableVibrations"
|
||||||
android:layout_marginEnd="4dp"
|
android:text="@string/str_enable_vibration"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:minHeight="48dp">
|
android:layout_marginStart="4dp"
|
||||||
|
android:layout_marginEnd="4dp"
|
||||||
|
android:minHeight="48dp" />
|
||||||
|
|
||||||
<Switch
|
|
||||||
android:id="@+id/prefMsgLowEnableVibrations"
|
|
||||||
android:text="@string/str_enable_vibration"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
@@ -349,22 +369,101 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<Switch
|
||||||
android:layout_marginStart="4dp"
|
android:id="@+id/prefMsgNormEnableSound"
|
||||||
android:layout_marginEnd="4dp"
|
android:text="@string/str_msg_enablesound"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:minHeight="48dp">
|
android:layout_marginStart="4dp"
|
||||||
|
android:layout_marginEnd="4dp"
|
||||||
|
android:minHeight="48dp" />
|
||||||
|
|
||||||
<Switch
|
<View
|
||||||
android:id="@+id/prefMsgNormEnableSound"
|
android:layout_width="match_parent"
|
||||||
android:text="@string/str_msg_enablesound"
|
android:layout_height="1dp"
|
||||||
|
android:layout_marginBottom="2dp"
|
||||||
|
android:layout_marginTop="2dp"
|
||||||
|
android:background="#c0c0c0"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/prefMsgNormRingtone_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:layout_marginEnd="4dp"
|
||||||
|
android:minHeight="48dp"
|
||||||
|
android:gravity="center">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvMsgNormRingtone"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/str_notificationsound"
|
||||||
|
android:textColor="#000" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/prefMsgNormRingtone_value"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:minWidth="64dp"
|
||||||
|
android:spinnerMode="dialog"
|
||||||
|
android:text="Whatever"
|
||||||
|
tools:ignore="HardcodedText" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:layout_marginBottom="2dp"
|
||||||
|
android:layout_marginTop="2dp"
|
||||||
|
android:background="#c0c0c0"/>
|
||||||
|
|
||||||
|
<Switch
|
||||||
|
android:id="@+id/prefMsgNormForceVolume"
|
||||||
|
android:text="@string/str_forcevolume"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:layout_marginEnd="4dp"
|
||||||
|
android:minHeight="48dp" />
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:columnCount="3"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:layout_marginEnd="4dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/icnNormVolume"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:contentDescription="@string/volume_icon"
|
||||||
|
android:src="@drawable/ic_volume"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
|
|
||||||
|
<SeekBar
|
||||||
|
android:id="@+id/prefMsgNormVolume"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toStartOf="@+id/btnNormVolumeTest"
|
||||||
|
app:layout_constraintHorizontal_bias="0.5"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/icnNormVolume"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/btnNormVolumeTest"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:src="@drawable/ic_play"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
android:contentDescription="@string/play_test_sound" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
<View
|
<View
|
||||||
@@ -374,39 +473,14 @@
|
|||||||
android:layout_marginTop="2dp"
|
android:layout_marginTop="2dp"
|
||||||
android:background="#c0c0c0"/>
|
android:background="#c0c0c0"/>
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<Switch
|
||||||
|
android:id="@+id/prefMsgNormRepeatSound"
|
||||||
|
android:text="@string/str_repeatnotificationsound"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="4dp"
|
android:layout_marginStart="4dp"
|
||||||
android:layout_marginEnd="4dp"
|
android:layout_marginEnd="4dp"
|
||||||
android:minHeight="48dp">
|
android:minHeight="48dp" />
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/prefMsgNormRingtone_container"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/tvMsgNormRingtone"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/str_notificationsound"
|
|
||||||
android:textColor="#000" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/prefMsgNormRingtone_value"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:minWidth="64dp"
|
|
||||||
android:spinnerMode="dialog"
|
|
||||||
android:text="Whatever"
|
|
||||||
tools:ignore="HardcodedText" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -415,48 +489,14 @@
|
|||||||
android:layout_marginTop="2dp"
|
android:layout_marginTop="2dp"
|
||||||
android:background="#c0c0c0"/>
|
android:background="#c0c0c0"/>
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<Switch
|
||||||
android:layout_marginStart="4dp"
|
android:id="@+id/prefMsgNormEnableLED"
|
||||||
android:layout_marginEnd="4dp"
|
android:text="@string/str_enable_led"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:minHeight="48dp">
|
|
||||||
|
|
||||||
<Switch
|
|
||||||
android:id="@+id/prefMsgNormRepeatSound"
|
|
||||||
android:text="@string/str_repeatnotificationsound"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
<View
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="1dp"
|
|
||||||
android:layout_marginBottom="2dp"
|
|
||||||
android:layout_marginTop="2dp"
|
|
||||||
android:background="#c0c0c0"/>
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:layout_marginStart="4dp"
|
android:layout_marginStart="4dp"
|
||||||
android:layout_marginEnd="4dp"
|
android:layout_marginEnd="4dp"
|
||||||
android:layout_width="match_parent"
|
android:minHeight="48dp" />
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:minHeight="48dp">
|
|
||||||
|
|
||||||
<Switch
|
|
||||||
android:id="@+id/prefMsgNormEnableLED"
|
|
||||||
android:text="@string/str_enable_led"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -506,23 +546,14 @@
|
|||||||
android:layout_marginTop="2dp"
|
android:layout_marginTop="2dp"
|
||||||
android:background="#c0c0c0"/>
|
android:background="#c0c0c0"/>
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<Switch
|
||||||
android:layout_marginStart="4dp"
|
android:id="@+id/prefMsgNormEnableVibrations"
|
||||||
android:layout_marginEnd="4dp"
|
android:text="@string/str_enable_vibration"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:minHeight="48dp">
|
android:layout_marginStart="4dp"
|
||||||
|
android:layout_marginEnd="4dp"
|
||||||
<Switch
|
android:minHeight="48dp" />
|
||||||
android:id="@+id/prefMsgNormEnableVibrations"
|
|
||||||
android:text="@string/str_enable_vibration"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
@@ -547,22 +578,96 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<Switch
|
||||||
android:layout_marginStart="4dp"
|
android:id="@+id/prefMsgHighEnableSound"
|
||||||
android:layout_marginEnd="4dp"
|
android:text="@string/str_msg_enablesound"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:minHeight="48dp">
|
android:layout_marginStart="4dp"
|
||||||
|
android:layout_marginEnd="4dp"
|
||||||
|
android:minHeight="48dp" />
|
||||||
|
|
||||||
<Switch
|
<View
|
||||||
android:id="@+id/prefMsgHighEnableSound"
|
android:layout_width="match_parent"
|
||||||
android:text="@string/str_msg_enablesound"
|
android:layout_height="1dp"
|
||||||
|
android:layout_marginBottom="2dp"
|
||||||
|
android:layout_marginTop="2dp"
|
||||||
|
android:background="#c0c0c0"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/prefMsgHighRingtone_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:layout_marginEnd="4dp"
|
||||||
|
android:minHeight="48dp"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:gravity="center"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvMsgHighRingtone"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/str_notificationsound"
|
||||||
|
android:textColor="#000" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/prefMsgHighRingtone_value"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:minWidth="64dp"
|
||||||
|
android:spinnerMode="dialog"
|
||||||
|
android:text="Whatever"
|
||||||
|
tools:ignore="HardcodedText" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<Switch
|
||||||
|
android:id="@+id/prefMsgHighForceVolume"
|
||||||
|
android:text="@string/str_forcevolume"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:layout_marginEnd="4dp"
|
||||||
|
android:minHeight="48dp" />
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:columnCount="3"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:layout_marginEnd="4dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/icnHighVolume"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:contentDescription="@string/volume_icon"
|
||||||
|
android:src="@drawable/ic_volume"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
|
|
||||||
|
<SeekBar
|
||||||
|
android:id="@+id/prefMsgHighVolume"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toStartOf="@+id/btnHighVolumeTest"
|
||||||
|
app:layout_constraintHorizontal_bias="0.5"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/icnHighVolume"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/btnHighVolumeTest"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:src="@drawable/ic_play"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
android:contentDescription="@string/play_test_sound" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
<View
|
<View
|
||||||
@@ -572,39 +677,14 @@
|
|||||||
android:layout_marginTop="2dp"
|
android:layout_marginTop="2dp"
|
||||||
android:background="#c0c0c0"/>
|
android:background="#c0c0c0"/>
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<Switch
|
||||||
|
android:id="@+id/prefMsgHighRepeatSound"
|
||||||
|
android:text="@string/str_repeatnotificationsound"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="4dp"
|
android:layout_marginStart="4dp"
|
||||||
android:layout_marginEnd="4dp"
|
android:layout_marginEnd="4dp"
|
||||||
android:minHeight="48dp">
|
android:minHeight="48dp" />
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/prefMsgHighRingtone_container"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/tvMsgHighRingtone"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/str_notificationsound"
|
|
||||||
android:textColor="#000" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/prefMsgHighRingtone_value"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:minWidth="64dp"
|
|
||||||
android:spinnerMode="dialog"
|
|
||||||
android:text="Whatever"
|
|
||||||
tools:ignore="HardcodedText" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -613,48 +693,14 @@
|
|||||||
android:layout_marginTop="2dp"
|
android:layout_marginTop="2dp"
|
||||||
android:background="#c0c0c0"/>
|
android:background="#c0c0c0"/>
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<Switch
|
||||||
android:layout_marginStart="4dp"
|
android:id="@+id/prefMsgHighEnableLED"
|
||||||
android:layout_marginEnd="4dp"
|
android:text="@string/str_enable_led"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:minHeight="48dp">
|
|
||||||
|
|
||||||
<Switch
|
|
||||||
android:id="@+id/prefMsgHighRepeatSound"
|
|
||||||
android:text="@string/str_repeatnotificationsound"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
<View
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="1dp"
|
|
||||||
android:layout_marginBottom="2dp"
|
|
||||||
android:layout_marginTop="2dp"
|
|
||||||
android:background="#c0c0c0"/>
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:layout_marginStart="4dp"
|
android:layout_marginStart="4dp"
|
||||||
android:layout_marginEnd="4dp"
|
android:layout_marginEnd="4dp"
|
||||||
android:layout_width="match_parent"
|
android:minHeight="48dp" />
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:minHeight="48dp">
|
|
||||||
|
|
||||||
<Switch
|
|
||||||
android:id="@+id/prefMsgHighEnableLED"
|
|
||||||
android:text="@string/str_enable_led"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -704,23 +750,14 @@
|
|||||||
android:layout_marginTop="2dp"
|
android:layout_marginTop="2dp"
|
||||||
android:background="#c0c0c0"/>
|
android:background="#c0c0c0"/>
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<Switch
|
||||||
android:layout_marginStart="4dp"
|
android:id="@+id/prefMsgHighEnableVibrations"
|
||||||
android:layout_marginEnd="4dp"
|
android:text="@string/str_enable_vibration"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:minHeight="48dp">
|
android:layout_marginStart="4dp"
|
||||||
|
android:layout_marginEnd="4dp"
|
||||||
<Switch
|
android:minHeight="48dp" />
|
||||||
android:id="@+id/prefMsgHighEnableVibrations"
|
|
||||||
android:text="@string/str_enable_vibration"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
@@ -1,81 +1,117 @@
|
|||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:card_view="http://schemas.android.com/apk/res-auto"
|
xmlns:card_view="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView
|
<RelativeLayout
|
||||||
android:id="@+id/card_view"
|
android:id="@+id/layoutBack"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="match_parent"
|
||||||
android:layout_gravity="center"
|
|
||||||
android:layout_margin="@dimen/card_margin"
|
android:layout_margin="@dimen/card_margin"
|
||||||
android:elevation="3dp"
|
android:background="@color/bg_row_background">
|
||||||
card_view:cardCornerRadius="@dimen/card_album_radius">
|
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/delete_icon"
|
||||||
|
android:layout_width="@dimen/ic_delete"
|
||||||
|
android:layout_height="@dimen/ic_delete"
|
||||||
|
android:layout_alignParentRight="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_marginRight="@dimen/padd_10"
|
||||||
|
android:src="@drawable/ic_trash"
|
||||||
|
android:contentDescription="@string/delete" />
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<TextView
|
||||||
android:background="#FFFFFFFF"
|
android:layout_width="wrap_content"
|
||||||
android:layout_margin="3dp"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_marginRight="@dimen/padd_10"
|
||||||
|
android:layout_toLeftOf="@id/delete_icon"
|
||||||
|
android:text="@string/delete"
|
||||||
|
android:textColor="#fff"
|
||||||
|
android:textSize="13dp" />
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:id="@+id/layoutFront"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<androidx.cardview.widget.CardView
|
||||||
|
android:id="@+id/card_view"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_margin="@dimen/card_margin"
|
||||||
|
android:elevation="3dp"
|
||||||
|
card_view:cardCornerRadius="@dimen/card_album_radius">
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/tvTimestamp"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
|
||||||
android:textSize="12sp"
|
|
||||||
android:textStyle="italic"
|
|
||||||
|
|
||||||
android:text="2018-09-11 20:22:32" />
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:background="#FFFFFFFF"
|
||||||
|
android:layout_margin="3dp"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tvTitle"
|
android:id="@+id/tvTimestamp"
|
||||||
android:layout_width="0dp"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
app:layout_constraintRight_toLeftOf="@+id/tvTimestamp"
|
android:textSize="12sp"
|
||||||
android:layout_marginEnd="4sp"
|
android:textStyle="italic"
|
||||||
android:textSize="16sp"
|
|
||||||
android:textColor="@color/colorBlack"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:ellipsize="none"
|
|
||||||
android:maxLines="6"
|
|
||||||
|
|
||||||
android:text="Message from me"/>
|
android:text="2018-09-11 20:22:32" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tvMessage"
|
android:id="@+id/tvTitle"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/tvTitle"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintRight_toLeftOf="@+id/ivPriority"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintRight_toLeftOf="@+id/tvTimestamp"
|
||||||
android:layout_margin="4sp"
|
android:layout_marginEnd="4sp"
|
||||||
android:ellipsize="none"
|
android:textSize="16sp"
|
||||||
android:maxLines="32"
|
android:textColor="@color/colorBlack"
|
||||||
android:scrollHorizontally="false"
|
android:textStyle="bold"
|
||||||
|
android:ellipsize="none"
|
||||||
|
android:maxLines="6"
|
||||||
|
|
||||||
android:text="asdasd asdasd asdasd a" />
|
android:text="Message from me"/>
|
||||||
|
|
||||||
<ImageView
|
<TextView
|
||||||
android:id="@+id/ivPriority"
|
android:id="@+id/tvMessage"
|
||||||
android:tint="#BBB"
|
android:layout_width="0dp"
|
||||||
android:visibility="gone"
|
android:layout_height="wrap_content"
|
||||||
android:layout_width="24dp"
|
app:layout_constraintTop_toBottomOf="@+id/tvTitle"
|
||||||
android:layout_height="24dp"
|
app:layout_constraintRight_toLeftOf="@+id/ivPriority"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/tvTimestamp"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
android:layout_margin="4sp"
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
android:ellipsize="end"
|
||||||
android:layout_margin="4sp"
|
android:maxLines="6"
|
||||||
android:paddingTop="3dp"
|
android:scrollHorizontally="false"
|
||||||
android:contentDescription="@string/desc_priority_icon" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
android:text="asdasd asdasd asdasd a" />
|
||||||
|
|
||||||
</androidx.cardview.widget.CardView>
|
<ImageView
|
||||||
|
android:id="@+id/ivPriority"
|
||||||
|
android:tint="#BBB"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/tvTimestamp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
android:layout_margin="4sp"
|
||||||
|
android:paddingTop="3dp"
|
||||||
|
android:contentDescription="@string/desc_priority_icon" />
|
||||||
|
|
||||||
</LinearLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
</FrameLayout>
|
BIN
android/app/src/main/res/mipmap-hdpi/ic_notification_full.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
android/app/src/main/res/mipmap-mdpi/ic_notification_full.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
android/app/src/main/res/mipmap-xhdpi/ic_notification_full.png
Normal file
After Width: | Height: | Size: 6.5 KiB |
BIN
android/app/src/main/res/mipmap-xxhdpi/ic_notification_full.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
android/app/src/main/res/mipmap-xxxhdpi/ic_notification_full.png
Normal file
After Width: | Height: | Size: 17 KiB |
@@ -7,4 +7,6 @@
|
|||||||
<color name="colorHeaderForeground">#FFFFFF</color>
|
<color name="colorHeaderForeground">#FFFFFF</color>
|
||||||
|
|
||||||
<color name="colorBlack">#000</color>
|
<color name="colorBlack">#000</color>
|
||||||
|
|
||||||
|
<color name="bg_row_background">#fa315b</color>
|
||||||
</resources>
|
</resources>
|
||||||
|
@@ -2,4 +2,8 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<dimen name="card_margin">5dp</dimen>
|
<dimen name="card_margin">5dp</dimen>
|
||||||
<dimen name="card_album_radius">0dp</dimen>
|
<dimen name="card_album_radius">0dp</dimen>
|
||||||
|
|
||||||
|
<dimen name="padd_10">10dp</dimen>
|
||||||
|
<dimen name="ic_delete">30dp</dimen>
|
||||||
|
<dimen name="thumbnail">90dp</dimen>
|
||||||
</resources>
|
</resources>
|
@@ -11,23 +11,28 @@
|
|||||||
<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 (low priority)</string>
|
<string name="str_header_prio0">Notifications (priority 0 - Low)</string>
|
||||||
<string name="str_header_prio1">Notifications (normal priority)</string>
|
<string name="str_header_prio1">Notifications (priority 1 - Normal)</string>
|
||||||
<string name="str_header_prio2">Notifications (high priority)</string>
|
<string name="str_header_prio2">Notifications (priority 2 - High)</string>
|
||||||
<string name="str_msg_enablesound">Enable notification sound</string>
|
<string name="str_msg_enablesound">Enable notification sound</string>
|
||||||
<string name="str_notificationsound">Notification sound</string>
|
<string name="str_notificationsound">Notification sound</string>
|
||||||
<string name="str_repeatnotificationsound">Repeat notification sound</string>
|
<string name="str_repeatnotificationsound">Repeat notification sound</string>
|
||||||
|
<string name="str_forcevolume">Automatically set the volume</string>
|
||||||
<string name="str_enable_led">Enable notification light</string>
|
<string name="str_enable_led">Enable notification light</string>
|
||||||
<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_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="play_test_sound">Play test sound</string>
|
||||||
|
<string name="delete">DELETE</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@@ -1,3 +1,3 @@
|
|||||||
#Sun Nov 11 19:37:57 CET 2018
|
#Mon Nov 19 12:30:54 CET 2018
|
||||||
VERSION_NAME=0.0.5
|
VERSION_NAME=1.0.0
|
||||||
VERSION_CODE=5
|
VERSION_CODE=14
|
||||||
|
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/icon_web.pdn
Normal file
BIN
data/phone.pdn
Normal file
BIN
data/phone.png
Normal file
After Width: | Height: | Size: 1.0 MiB |
77
examples/scn_send.sh
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
#
|
||||||
|
# Call with `scn_send title`
|
||||||
|
# or `scn_send title content`
|
||||||
|
# or `scn_send title content priority`
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
if [ "$#" -lt 1 ]; then
|
||||||
|
echo "no title supplied via parameter"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# INSERT YOUR DATA HERE #
|
||||||
|
################################################################################
|
||||||
|
user_id=999
|
||||||
|
user_key="????????????????????????????????????????????????????????????????"
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
title=$1
|
||||||
|
content=""
|
||||||
|
|
||||||
|
if [ "$#" -gt 1 ]; then
|
||||||
|
content=$2
|
||||||
|
fi
|
||||||
|
|
||||||
|
priority=1
|
||||||
|
|
||||||
|
if [ "$#" -gt 2 ]; then
|
||||||
|
priority=$3
|
||||||
|
fi
|
||||||
|
|
||||||
|
usr_msg_id=$(uuidgen)
|
||||||
|
|
||||||
|
while true ; do
|
||||||
|
|
||||||
|
curlresp=$(curl -s -o /dev/null -w "%{http_code}" \
|
||||||
|
-d "user_id=$user_id" -d "user_key=$user_key" -d "title=$title" \
|
||||||
|
-d "content=$content" -d "priority=$priority" -d "msg_id=$usr_msg_id" \
|
||||||
|
https://scn.blackforestbytes.com/send.php)
|
||||||
|
|
||||||
|
if [ "$curlresp" == 200 ] ; then
|
||||||
|
echo "Successfully send"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$curlresp" == 400 ] ; then
|
||||||
|
echo "Bad request - something went wrong"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$curlresp" == 401 ] ; then
|
||||||
|
echo "Unauthorized - wrong userid/userkey"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$curlresp" == 403 ] ; then
|
||||||
|
echo "Quota exceeded - wait one hour before re-try"
|
||||||
|
sleep 3600
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$curlresp" == 412 ] ; then
|
||||||
|
echo "Precondition Failed - No device linked"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$curlresp" == 500 ] ; then
|
||||||
|
echo "Internal server error - waiting for better times"
|
||||||
|
sleep 60
|
||||||
|
fi
|
||||||
|
|
||||||
|
# if none of the above matched we probably hav no network ...
|
||||||
|
echo "Send failed (response code $curlresp) ... try again in 5s"
|
||||||
|
sleep 5
|
||||||
|
done
|
@@ -1,13 +1,13 @@
|
|||||||
SimpleCloudNotifier is a app to display messages that you can send to your phone with a simple POST request to the right URL.
|
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 |
@@ -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 -->
|
||||||
@@ -8,13 +18,21 @@
|
|||||||
<!--<link rel="stylesheet" href="/css/mini-dark.min.css">-->
|
<!--<link rel="stylesheet" href="/css/mini-dark.min.css">-->
|
||||||
<link rel="stylesheet" href="/css/style.css">
|
<link rel="stylesheet" href="/css/style.css">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="icon" type="image/png" href="/favicon.png"/>
|
||||||
|
<link rel="icon" type="image/png" href="/favicon.ico"/>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="mainpnl">
|
|
||||||
<a href="https://play.google.com/store/apps/details?id=com.blackforestbytes.simplecloudnotifier" class="button bordered" id="tl_link"><span class="icn-google-play"></span></a>
|
|
||||||
<a href="/index.php" class="button bordered" id="tr_link">Send</a>
|
|
||||||
|
|
||||||
<a href="/" class="linkcaption"><h1>Simple Cloud Notifier</h1></a>
|
<div id="copyinfo">
|
||||||
|
<a tabindex="-1" href="https://www.blackforestbytes.com">© blackforestbytes</a>
|
||||||
|
<a tabindex="-1" href="https://www.mikescher.com">made by Mike Schwörer</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="mainpnl">
|
||||||
|
<a tabindex="-1" href="https://play.google.com/store/apps/details?id=com.blackforestbytes.simplecloudnotifier" class="button bordered" id="tl_link"><span class="icn-google-play"></span></a>
|
||||||
|
<a tabindex="-1" href="/index.php" class="button bordered" id="tr_link">Send</a>
|
||||||
|
|
||||||
|
<a tabindex="-1" href="/" class="linkcaption"><h1>Simple Cloud Notifier</h1></a>
|
||||||
|
|
||||||
<p>Get your user-id and user-key from the app and send notifications to your phone by performing a POST request against <code>https://simplecloudnotifier.blackforestbytes.com/send.php</code></p>
|
<p>Get your user-id and user-key from the app and send notifications to your phone by performing a POST request against <code>https://simplecloudnotifier.blackforestbytes.com/send.php</code></p>
|
||||||
<pre>curl \
|
<pre>curl \
|
||||||
@@ -32,12 +50,8 @@
|
|||||||
--data "title={message_title}" \
|
--data "title={message_title}" \
|
||||||
https://scn.blackforestbytes.com/send.php</pre>
|
https://scn.blackforestbytes.com/send.php</pre>
|
||||||
|
|
||||||
<a href="/index_more.php" class="button bordered tertiary" style="float: right; min-width: 100px; text-align: center">More</a>
|
<a href="/api_more.php" class="button bordered tertiary" style="float: right; min-width: 100px; text-align: center">More</a>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="copyinfo">
|
|
||||||
<a href="https://www.blackforestbytes.com">© blackforestbytes</a>
|
|
||||||
</div>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
2
web/api/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
config.php
|
||||||
|
.verify_accesstoken
|
56
web/api/__config_example.php
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// insert your values here and rename to config.php
|
||||||
|
|
||||||
|
return
|
||||||
|
[
|
||||||
|
'global' =>
|
||||||
|
[
|
||||||
|
'prod' => true,
|
||||||
|
],
|
||||||
|
|
||||||
|
'database' =>
|
||||||
|
[
|
||||||
|
'host' => '?',
|
||||||
|
'database' => '?',
|
||||||
|
'user' => '?',
|
||||||
|
'password' => '?',
|
||||||
|
],
|
||||||
|
|
||||||
|
'firebase' =>
|
||||||
|
[
|
||||||
|
'type' => 'service_account',
|
||||||
|
'project_id' => '?',
|
||||||
|
'private_key_id' => '???',
|
||||||
|
'client_email' => '???.iam.gserviceaccount.com',
|
||||||
|
'client_id' => '???',
|
||||||
|
'auth_uri' => 'https://accounts.google.com/o/oauth2/auth',
|
||||||
|
'token_uri' => 'https://oauth2.googleapis.com/token',
|
||||||
|
'auth_provider_x509_cert_url' => 'https://www.googleapis.com/oauth2/v1/certs',
|
||||||
|
'client_x509_cert_url' => 'https://www.googleapis.com/robot/v1/metadata/x509/???f.iam.gserviceaccount.com',
|
||||||
|
'private_key' => "-----BEGIN PRIVATE KEY-----\n"
|
||||||
|
. "??????????\n"
|
||||||
|
. "-----END PRIVATE KEY-----\n",
|
||||||
|
'server_key' => '????',
|
||||||
|
],
|
||||||
|
|
||||||
|
'verify_api' =>
|
||||||
|
[
|
||||||
|
'package_name' => 'com.blackforestbytes.simplecloudnotifier',
|
||||||
|
'product_id' => '???',
|
||||||
|
|
||||||
|
'clientid' => '???.apps.googleusercontent.com',
|
||||||
|
'clientsecret' => '???',
|
||||||
|
'accesstoken' => file_exists('.verify_accesstoken') ? file_get_contents('.verify_accesstoken') : '',
|
||||||
|
'refreshtoken' => '???',
|
||||||
|
'scope' => 'https://www.googleapis.com/auth/androidpublisher',
|
||||||
|
],
|
||||||
|
|
||||||
|
'error_reporting' =>
|
||||||
|
[
|
||||||
|
'send-mail' => true,
|
||||||
|
'email-error-target' => '???@???.com',
|
||||||
|
'email-error-sender' => '???@???.com',
|
||||||
|
],
|
||||||
|
|
||||||
|
];
|
46
web/api/ack.php
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<?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 ack 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']));
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare('UPDATE messages SET ack=1 WHERE scn_message_id=:smid AND sender_user_id=:uid');
|
||||||
|
$stmt->execute(['smid' => $scn_msg_id, 'uid' => $user_id]);
|
||||||
|
|
||||||
|
api_return(200,
|
||||||
|
[
|
||||||
|
'success' => true,
|
||||||
|
'prev_ack' => $datas[0]['ack'],
|
||||||
|
'new_ack' => 1,
|
||||||
|
'message' => 'ok'
|
||||||
|
]);
|
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' => strtotime($msg['timestamp']),
|
||||||
|
'usr_msg_id' => $msg['usr_message_id'],
|
||||||
|
'scn_msg_id' => $msg['scn_message_id'],
|
||||||
|
],
|
||||||
|
'message' => 'ok'
|
||||||
|
]);
|
@@ -15,7 +15,7 @@ $user_key = $INPUT['user_key'];
|
|||||||
|
|
||||||
$pdo = getDatabase();
|
$pdo = getDatabase();
|
||||||
|
|
||||||
$stmt = $pdo->prepare('SELECT user_id, user_key, quota_today, is_pro, quota_day FROM users WHERE user_id = :uid LIMIT 1');
|
$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]);
|
$stmt->execute(['uid' => $user_id]);
|
||||||
|
|
||||||
$datas = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
$datas = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
@@ -26,18 +26,26 @@ if ($data === null) die(json_encode(['success' => false, 'errid'=>202, 'message'
|
|||||||
if ($data['user_id'] !== (int)$user_id) die(json_encode(['success' => false, 'errid'=>203, 'message' => 'UserID 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']));
|
if ($data['user_key'] !== $user_key) die(json_encode(['success' => false, 'errid'=>204, 'message' => 'Authentification failed']));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare('SELECT COUNT(*) FROM messages WHERE ack=0 AND sender_user_id=:uid');
|
||||||
|
$stmt->execute(['uid' => $user_id]);
|
||||||
|
$nack_count = $stmt->fetch(PDO::FETCH_NUM)[0];
|
||||||
|
|
||||||
|
|
||||||
$quota = $data['quota_today'];
|
$quota = $data['quota_today'];
|
||||||
$is_pro = $data['is_pro'];
|
$is_pro = $data['is_pro'];
|
||||||
|
|
||||||
if ($data['quota_day'] === null || $data['quota_day'] !== date("Y-m-d")) $quota=0;
|
if ($data['quota_day'] === null || $data['quota_day'] !== date("Y-m-d")) $quota=0;
|
||||||
|
|
||||||
echo json_encode(
|
api_return(200,
|
||||||
[
|
[
|
||||||
'success' => true,
|
'success' => true,
|
||||||
'user_id' => $user_id,
|
'message' => 'ok',
|
||||||
'quota' => $quota,
|
'user_id' => $user_id,
|
||||||
'quota_max' => Statics::quota_max($is_pro),
|
'quota' => $quota,
|
||||||
'is_pro' => $is_pro,
|
'quota_max' => Statics::quota_max($is_pro),
|
||||||
'message' => 'ok'
|
'is_pro' => $is_pro,
|
||||||
|
'fcm_token_set' => ($data['fcm_token'] != null),
|
||||||
|
'unack_count' => $nack_count,
|
||||||
]);
|
]);
|
||||||
return 0;
|
|
@@ -2,12 +2,47 @@
|
|||||||
|
|
||||||
include('lib/httpful.phar');
|
include('lib/httpful.phar');
|
||||||
|
|
||||||
|
class ERR
|
||||||
|
{
|
||||||
|
const NO_ERROR = 0000;
|
||||||
|
|
||||||
|
const MISSING_UID = 1101;
|
||||||
|
const MISSING_TOK = 1102;
|
||||||
|
const MISSING_TITLE = 1103;
|
||||||
|
const INVALID_PRIO = 1104;
|
||||||
|
const REQ_METHOD = 1105;
|
||||||
|
|
||||||
|
const NO_TITLE = 1201;
|
||||||
|
const TITLE_TOO_LONG = 1202;
|
||||||
|
const CONTENT_TOO_LONG = 1203;
|
||||||
|
const USR_MSG_ID_TOO_LONG = 1204;
|
||||||
|
|
||||||
|
const USER_NOT_FOUND = 1301;
|
||||||
|
const USER_AUTH_FAILED = 1302;
|
||||||
|
|
||||||
|
const NO_DEVICE_LINKED = 1401;
|
||||||
|
|
||||||
|
const QUOTA_REACHED = 2101;
|
||||||
|
|
||||||
|
const FIREBASE_COM_FAILED = 9901;
|
||||||
|
const FIREBASE_COM_ERRORED = 9902;
|
||||||
|
const INTERNAL_EXCEPTION = 9903;
|
||||||
|
}
|
||||||
|
|
||||||
class Statics
|
class Statics
|
||||||
{
|
{
|
||||||
public static $DB = NULL;
|
public static $DB = NULL;
|
||||||
public static $CFG = NULL;
|
public static $CFG = NULL;
|
||||||
|
|
||||||
public static function quota_max($is_pro) { return $is_pro ? 1000 : 100; }
|
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()
|
||||||
@@ -123,7 +158,7 @@ function sendPOST($url, $body, $header)
|
|||||||
|
|
||||||
if ($response->code != 200) throw new Exception("Repsponse code: " . $response->code);
|
if ($response->code != 200) throw new Exception("Repsponse code: " . $response->code);
|
||||||
|
|
||||||
return $response->body;
|
return $response->raw_body;
|
||||||
}
|
}
|
||||||
|
|
||||||
function verifyOrderToken($tok)
|
function verifyOrderToken($tok)
|
||||||
@@ -136,26 +171,25 @@ function verifyOrderToken($tok)
|
|||||||
$product = getConfig()['verify_api']['product_id'];
|
$product = getConfig()['verify_api']['product_id'];
|
||||||
$acctoken = getConfig()['verify_api']['accesstoken'];
|
$acctoken = getConfig()['verify_api']['accesstoken'];
|
||||||
|
|
||||||
if ($acctoken == '') $acctoken = refreshVerifyToken();
|
if ($acctoken == '' || $acctoken == null || $acctoken == false) $acctoken = refreshVerifyToken();
|
||||||
|
|
||||||
$url = 'https://www.googleapis.com/androidpublisher/v3/applications/'.$package.'/purchases/products/'.$product.'/tokens/'.$tok.'?access_token='.$acctoken;
|
$url = 'https://www.googleapis.com/androidpublisher/v3/applications/'.$package.'/purchases/products/'.$product.'/tokens/'.$tok.'?access_token='.$acctoken;
|
||||||
|
$response = $builder = \Httpful\Request::get($url)->send();
|
||||||
|
$obj = json_decode($response->raw_body, true);
|
||||||
|
|
||||||
$json = sendPOST($url, "", []);
|
if ($response->code != 401 && ($obj === null || $obj === false))
|
||||||
$obj = json_decode($json);
|
|
||||||
|
|
||||||
if ($obj === null || $obj === false)
|
|
||||||
{
|
{
|
||||||
reportError('verify-token returned NULL');
|
reportError('verify-token returned NULL');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($obj['error']) && isset($obj['error']['code']) && $obj['error']['code'] == 401) // "Invalid Credentials" -- refresh acces_token
|
if ($response->code == 401 || isset($obj['error']) && isset($obj['error']['code']) && $obj['error']['code'] == 401) // "Invalid Credentials" -- refresh acces_token
|
||||||
{
|
{
|
||||||
$acctoken = refreshVerifyToken();
|
$acctoken = refreshVerifyToken();
|
||||||
|
|
||||||
$url = 'https://www.googleapis.com/androidpublisher/v3/applications/'.$package.'/purchases/products/'.$product.'/tokens/'.$tok.'?access_token='.$acctoken;
|
$url = 'https://www.googleapis.com/androidpublisher/v3/applications/'.$package.'/purchases/products/'.$product.'/tokens/'.$tok.'?access_token='.$acctoken;
|
||||||
$json = sendPOST($url, "", []);
|
$response = $builder = \Httpful\Request::get($url)->send();
|
||||||
$obj = json_decode($json);
|
$obj = json_decode($response->raw_body, true);
|
||||||
|
|
||||||
if ($obj === null || $obj === false)
|
if ($obj === null || $obj === false)
|
||||||
{
|
{
|
||||||
@@ -185,28 +219,34 @@ function refreshVerifyToken()
|
|||||||
'&client_secret='.getConfig()['verify_api']['clientsecret'];
|
'&client_secret='.getConfig()['verify_api']['clientsecret'];
|
||||||
|
|
||||||
$json = sendPOST($url, "", []);
|
$json = sendPOST($url, "", []);
|
||||||
$obj = json_decode($json);
|
$obj = json_decode($json, true);
|
||||||
file_put_contents('.verify_accesstoken', $obj['access_token']);
|
file_put_contents('.verify_accesstoken', $obj['access_token']);
|
||||||
|
|
||||||
return $obj->access_token;
|
return $obj['access_token'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $http_code
|
||||||
|
* @param array $message
|
||||||
|
*/
|
||||||
function api_return($http_code, $message)
|
function api_return($http_code, $message)
|
||||||
{
|
{
|
||||||
http_response_code($http_code);
|
http_response_code($http_code);
|
||||||
echo $message;
|
header('Content-Type: application/json');
|
||||||
|
echo json_encode($message);
|
||||||
die();
|
die();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param String $str
|
* @param String $str
|
||||||
* @param String[] $path
|
* @param String[] $path
|
||||||
|
* @return mixed|null
|
||||||
*/
|
*/
|
||||||
function try_json($str, $path)
|
function try_json($str, $path)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
$o = json_decode($str);
|
$o = json_decode($str, true);
|
||||||
foreach ($path as $p) $o = $o[$p];
|
foreach ($path as $p) $o = $o[$p];
|
||||||
return $o;
|
return $o;
|
||||||
}
|
}
|
||||||
@@ -215,3 +255,15 @@ function try_json($str, $path)
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//#################################################################################################################
|
||||||
|
|
||||||
|
if (getConfig()['global']['prod']) {
|
||||||
|
ini_set('display_errors', 0);
|
||||||
|
ini_set('log_errors', 1);
|
||||||
|
} else {
|
||||||
|
error_reporting(E_STRICT);
|
||||||
|
ini_set('display_errors', 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
//#################################################################################################################
|
@@ -15,16 +15,30 @@ $user_key = generateRandomAuthKey();
|
|||||||
|
|
||||||
$pdo = getDatabase();
|
$pdo = getDatabase();
|
||||||
|
|
||||||
|
$pdo->beginTransaction();
|
||||||
|
|
||||||
if ($ispro)
|
if ($ispro)
|
||||||
{
|
{
|
||||||
if (!verifyOrderToken($pro_token)) die(json_encode(['success' => false, 'message' => 'Purchase token could not be verified']));
|
if (!verifyOrderToken($pro_token))
|
||||||
|
{
|
||||||
|
$pdo->rollBack();
|
||||||
|
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, 'spro' => $ispro ? $pro_token : null]);
|
||||||
$user_id = $pdo->lastInsertId('user_id');
|
$user_id = $pdo->lastInsertId('user_id');
|
||||||
|
|
||||||
echo json_encode(
|
$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]);
|
||||||
|
|
||||||
|
$pdo->commit();
|
||||||
|
|
||||||
|
api_return(200,
|
||||||
[
|
[
|
||||||
'success' => true,
|
'success' => true,
|
||||||
'user_id' => $user_id,
|
'user_id' => $user_id,
|
||||||
@@ -34,5 +48,3 @@ echo json_encode(
|
|||||||
'is_pro' => $ispro,
|
'is_pro' => $ispro,
|
||||||
'message' => 'New user registered'
|
'message' => 'New user registered'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return 0;
|
|
56
web/api/requery.php
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
<?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]]']));
|
||||||
|
|
||||||
|
$user_id = $INPUT['user_id'];
|
||||||
|
$user_key = $INPUT['user_key'];
|
||||||
|
|
||||||
|
//----------------------
|
||||||
|
|
||||||
|
$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 ack=0 AND sender_user_id=:uid ORDER BY `timestamp` DESC LIMIT 16');
|
||||||
|
$stmt->execute(['uid' => $user_id]);
|
||||||
|
$nonacks_sql = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
$nonacks = [];
|
||||||
|
|
||||||
|
foreach ($nonacks_sql as $nack)
|
||||||
|
{
|
||||||
|
$nonacks []=
|
||||||
|
[
|
||||||
|
'title' => $nack['title'],
|
||||||
|
'body' => $nack['content'],
|
||||||
|
'priority' => $nack['priority'],
|
||||||
|
'timestamp' => strtotime($nack['timestamp']),
|
||||||
|
'usr_msg_id' => $nack['usr_message_id'],
|
||||||
|
'scn_msg_id' => $nack['scn_message_id'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
api_return(200,
|
||||||
|
[
|
||||||
|
'success' => true,
|
||||||
|
'message' => 'ok',
|
||||||
|
'count' => count($nonacks),
|
||||||
|
'data' => $nonacks,
|
||||||
|
]);
|
@@ -1,3 +1,4 @@
|
|||||||
|
DROP TABLE IF EXISTS `users`;
|
||||||
CREATE TABLE `users`
|
CREATE TABLE `users`
|
||||||
(
|
(
|
||||||
`user_id` INT(11) NOT NULL AUTO_INCREMENT,
|
`user_id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||||
@@ -16,19 +17,21 @@ CREATE TABLE `users`
|
|||||||
PRIMARY KEY (`user_id`)
|
PRIMARY KEY (`user_id`)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `messages`;
|
||||||
CREATE TABLE `messages`
|
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` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
`ack` BIT 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,
|
||||||
|
|
||||||
`fcn_message_id` VARCHAR(256) NOT NULL,
|
`fcm_message_id` VARCHAR(256) NULL,
|
||||||
`usr_message_id` VARCHAR(256) NULL,
|
`usr_message_id` VARCHAR(256) NULL,
|
||||||
|
|
||||||
PRIMARY KEY (`scn_message_id`)
|
PRIMARY KEY (`scn_message_id`)
|
||||||
)
|
);
|
@@ -34,10 +34,12 @@ $new_userkey = generateRandomAuthKey();
|
|||||||
|
|
||||||
if ($fcm_token === null)
|
if ($fcm_token === null)
|
||||||
{
|
{
|
||||||
|
// only gen new user_secret
|
||||||
|
|
||||||
$stmt = $pdo->prepare('UPDATE users SET timestamp_accessed=NOW(), user_key=:at WHERE user_id = :uid');
|
$stmt = $pdo->prepare('UPDATE users SET timestamp_accessed=NOW(), user_key=:at WHERE user_id = :uid');
|
||||||
$stmt->execute(['uid' => $user_id, 'at' => $new_userkey]);
|
$stmt->execute(['uid' => $user_id, 'at' => $new_userkey]);
|
||||||
|
|
||||||
echo json_encode(
|
api_return(200,
|
||||||
[
|
[
|
||||||
'success' => true,
|
'success' => true,
|
||||||
'user_id' => $user_id,
|
'user_id' => $user_id,
|
||||||
@@ -47,14 +49,18 @@ if ($fcm_token === null)
|
|||||||
'is_pro' => $is_pro,
|
'is_pro' => $is_pro,
|
||||||
'message' => 'user updated'
|
'message' => 'user updated'
|
||||||
]);
|
]);
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// update fcm and gen new user_secret
|
||||||
|
|
||||||
$stmt = $pdo->prepare('UPDATE users SET timestamp_accessed=NOW(), fcm_token=:ft, user_key=:at WHERE user_id = :uid');
|
$stmt = $pdo->prepare('UPDATE users SET timestamp_accessed=NOW(), fcm_token=:ft, user_key=:at WHERE user_id = :uid');
|
||||||
$stmt->execute(['uid' => $user_id, 'ft' => $fcm_token, 'at' => $new_userkey]);
|
$stmt->execute(['uid' => $user_id, 'ft' => $fcm_token, 'at' => $new_userkey]);
|
||||||
|
|
||||||
echo json_encode(
|
$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]);
|
||||||
|
|
||||||
|
api_return(200,
|
||||||
[
|
[
|
||||||
'success' => true,
|
'success' => true,
|
||||||
'user_id' => $user_id,
|
'user_id' => $user_id,
|
||||||
@@ -64,5 +70,4 @@ else
|
|||||||
'is_pro' => $is_pro,
|
'is_pro' => $is_pro,
|
||||||
'message' => 'user updated'
|
'message' => 'user updated'
|
||||||
]);
|
]);
|
||||||
return 0;
|
|
||||||
}
|
}
|
@@ -45,17 +45,15 @@ if ($ispro)
|
|||||||
$stmt = $pdo->prepare('UPDATE users SET is_pro=0, pro_token=NULL WHERE user_id <> :uid AND pro_token = :ptk');
|
$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->execute(['uid' => $user_id, 'ptk' => $pro_token]);
|
||||||
|
|
||||||
echo json_encode(
|
api_return(200,
|
||||||
[
|
[
|
||||||
'success' => true,
|
'success' => true,
|
||||||
'user_id' => $user_id,
|
'user_id' => $user_id,
|
||||||
'user_key' => $new_userkey,
|
'quota' => $data['quota_today'],
|
||||||
'quota' => $data['quota'],
|
'quota_max'=> Statics::quota_max(true),
|
||||||
'quota_max'=> Statics::quota_max(true),
|
'is_pro' => true,
|
||||||
'is_pro' => true,
|
'message' => 'user updated'
|
||||||
'message' => 'user updated'
|
]);
|
||||||
]);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -64,15 +62,13 @@ else
|
|||||||
$stmt = $pdo->prepare('UPDATE users SET timestamp_accessed=NOW(), is_pro=0, pro_token=NULL WHERE user_id = :uid');
|
$stmt = $pdo->prepare('UPDATE users SET timestamp_accessed=NOW(), is_pro=0, pro_token=NULL WHERE user_id = :uid');
|
||||||
$stmt->execute(['uid' => $user_id]);
|
$stmt->execute(['uid' => $user_id]);
|
||||||
|
|
||||||
echo json_encode(
|
api_return(200,
|
||||||
[
|
[
|
||||||
'success' => true,
|
'success' => true,
|
||||||
'user_id' => $user_id,
|
'user_id' => $user_id,
|
||||||
'user_key' => $new_userkey,
|
'quota' => $data['quota_today'],
|
||||||
'quota' => $data['quota'],
|
'quota_max'=> Statics::quota_max(false),
|
||||||
'quota_max'=> Statics::quota_max(false),
|
'is_pro' => false,
|
||||||
'is_pro' => false,
|
'message' => 'user updated'
|
||||||
'message' => 'user updated'
|
]);
|
||||||
]);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
292
web/api_more.php
Normal file
@@ -0,0 +1,292 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<?php
|
||||||
|
if (file_exists('/var/www/openwebanalytics/owa_php.php'))
|
||||||
|
{
|
||||||
|
require_once('/var/www/openwebanalytics/owa_php.php');
|
||||||
|
$owa = new owa_php();
|
||||||
|
$owa->setSiteId('6386b0efc00d2e84ef642525345e1207');
|
||||||
|
$owa->setPageTitle('API (Long)');
|
||||||
|
$owa->trackPageView();
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<link rel="stylesheet" href="/css/mini-default.min.css"> <!-- https://minicss.org/docs -->
|
||||||
|
<title>Simple Cloud Notifications - API</title>
|
||||||
|
<!--<link rel="stylesheet" href="/css/mini-nord.min.css">-->
|
||||||
|
<!--<link rel="stylesheet" href="/css/mini-dark.min.css">-->
|
||||||
|
<link rel="stylesheet" href="/css/style.css">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="icon" type="image/png" href="/favicon.png"/>
|
||||||
|
<link rel="icon" type="image/png" href="/favicon.ico"/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div id="copyinfo">
|
||||||
|
<a tabindex="-1" href="https://www.blackforestbytes.com">© blackforestbytes</a>
|
||||||
|
<a tabindex="-1" href="https://www.mikescher.com">made by Mike Schwörer</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="mainpnl">
|
||||||
|
<a tabindex="-1" href="https://play.google.com/store/apps/details?id=com.blackforestbytes.simplecloudnotifier" class="button bordered" id="tl_link"><span class="icn-google-play"></span></a>
|
||||||
|
<a tabindex="-1" href="/index.php" class="button bordered" id="tr_link">Send</a>
|
||||||
|
|
||||||
|
<a tabindex="-1" href="/" class="linkcaption"><h1>Simple Cloud Notifier</h1></a>
|
||||||
|
|
||||||
|
<h2>Introduction</h2>
|
||||||
|
<div class="section">
|
||||||
|
<p>
|
||||||
|
With this API you can send push notifications to your phone.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
To recieve them you will need to install the <a href="https://play.google.com/store/apps/details?id=com.blackforestbytes.simplecloudnotifier">SimpleCloudNotifier</a> app from the play store.
|
||||||
|
When you open the app you can click on the account tab to see you unique <code>user_id</code> and <code>user_key</code>.
|
||||||
|
These two values are used to identify and authenticate your device so that send messages can be routed to your phone.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
You can at any time generate a new <code>user_key</code> in the app and invalidate the old one.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
There is also a <a href="/index.php">web interface</a> for this API to manually send notifications to your phone or to test your setup.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>Quota</h2>
|
||||||
|
<div class="section">
|
||||||
|
<p>
|
||||||
|
By default you can send up to 100 messages per day per device.
|
||||||
|
If you need more you can upgrade your account in the app to get 1000 messages per day, this has the additional benefit of removing ads and supporting the development of the app (and making sure I can pay the server costs).
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>API Requests</h2>
|
||||||
|
<div class="section">
|
||||||
|
<p>
|
||||||
|
To send a new notification you send a <code>POST</code> request to the URL <code>https://scn.blackforestbytes.com/send.php</code>.
|
||||||
|
All Parameters can either directly be submitted as URL parameters or they can be put into the POST body.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
You <i>need</i> to supply a valid <code>user_id</code> - <code>user_key</code> pair and a <code>title</code> for your message, all other parameter are optional.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>API Response</h2>
|
||||||
|
<div class="section">
|
||||||
|
<p>
|
||||||
|
If the operation was successful the API will respond with an HTTP statuscode 200 and an JSON payload indicating the send message and your remaining quota
|
||||||
|
</p>
|
||||||
|
<pre class="red-code">{
|
||||||
|
"success":true,
|
||||||
|
"message":"Message sent",
|
||||||
|
"response":
|
||||||
|
{
|
||||||
|
"multicast_id":8000000000000000006,
|
||||||
|
"success":1,
|
||||||
|
"failure":0,
|
||||||
|
"canonical_ids":0,
|
||||||
|
"results": [{"message_id":"0:10000000000000000000000000000000d"}]
|
||||||
|
},
|
||||||
|
"quota":17,
|
||||||
|
"quota_max":100
|
||||||
|
}</pre>
|
||||||
|
<p>
|
||||||
|
If the operation is <b>not</b> successful the API will respond with an 4xx HTTP statuscode.
|
||||||
|
</p>
|
||||||
|
<table class="scode_table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Statuscode</th>
|
||||||
|
<th>Explanation</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td data-label="Statuscode">200 (OK)</td>
|
||||||
|
<td data-label="Explanation">Message sent</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td data-label="Statuscode">400 (Bad Request)</td>
|
||||||
|
<td data-label="Explanation">The request is invalid (missing parameters or wrong values)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td data-label="Statuscode">401 (Unauthorized)</td>
|
||||||
|
<td data-label="Explanation">The user_id was not found or the user_key is wrong</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td data-label="Statuscode">403 (Forbidden)</td>
|
||||||
|
<td data-label="Explanation">The user has exceeded its daily quota - wait 24 hours or upgrade your account</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td data-label="Statuscode">412 (Precondition Failed)</td>
|
||||||
|
<td data-label="Explanation">There is no device connected with this account - open the app and press the refresh button in the account tab</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td data-label="Statuscode">500 (Internal Server Error)</td>
|
||||||
|
<td data-label="Explanation">There was an internal error while sending your data - try again later</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<p>
|
||||||
|
There is also always a JSON payload with additional information.
|
||||||
|
The <code>success</code> field is always there and in the error state you the <code>message</code> field to get a descritpion of the problem.
|
||||||
|
</p>
|
||||||
|
<pre class="red-code">{
|
||||||
|
"success":false,
|
||||||
|
"error":2101,
|
||||||
|
"errhighlight":-1,
|
||||||
|
"message":"Daily quota reached (100)"
|
||||||
|
}</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>Message Content</h2>
|
||||||
|
<div class="section">
|
||||||
|
<p>
|
||||||
|
Every message must have a title set.
|
||||||
|
But you also (optionally) add more content, while the title has a max length of 120 characters, the conntent can be up to 10.000 characters.
|
||||||
|
You can see the whole message with title and content in the app or when clicking on the notification.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
If needed the content can be supplied in the <code>content</code> parameter.
|
||||||
|
</p>
|
||||||
|
<pre>curl \
|
||||||
|
--data "user_id={userid}" \
|
||||||
|
--data "user_key={userkey}" \
|
||||||
|
--data "title={message_title}" \
|
||||||
|
--data "content={message_content}" \
|
||||||
|
https://scn.blackforestbytes.com/send.php</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>Message Priority</h2>
|
||||||
|
<div class="section">
|
||||||
|
<p>
|
||||||
|
Currently you can send a message with three different priorities: 0 (low), 1 (normal) and 2 (high).
|
||||||
|
In the app you can then configure a different behaviour for different priorities, e.g. only playing a sound if the notification is high priority.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Priorites are either 0, 1 or 2 and are supplied in the <code>priority</code> parameter.
|
||||||
|
If no priority is supplied the message will get the default priority of 1.
|
||||||
|
</p>
|
||||||
|
<pre>curl \
|
||||||
|
--data "user_id={userid}" \
|
||||||
|
--data "user_key={userkey}" \
|
||||||
|
--data "title={message_title}" \
|
||||||
|
--data "priority={0|1|2}" \
|
||||||
|
https://scn.blackforestbytes.com/send.php</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>Message Uniqueness</h2>
|
||||||
|
<div class="section">
|
||||||
|
<p>
|
||||||
|
Sometimes your script can run in an environment with an unstable connection and you want to implement an automatic re-try mechanism to send a message again if the last try failed due to bad connectivity.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
To ensure that a message is only send once you can generate a unique id for your message (I would recommend a simple <code>uuidgen</code>).
|
||||||
|
If you send a message with an UUID that was already used in the near past the API still returns OK, but no new message is sent.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The message_id is optional - but if you want to use it you need to supply it via the <code>msg_id</code> parameter.
|
||||||
|
</p>
|
||||||
|
<pre>curl \
|
||||||
|
--data "user_id={userid}" \
|
||||||
|
--data "user_key={userkey}" \
|
||||||
|
--data "title={message_title}" \
|
||||||
|
--data "msg_id={message_id}" \
|
||||||
|
https://scn.blackforestbytes.com/send.php</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>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<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> <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>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
@@ -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
|
||||||
{
|
{
|
||||||
@@ -86,13 +91,10 @@ body
|
|||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
z-index: -999;
|
//z-index: -999;
|
||||||
}
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
#copyinfo a:hover
|
text-align: right;
|
||||||
{
|
|
||||||
font-family: "Courier New", monospace;
|
|
||||||
color: #00F;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#copyinfo a,
|
#copyinfo a,
|
||||||
@@ -102,6 +104,14 @@ body
|
|||||||
font-family: "Courier New", monospace;
|
font-family: "Courier New", monospace;
|
||||||
color: #AAA;
|
color: #AAA;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
display: block;
|
||||||
|
line-height: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#copyinfo a:hover
|
||||||
|
{
|
||||||
|
font-family: "Courier New", monospace;
|
||||||
|
color: #0288D1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#tr_link
|
#tr_link
|
||||||
@@ -193,6 +203,11 @@ input::-webkit-inner-spin-button {
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fullcenterflex .card
|
||||||
|
{
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
|
||||||
a.card,
|
a.card,
|
||||||
a.card:active,
|
a.card:active,
|
||||||
a.card:visited,
|
a.card:visited,
|
||||||
@@ -223,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;;
|
||||||
|
}
|
BIN
web/favicon.ico
Normal file
After Width: | Height: | Size: 97 KiB |
BIN
web/favicon.png
Normal file
After Width: | Height: | Size: 90 KiB |
@@ -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>
|
||||||
@@ -9,14 +19,22 @@
|
|||||||
<!--<link rel="stylesheet" href="/css/mini-dark.min.css">-->
|
<!--<link rel="stylesheet" href="/css/mini-dark.min.css">-->
|
||||||
<link rel="stylesheet" href="/css/style.css">
|
<link rel="stylesheet" href="/css/style.css">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="icon" type="image/png" href="/favicon.png"/>
|
||||||
|
<link rel="icon" type="image/png" href="/favicon.ico"/>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
|
<div id="copyinfo">
|
||||||
|
<a tabindex="-1" href="https://www.blackforestbytes.com">© blackforestbytes</a>
|
||||||
|
<a tabindex="-1" href="https://www.mikescher.com">made by Mike Schwörer</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
<form id="mainpnl">
|
<form id="mainpnl">
|
||||||
|
|
||||||
<a href="https://play.google.com/store/apps/details?id=com.blackforestbytes.simplecloudnotifier" class="button bordered" id="tl_link"><span class="icn-google-play"></span></a>
|
<a tabindex="-1" href="https://play.google.com/store/apps/details?id=com.blackforestbytes.simplecloudnotifier" class="button bordered" id="tl_link"><span class="icn-google-play"></span></a>
|
||||||
<a href="/index_api.php" class="button bordered" id="tr_link">API</a>
|
<a tabindex="-1" href="/api.php" class="button bordered" id="tr_link">API</a>
|
||||||
|
|
||||||
<a href="/" class="linkcaption"><h1>Simple Cloud Notifier</h1></a>
|
<a tabindex="-1" href="/" class="linkcaption"><h1>Simple Cloud Notifier</h1></a>
|
||||||
|
|
||||||
<div class="row responsive-label">
|
<div class="row responsive-label">
|
||||||
<div class="col-sm-12 col-md-3"><label for="uid" class="doc">UserID</label></div>
|
<div class="col-sm-12 col-md-3"><label for="uid" class="doc">UserID</label></div>
|
||||||
@@ -46,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">
|
||||||
@@ -55,10 +73,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div id="copyinfo">
|
|
||||||
<a href="https://www.blackforestbytes.com">© blackforestbytes</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="/js/logic.js" type="text/javascript" ></script>
|
<script src="/js/logic.js" type="text/javascript" ></script>
|
||||||
<script src="/js/toastify.js"></script>
|
<script src="/js/toastify.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
@@ -1,186 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<link rel="stylesheet" href="/css/mini-default.min.css"> <!-- https://minicss.org/docs -->
|
|
||||||
<title>Simple Cloud Notifications - API</title>
|
|
||||||
<!--<link rel="stylesheet" href="/css/mini-nord.min.css">-->
|
|
||||||
<!--<link rel="stylesheet" href="/css/mini-dark.min.css">-->
|
|
||||||
<link rel="stylesheet" href="/css/style.css">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="mainpnl">
|
|
||||||
<a href="https://play.google.com/store/apps/details?id=com.blackforestbytes.simplecloudnotifier" class="button bordered" id="tl_link"><span class="icn-google-play"></span></a>
|
|
||||||
<a href="/index.php" class="button bordered" id="tr_link">Send</a>
|
|
||||||
|
|
||||||
<a href="/" class="linkcaption"><h1>Simple Cloud Notifier</h1></a>
|
|
||||||
|
|
||||||
<h2>Introduction</h2>
|
|
||||||
<div class="section">
|
|
||||||
<p>
|
|
||||||
With this API you can send push notifications to your phone.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
To recieve them you will need to install the <a href="https://play.google.com/store/apps/details?id=com.blackforestbytes.simplecloudnotifier">SimpleCloudNotifier</a> app from the play store.
|
|
||||||
When you open the app you can click on the account tab to see you unique <code>user_id</code> and <code>user_key</code>.
|
|
||||||
These two values are used to identify and authenticate your device so that send messages can be routed to your phone.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
You can at any time generate a new <code>user_key</code> in the app and invalidate the old one.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
There is also a <a href="/index.php">web interface</a> for this API to manually send notifications to your phone or to test your setup.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2>Quota</h2>
|
|
||||||
<div class="section">
|
|
||||||
<p>
|
|
||||||
By default you can send up to 100 messages per day per device.
|
|
||||||
If you need more you can upgrade your account in the app to get 1000 messages per day, this has the additional benefit of removing ads and supporting the development of the app (and making sure I can pay the server costs).
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2>API Requests</h2>
|
|
||||||
<div class="section">
|
|
||||||
<p>
|
|
||||||
To send a new notification you send a <code>POST</code> request to the URL <code>https://scn.blackforestbytes.com/send.php</code>.
|
|
||||||
All Parameters can either directly be submitted as URL parameters or they can be put into the POST body.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
You <i>need</i> to supply a valid <code>user_id</code> - <code>user_key</code> pair and a <code>title</code> for your message, all other parameter are optional.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2>API Response</h2>
|
|
||||||
<div class="section">
|
|
||||||
<p>
|
|
||||||
If the operation was successful the API will respond with an HTTP statuscode 200 and an JSON payload indicating the send message and your remaining quota
|
|
||||||
</p>
|
|
||||||
<pre class="red-code">{
|
|
||||||
"success":true,
|
|
||||||
"message":"Message sent",
|
|
||||||
"response":
|
|
||||||
{
|
|
||||||
"multicast_id":8000000000000000006,
|
|
||||||
"success":1,
|
|
||||||
"failure":0,
|
|
||||||
"canonical_ids":0,
|
|
||||||
"results": [{"message_id":"0:10000000000000000000000000000000d"}]
|
|
||||||
},
|
|
||||||
"messagecount":623,
|
|
||||||
"quota":17,
|
|
||||||
"quota_max":100
|
|
||||||
}</pre>
|
|
||||||
<p>
|
|
||||||
If the operation is <b>not</b> successful the API will respond with an 4xx HTTP statuscode.
|
|
||||||
</p>
|
|
||||||
<table class="scode_table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Statuscode</th>
|
|
||||||
<th>Explanation</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td data-label="Statuscode">200 (OK)</td>
|
|
||||||
<td data-label="Explanation">Message sent</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td data-label="Statuscode">400 (Bad Request)</td>
|
|
||||||
<td data-label="Explanation">The request is invalid (missing parameters or wrong values)</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td data-label="Statuscode">401 (Unauthorized)</td>
|
|
||||||
<td data-label="Explanation">The user_id was not found or the user_key is wrong</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td data-label="Statuscode">403 (Forbidden)</td>
|
|
||||||
<td data-label="Explanation">The user has exceeded its daily quota - wait 24 hours or upgrade your account</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td data-label="Statuscode">500 (Internal Server Error)</td>
|
|
||||||
<td data-label="Explanation">There was an internal error while sending your data - try again later</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<p>
|
|
||||||
There is also always a JSON payload with additional information.
|
|
||||||
The <code>success</code> field is always there and in the error state you the <code>message</code> field to get a descritpion of the problem.
|
|
||||||
</p>
|
|
||||||
<pre class="red-code">{
|
|
||||||
"success":false,
|
|
||||||
"error":2101,
|
|
||||||
"errhighlight":-1,
|
|
||||||
"message":"Daily quota reached (100)"
|
|
||||||
}</pre>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2>Message Content</h2>
|
|
||||||
<div class="section">
|
|
||||||
<p>
|
|
||||||
Every message must have a title set.
|
|
||||||
But you also (optionally) add more content, while the title has a max length of 120 characters, the conntent can be up to 10.000 characters.
|
|
||||||
You can see the whole message with title and content in the app or when clicking on the notification.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
If needed the content can be supplied in the <code>content</code> parameter.
|
|
||||||
</p>
|
|
||||||
<pre>curl \
|
|
||||||
--data "user_id={userid}" \
|
|
||||||
--data "user_key={userkey}" \
|
|
||||||
--data "title={message_title}" \
|
|
||||||
--data "content={message_content}" \
|
|
||||||
https://scn.blackforestbytes.com/send.php</pre>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2>Message Priority</h2>
|
|
||||||
<div class="section">
|
|
||||||
<p>
|
|
||||||
Currently you can send a message with three different priorities: 0 (low), 1 (normal) and 2 (high).
|
|
||||||
In the app you can then configure a different behaviour for different priorities, e.g. only playing a sound if the notification is high priority.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Priorites are either 0, 1 or 2 and are supplied in the <code>priority</code> parameter.
|
|
||||||
If no priority is supplied the message will get the default priority of 1.
|
|
||||||
</p>
|
|
||||||
<pre>curl \
|
|
||||||
--data "user_id={userid}" \
|
|
||||||
--data "user_key={userkey}" \
|
|
||||||
--data "title={message_title}" \
|
|
||||||
--data "priority={0|1|2}" \
|
|
||||||
https://scn.blackforestbytes.com/send.php</pre>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2>Message Uniqueness</h2>
|
|
||||||
<div class="section">
|
|
||||||
<p>
|
|
||||||
Sometimes your script can run in an environment with an unstable connection and you want to implement an automatic re-try mechanism to send a message again if the last try failed due to bad connectivity.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
To ensure that a message is only send once you can generate a unique id for your message (I would recommend a simple <code>uuidgen</code>).
|
|
||||||
If you send a message with an UUID that was already used in the near past the API still returns OK, but no new message is sent.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
The message_id is optional - but if you want to use it you need to supply it via the <code>msg_id</code> parameter.
|
|
||||||
</p>
|
|
||||||
<pre>curl \
|
|
||||||
--data "user_id={userid}" \
|
|
||||||
--data "user_key={userkey}" \
|
|
||||||
--data "title={message_title}" \
|
|
||||||
--data "msg_id={message_id}" \
|
|
||||||
https://scn.blackforestbytes.com/send.php</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 id="copyinfo">
|
|
||||||
<a href="https://www.blackforestbytes.com">© blackforestbytes</a>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@@ -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');
|
||||||
@@ -55,7 +55,7 @@ function send()
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
window.location.href =
|
window.location.href =
|
||||||
'/index_sent.php' +
|
'/message_sent.php' +
|
||||||
'?ok=' + 1 +
|
'?ok=' + 1 +
|
||||||
'&message_count=' + resp.messagecount +
|
'&message_count=' + resp.messagecount +
|
||||||
'"a=' + resp.quota +
|
'"a=' + resp.quota +
|
||||||
|
@@ -8,9 +8,15 @@
|
|||||||
<!--<link rel="stylesheet" href="/css/mini-dark.min.css">-->
|
<!--<link rel="stylesheet" href="/css/mini-dark.min.css">-->
|
||||||
<link rel="stylesheet" href="/css/style.css">
|
<link rel="stylesheet" href="/css/style.css">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="icon" type="image/png" href="/favicon.png"/>
|
||||||
|
<link rel="icon" type="image/png" href="/favicon.ico"/>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
|
<div id="copyinfo">
|
||||||
|
<a tabindex="-1" href="https://www.blackforestbytes.com">© blackforestbytes</a>
|
||||||
|
<a tabindex="-1" href="https://www.mikescher.com">made by Mike Schwörer</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="mainpnl">
|
<div id="mainpnl">
|
||||||
|
|
||||||
@@ -39,16 +45,12 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a href="https://play.google.com/store/apps/details?id=com.blackforestbytes.simplecloudnotifier" class="button bordered" id="tl_link"><span class="icn-google-play"></span></a>
|
<a tabindex="-1" href="https://play.google.com/store/apps/details?id=com.blackforestbytes.simplecloudnotifier" class="button bordered" id="tl_link"><span class="icn-google-play"></span></a>
|
||||||
<a href="/index.php" class="button bordered" id="tr_link">Send</a>
|
<a tabindex="-1" href="/index.php" class="button bordered" id="tr_link">Send</a>
|
||||||
|
|
||||||
<a href="/" class="linkcaption"><h1>Simple Cloud Notifier</h1></a>
|
<a tabindex="-1" href="/" class="linkcaption"><h1>Simple Cloud Notifier</h1></a>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="copyinfo">
|
|
||||||
<a href="https://www.blackforestbytes.com">© blackforestbytes</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
108
web/send.php
@@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
include_once 'model.php';
|
include_once 'api/model.php';
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -9,11 +9,13 @@ try
|
|||||||
//sleep(1);
|
//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)']);
|
||||||
|
|
||||||
$INPUT = array_merge($_GET, $_POST);
|
$INPUT = array_merge($_GET, $_POST);
|
||||||
|
|
||||||
if (!isset($INPUT['user_id'])) api_return(400, json_encode(['success' => false, 'error' => 1101, 'errhighlight' => 101, 'message' => 'Missing parameter [[user_id]]']));
|
if (!isset($INPUT['user_id'])) api_return(400, ['success' => false, 'error' => ERR::MISSING_UID, 'errhighlight' => 101, 'message' => 'Missing parameter [[user_id]]']);
|
||||||
if (!isset($INPUT['user_key'])) api_return(400, json_encode(['success' => false, 'error' => 1102, 'errhighlight' => 102, 'message' => 'Missing parameter [[user_token]]']));
|
if (!isset($INPUT['user_key'])) api_return(400, ['success' => false, 'error' => ERR::MISSING_TOK, 'errhighlight' => 102, 'message' => 'Missing parameter [[user_token]]']);
|
||||||
if (!isset($INPUT['title'])) api_return(400, json_encode(['success' => false, 'error' => 1103, 'errhighlight' => 103, 'message' => 'Missing parameter [[title]]']));
|
if (!isset($INPUT['title'])) api_return(400, ['success' => false, 'error' => ERR::MISSING_TITLE, 'errhighlight' => 103, 'message' => 'Missing parameter [[title]]']);
|
||||||
|
|
||||||
//------------------------------------------------------------------
|
//------------------------------------------------------------------
|
||||||
|
|
||||||
@@ -27,11 +29,10 @@ try
|
|||||||
|
|
||||||
//------------------------------------------------------------------
|
//------------------------------------------------------------------
|
||||||
|
|
||||||
if ($priority !== '0' && $priority !== '1' && $priority !== '2') api_return(400, json_encode(['success' => false, 'error' => 1104, '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, json_encode(['success' => false, 'error' => 1201, '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, json_encode(['success' => false, 'error' => 1202, 'errhighlight' => 103, 'message' => 'Title too long (120 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 (strlen($content) > 10000) api_return(400, json_encode(['success' => false, 'error' => 1203, 'errhighlight' => 104, 'message' => 'Content too long (10000 characters)']));
|
|
||||||
|
|
||||||
//------------------------------------------------------------------
|
//------------------------------------------------------------------
|
||||||
|
|
||||||
@@ -41,18 +42,30 @@ 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' => 1301, '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, json_encode(['success' => false, 'error' => 1301, '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, json_encode(['success' => false, 'error' => 1302, '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, json_encode(['success' => false, 'error' => 1303, '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;
|
||||||
if ($data['quota_day'] === null || $data['quota_day'] !== date("Y-m-d")) $new_quota=1;
|
if ($data['quota_day'] === null || $data['quota_day'] !== date("Y-m-d")) $new_quota=1;
|
||||||
if ($new_quota > Statics::quota_max($data['is_pro'])) api_return(403, json_encode(['success' => false, 'error' => 2101, 'errhighlight' => -1, 'message' => 'Daily quota reached ('.Statics::quota_max($data['is_pro']).')']));
|
if ($new_quota > Statics::quota_max($data['is_pro'])) api_return(403, ['success' => false, 'error' => ERR::QUOTA_REACHED, 'errhighlight' => -1, 'message' => 'Daily quota reached ('.Statics::quota_max($data['is_pro']).')']);
|
||||||
|
|
||||||
|
if ($fcm == null || $fcm == '' || $fcm == false)
|
||||||
|
{
|
||||||
|
api_return(412, ['success' => false, 'error' => ERR::NO_DEVICE_LINKED, 'errhighlight' => -1, 'message' => 'No device linked with this account']);
|
||||||
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------
|
//------------------------------------------------------------------
|
||||||
|
|
||||||
@@ -63,7 +76,7 @@ try
|
|||||||
|
|
||||||
if (count($stmt->fetchAll(PDO::FETCH_ASSOC))>0)
|
if (count($stmt->fetchAll(PDO::FETCH_ASSOC))>0)
|
||||||
{
|
{
|
||||||
api_return(200, json_encode(
|
api_return(200,
|
||||||
[
|
[
|
||||||
'success' => true,
|
'success' => true,
|
||||||
'message' => 'Message already sent',
|
'message' => 'Message already sent',
|
||||||
@@ -73,12 +86,28 @@ try
|
|||||||
'quota' => $data['quota_today'],
|
'quota' => $data['quota_today'],
|
||||||
'is_pro' => $data['is_pro'],
|
'is_pro' => $data['is_pro'],
|
||||||
'quota_max' => Statics::quota_max($data['is_pro']),
|
'quota_max' => Statics::quota_max($data['is_pro']),
|
||||||
]));
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------
|
//------------------------------------------------------------------
|
||||||
|
|
||||||
|
$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->execute(
|
||||||
|
[
|
||||||
|
'suid' => $user_id,
|
||||||
|
't' => $message,
|
||||||
|
'c' => $content,
|
||||||
|
'p' => $priority,
|
||||||
|
'fmid' => null,
|
||||||
|
'umid' => $usrmsgid,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$scn_msg_id = $pdo->lastInsertId();
|
||||||
|
|
||||||
$url = "https://fcm.googleapis.com/fcm/send";
|
$url = "https://fcm.googleapis.com/fcm/send";
|
||||||
$payload = json_encode(
|
$payload = json_encode(
|
||||||
[
|
[
|
||||||
@@ -91,13 +120,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(),
|
||||||
]
|
'usr_msg_id' => $usrmsgid,
|
||||||
|
'scn_msg_id' => $scn_msg_id,
|
||||||
|
]
|
||||||
]);
|
]);
|
||||||
$header=
|
$header=
|
||||||
[
|
[
|
||||||
@@ -112,32 +143,34 @@ try
|
|||||||
if (try_json($httpresult, ['success']) != 1)
|
if (try_json($httpresult, ['success']) != 1)
|
||||||
{
|
{
|
||||||
reportError("FCM communication failed (success_1 <> true)\n\n".$httpresult);
|
reportError("FCM communication failed (success_1 <> true)\n\n".$httpresult);
|
||||||
api_return(403, json_encode(['success' => false, 'error' => 9902, 'errhighlight' => -1, 'message' => 'Communication with firebase service failed.']));
|
$pdo->rollBack();
|
||||||
|
api_return(500, ['success' => false, 'error' => ERR::FIREBASE_COM_ERRORED, 'errhighlight' => -1, 'message' => 'Communication with firebase service failed.']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception $e)
|
catch (Exception $e)
|
||||||
{
|
{
|
||||||
reportError("FCM communication failed", $e);
|
reportError("FCM communication failed", $e);
|
||||||
api_return(403, json_encode(['success' => false, 'error' => 9901, 'errhighlight' => -1, 'message' => 'Communication with firebase service failed.'."\n\n".'Exception: ' . $e->getMessage()]));
|
$pdo->rollBack();
|
||||||
|
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]);
|
||||||
|
|
||||||
$stmt = $pdo->prepare('INSERT INTO messages (sender_user_id, title, content, priority, fcn_message_id, usr_message_id) VALUES (:suid, :t, :c, :p, :fmid, :umid)');
|
$stmt = $pdo->prepare('UPDATE messages SET fcm_message_id=:fmid WHERE scn_message_id=:smid');
|
||||||
$stmt->execute(
|
$stmt->execute([ 'fmid' => try_json($httpresult, ['results', 0, 'message_id']), 'smid' => $scn_msg_id ]);
|
||||||
[
|
|
||||||
'suid' => $user_id,
|
|
||||||
't' => $message,
|
|
||||||
'c' => $content,
|
|
||||||
'p' => $priority,
|
|
||||||
'fmid' => try_json($httpresult, ['results', 'message_id']),
|
|
||||||
'umid' => $usrmsgid,
|
|
||||||
]);
|
|
||||||
|
|
||||||
api_return(200, json_encode(
|
$pdo->commit();
|
||||||
|
|
||||||
|
//------------------------------------------------------------------
|
||||||
|
|
||||||
|
api_return(200,
|
||||||
[
|
[
|
||||||
'success' => true,
|
'success' => true,
|
||||||
|
'error' => ERR::NO_ERROR,
|
||||||
|
'errhighlight' => -1,
|
||||||
'message' => 'Message sent',
|
'message' => 'Message sent',
|
||||||
'suppress_send' => false,
|
'suppress_send' => false,
|
||||||
'response' => $httpresult,
|
'response' => $httpresult,
|
||||||
@@ -145,9 +178,12 @@ try
|
|||||||
'quota' => $new_quota,
|
'quota' => $new_quota,
|
||||||
'is_pro' => $data['is_pro'],
|
'is_pro' => $data['is_pro'],
|
||||||
'quota_max' => Statics::quota_max($data['is_pro']),
|
'quota_max' => Statics::quota_max($data['is_pro']),
|
||||||
]));
|
'scn_msg_id' => $scn_msg_id,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
catch (Exception $mex)
|
catch (Exception $mex)
|
||||||
{
|
{
|
||||||
reportError("Root try-catch triggered", $mex);
|
reportError("Root try-catch triggered", $mex);
|
||||||
|
if ($pdo->inTransaction()) $pdo->rollBack();
|
||||||
|
api_return(500, ['success' => false, 'error' => ERR::INTERNAL_EXCEPTION, 'errhighlight' => -1, 'message' => 'PHP script threw exception.'."\n\n".'Exception: ' . $e->getMessage()]);
|
||||||
}
|
}
|
||||||
|