18 Commits

Author SHA1 Message Date
66f82f26d5 channel bullshit 2018-11-12 18:25:41 +01:00
6cb2aa00fb intermed 2018-11-12 17:23:19 +01:00
96a236803e simplify settings layout 2018-11-12 16:29:43 +01:00
9d24044eb3 center pro message 2018-11-12 16:11:31 +01:00
90248bcb54 confirm-dialog 2018-11-12 16:04:59 +01:00
a3b2a1b14f only one account per fcm_token (2) 2018-11-12 16:01:06 +01:00
b21b159b95 fix user_key reset in promode 2018-11-12 15:58:59 +01:00
93ec261dc0 ack messages on recieve 2018-11-12 15:51:06 +01:00
ae246e9219 favicon 2018-11-12 15:28:07 +01:00
3f18fdd35a only one account per fcm_token 2018-11-12 15:22:21 +01:00
faf5207478 fixed wrong sql key 2018-11-12 15:11:26 +01:00
71f003dd66 fix error in send.php 2018-11-12 15:07:48 +01:00
3f85ab514e prevent purchase re-use 2018-11-12 14:57:54 +01:00
9eb5a6b1b9 fix NPE 2018-11-12 14:57:44 +01:00
8e26cd6078 fixed order verify 2018-11-12 14:49:09 +01:00
36b9263730 fix json_decode problems 2018-11-12 14:24:11 +01:00
3d29fecaec gracefully handle user_not_found 2018-11-12 14:23:50 +01:00
jenkins
92ac05f1e3 [Jenkins] Increment version 2018-11-12 13:06:38 +01:00
31 changed files with 647 additions and 473 deletions

View File

@@ -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

View File

@@ -116,6 +116,10 @@ public class SCNApp extends Application implements LifecycleObserver
[X] - perhaps response codes in api (?) [X] - perhaps response codes in api (?)
[X] - verify recieve
[ ] - Android O repeat sound
[ ] - test notification channels [ ] - test notification channels
[ ] - publish (+ HN post ?) [ ] - publish (+ HN post ?)

View File

@@ -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;

View File

@@ -38,14 +38,15 @@ public class CMessageList
String title = sharedPref.getString("message["+i+"].title", ""); String title = sharedPref.getString("message["+i+"].title", "");
String content = sharedPref.getString("message["+i+"].content", ""); String content = sharedPref.getString("message["+i+"].content", "");
PriorityEnum prio = PriorityEnum.parseAPI(sharedPref.getInt("message["+i+"].priority", 1)); PriorityEnum prio = PriorityEnum.parseAPI(sharedPref.getInt("message["+i+"].priority", 1));
long scnid = sharedPref.getLong("message["+i+"].scnid", 0);
Messages.add(new CMessage(time, title, content, prio)); Messages.add(new CMessage(scnid, time, title, content, prio));
} }
} }
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(() ->
{ {
@@ -58,11 +59,12 @@ public class CMessageList
while (Messages.size()>SCNSettings.inst().LocalCacheSize) Messages.remove(0); while (Messages.size()>SCNSettings.inst().LocalCacheSize) Messages.remove(0);
e.putInt("message_count", count+1); e.putInt( "message_count", count+1);
e.putLong("message["+count+"].timestamp", time); e.putLong( "message["+count+"].timestamp", time);
e.putString("message["+count+"].title", title); e.putString("message["+count+"].title", title);
e.putString("message["+count+"].content", content); e.putString("message["+count+"].content", content);
e.putInt("message["+count+"].priority", pe.ID); e.putInt( "message["+count+"].priority", pe.ID);
e.putLong( "message["+count+"].scnid", scnid);
e.apply(); e.apply();
@@ -77,7 +79,7 @@ public class CMessageList
if (!run) if (!run)
{ {
Messages.add(new CMessage(time, title, content, pe)); Messages.add(new CMessage(scnid, time, title, content, pe));
fullSave(); fullSave();
} }
@@ -109,10 +111,11 @@ public class CMessageList
for (int i = 0; i < Messages.size(); i++) for (int i = 0; i < Messages.size(); i++)
{ {
e.putLong("message["+i+"].timestamp", Messages.get(i).Timestamp); e.putLong( "message["+i+"].timestamp", Messages.get(i).Timestamp);
e.putString("message["+i+"].title", Messages.get(i).Title); e.putString("message["+i+"].title", Messages.get(i).Title);
e.putString("message["+i+"].content", Messages.get(i).Content); e.putString("message["+i+"].content", Messages.get(i).Content);
e.putInt("message["+i+"].priority", Messages.get(i).Priority.ID); e.putInt( "message["+i+"].priority", Messages.get(i).Priority.ID);
e.putLong( "message["+i+"].scnid", Messages.get(i).SCN_ID);
} }
e.apply(); e.apply();

View File

@@ -1,17 +1,29 @@
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 EnableLED;
public int LEDColor = Color.BLUE; public int LEDColor;
public boolean EnableVibration = false; public boolean EnableVibration;
public NotificationSettings(PriorityEnum p)
{
EnableSound = (p == PriorityEnum.HIGH);
SoundName = (p == PriorityEnum.HIGH) ? "Default" : "";
SoundSource = (p == PriorityEnum.HIGH) ? RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION).toString() : Uri.EMPTY.toString();
RepeatSound = false;
EnableLED = (p == PriorityEnum.HIGH) || (p == PriorityEnum.NORMAL);
LEDColor = Color.BLUE;
EnableVibration = (p == PriorityEnum.HIGH) || (p == PriorityEnum.NORMAL);
}
} }

View File

@@ -8,6 +8,7 @@ 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;
@@ -47,9 +48,9 @@ public class SCNSettings
public boolean Enabled = true; public boolean Enabled = true;
public int LocalCacheSize = 500; public int LocalCacheSize = 500;
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);
// ------------------------------------------------------------ // ------------------------------------------------------------
@@ -195,10 +196,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
{ {

View File

@@ -4,7 +4,9 @@ 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.string.Str;
import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import org.json.JSONTokener; import org.json.JSONTokener;
@@ -39,7 +41,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); });
} }
@@ -57,25 +59,25 @@ public class ServerCommunication
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,7 +89,7 @@ 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);
} }
} }
@@ -105,7 +107,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); });
} }
@@ -123,25 +125,25 @@ public class ServerCommunication
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,7 +155,7 @@ 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);
} }
} }
@@ -169,7 +171,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:update_2", e.toString());
SCNApp.showToast("Communication with server failed", 4000); SCNApp.showToast("Communication with server failed", 4000);
} }
@@ -185,21 +187,21 @@ public class ServerCommunication
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 +213,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 +229,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);
@@ -246,20 +248,40 @@ public class ServerCommunication
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();
} 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 +293,7 @@ public class ServerCommunication
} }
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);
} }
} }
@@ -289,7 +311,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);
} }
@@ -305,21 +327,20 @@ public class ServerCommunication
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 +355,49 @@ public class ServerCommunication
} }
} }
public static void ack(int id, String key, CMessage msg)
{
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) {
// ????
}
});
}
catch (Exception e)
{
Log.e("SC:ack", e.toString());
}
}
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 String json_str(JSONObject o, String key) throws JSONException
{
return o.getString(key);
}
} }

View File

@@ -8,6 +8,7 @@ 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,9 +37,9 @@ 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"));
CMessage msg = CMessageList.inst().add(time, title, content, prio); CMessage msg = CMessageList.inst().add(scn_id, time, title, content, prio);
if (SCNApp.isBackground()) if (SCNApp.isBackground())
{ {
@@ -48,6 +49,8 @@ public class FBMService extends FirebaseMessagingService
{ {
NotificationService.inst().showForeground(msg); NotificationService.inst().showForeground(msg);
} }
ServerCommunication.ack(SCNSettings.inst().user_id, SCNSettings.inst().user_key, msg);
} }
catch (Exception e) catch (Exception e)
{ {

View File

@@ -6,9 +6,14 @@ 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.media.AudioAttributes;
import android.media.AudioManager; import android.media.AudioManager;
import android.media.Ringtone;
import android.media.RingtoneManager;
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;
@@ -23,9 +28,7 @@ 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_ID = "CHAN_BFB_SCN_MESSAGES";
private final static String CHANNEL_ID_NORM = "CHAN_BFB_SCN_MESSAGES_NORM";
private final static String CHANNEL_ID_HIGH = "CHAN_BFB_SCN_MESSAGES_HIGH";
private final static Object _lock = new Object(); private final static Object _lock = new Object();
private static NotificationService _inst = null; private static NotificationService _inst = null;
@@ -51,81 +54,139 @@ 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); NotificationChannel channel = notifman.getNotificationChannel(CHANNEL_ID);
if (channelLow == null) notifman.createNotificationChannel(channelLow = new NotificationChannel(CHANNEL_ID_LOW, "Push notifications (low priority)", NotificationManager.IMPORTANCE_LOW)); if (channel == null)
NotificationChannel channelNorm = notifman.getNotificationChannel(CHANNEL_ID_NORM); {
if (channelNorm == null) notifman.createNotificationChannel(channelNorm = new NotificationChannel(CHANNEL_ID_NORM, "Push notifications (normal priority)", NotificationManager.IMPORTANCE_DEFAULT)); channel = new NotificationChannel(CHANNEL_ID, "Push notifications", NotificationManager.IMPORTANCE_DEFAULT);
NotificationChannel channelHigh = notifman.getNotificationChannel(CHANNEL_ID_HIGH); channel.setDescription("Push notifications from the server");
if (channelHigh == null) notifman.createNotificationChannel(channelHigh = new NotificationChannel(CHANNEL_ID_HIGH, "Push notifications (high priority)", NotificationManager.IMPORTANCE_HIGH)); channel.setSound(null, null);
channel.setVibrationPattern(null);
channelLow.setDescription("Messages from the API with priority set to low"); notifman.createNotificationChannel(channel);
channelLow.enableLights(SCNSettings.inst().PriorityLow.EnableLED); }
channelLow.setLightColor(SCNSettings.inst().PriorityLow.LEDColor);
channelLow.enableVibration(SCNSettings.inst().PriorityLow.EnableVibration);
channelLow.setVibrationPattern(new long[]{200});
channelNorm.setDescription("Messages from the API with priority set to normal");
channelNorm.enableLights(SCNSettings.inst().PriorityNorm.EnableLED);
channelNorm.setLightColor(SCNSettings.inst().PriorityNorm.LEDColor);
channelNorm.enableVibration(SCNSettings.inst().PriorityNorm.EnableVibration);
channelNorm.setVibrationPattern(new long[]{200});
channelHigh.setDescription("Messages from the API with priority set to high");
channelHigh.enableLights(SCNSettings.inst().PriorityHigh.EnableLED);
channelHigh.setLightColor(SCNSettings.inst().PriorityHigh.LEDColor);
channelHigh.enableVibration(SCNSettings.inst().PriorityHigh.EnableVibration);
channelHigh.setVibrationPattern(new long[]{200});
channelLow.setBypassDnd(true);
channelLow.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
} }
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;
}
if (ns.EnableSound && !ns.SoundSource.isEmpty())
{
Ringtone rt = RingtoneManager.getRingtone(SCNApp.getContext(), Uri.parse(ns.SoundSource));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) rt.setLooping(false);
rt.play();
new Thread(() -> { try { Thread.sleep(5*1000); } catch (InterruptedException e) { /* */ } rt.stop(); }).start();
}
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);
mBuilder.setSmallIcon(R.drawable.ic_bfb);
mBuilder.setContentTitle(msg.Title);
mBuilder.setContentText(msg.Content);
mBuilder.setShowWhen(true);
mBuilder.setWhen(msg.Timestamp);
mBuilder.setAutoCancel(true);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
{ {
if (msg.Priority == PriorityEnum.LOW) mBuilder.setPriority(NotificationCompat.PRIORITY_LOW); // old
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(ctxt, CHANNEL_ID);
mBuilder.setSmallIcon(R.drawable.ic_bfb);
mBuilder.setContentTitle(msg.Title);
mBuilder.setContentText(msg.Content);
mBuilder.setShowWhen(true);
mBuilder.setWhen(msg.Timestamp * 1000);
mBuilder.setAutoCancel(true);
if (msg.Priority == PriorityEnum.LOW) mBuilder.setPriority(NotificationCompat.PRIORITY_LOW);
if (msg.Priority == PriorityEnum.NORMAL) mBuilder.setPriority(NotificationCompat.PRIORITY_DEFAULT); if (msg.Priority == PriorityEnum.NORMAL) mBuilder.setPriority(NotificationCompat.PRIORITY_DEFAULT);
if (msg.Priority == PriorityEnum.HIGH) mBuilder.setPriority(NotificationCompat.PRIORITY_HIGH); if (msg.Priority == PriorityEnum.HIGH) mBuilder.setPriority(NotificationCompat.PRIORITY_HIGH);
if (ns.EnableVibration) mBuilder.setVibrate(new long[]{200}); if (ns.EnableVibration) mBuilder.setVibrate(new long[]{500});
if (ns.EnableLED) mBuilder.setLights(ns.LEDColor, 500, 500); if (ns.EnableLED) mBuilder.setLights(ns.LEDColor, 500, 500);
}
if (ns.EnableSound && !ns.SoundSource.isEmpty()) if (ns.EnableSound && !ns.SoundSource.isEmpty()) mBuilder.setSound(Uri.parse(ns.SoundSource), AudioManager.STREAM_NOTIFICATION);
Intent intent = new Intent(ctxt, MainActivity.class);
PendingIntent pi = PendingIntent.getActivity(ctxt, 0, intent, 0);
mBuilder.setContentIntent(pi);
NotificationManager mNotificationManager = (NotificationManager) ctxt.getSystemService(Context.NOTIFICATION_SERVICE);
Notification n = mBuilder.build();
if (ns.EnableSound && !ns.SoundSource.isEmpty() && ns.RepeatSound) n.flags |= Notification.FLAG_INSISTENT;
if (mNotificationManager != null) mNotificationManager.notify(0, n);
}
else
{ {
mBuilder.setSound(Uri.parse(ns.SoundSource), AudioManager.STREAM_ALARM); // new
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(ctxt, CHANNEL_ID);
mBuilder.setSmallIcon(R.drawable.ic_bfb);
mBuilder.setContentTitle(msg.Title);
mBuilder.setContentText(msg.Content);
mBuilder.setShowWhen(true);
mBuilder.setWhen(msg.Timestamp * 1000);
mBuilder.setAutoCancel(true);
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 intent = new Intent(ctxt, MainActivity.class);
PendingIntent pi = PendingIntent.getActivity(ctxt, 0, intent, 0);
mBuilder.setContentIntent(pi);
NotificationManager mNotificationManager = (NotificationManager) ctxt.getSystemService(Context.NOTIFICATION_SERVICE);
if (mNotificationManager == null) return;
Notification n = mBuilder.build();
n.flags |= Notification.FLAG_AUTO_CANCEL;
mNotificationManager.notify(0, n);
if (ns.EnableSound && !ns.SoundSource.isEmpty())
{
Ringtone rt = RingtoneManager.getRingtone(SCNApp.getContext(), Uri.parse(ns.SoundSource));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) rt.setLooping(false);
rt.play();
new Thread(() -> { try { Thread.sleep(5*1000); } catch (InterruptedException e) { /* */ } rt.stop(); }).start();
}
if (ns.EnableVibration)
{
Vibrator v = (Vibrator) SCNApp.getContext().getSystemService(Context.VIBRATOR_SERVICE);
v.vibrate(VibrationEffect.createOneShot(1500, VibrationEffect.DEFAULT_AMPLITUDE));
}
} }
Intent intent = new Intent(ctxt, MainActivity.class);
PendingIntent pi = PendingIntent.getActivity(ctxt, 0, intent, 0);
mBuilder.setContentIntent(pi);
NotificationManager mNotificationManager = (NotificationManager) ctxt.getSystemService(Context.NOTIFICATION_SERVICE);
Notification n = mBuilder.build();
if (ns.EnableSound && !ns.SoundSource.isEmpty() && ns.RepeatSound) n.flags |= Notification.FLAG_INSISTENT;
if (mNotificationManager != null) mNotificationManager.notify(0, n);
} }
} }

View File

@@ -1,8 +1,10 @@
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.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
@@ -21,6 +23,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,15 +58,47 @@ 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("Notifications 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 ->

View File

@@ -45,6 +45,6 @@ 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);
} }
} }

View File

@@ -201,7 +201,6 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
{ {
SCNSettings.inst().save(); SCNSettings.inst().save();
updateUI(); updateUI();
NotificationService.inst().updateChannels();
} }
private void onUpgradeAccount() private void onUpgradeAccount()
@@ -213,9 +212,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)

View File

@@ -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"
@@ -102,6 +92,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,23 +142,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: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_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"/>
android:minHeight="48dp">
<Switch
android:id="@+id/prefMsgLowEnableSound"
android:text="@string/str_msg_enablesound"
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"
@@ -176,39 +158,33 @@
android:layout_marginTop="2dp" android:layout_marginTop="2dp"
android:background="#c0c0c0"/> android:background="#c0c0c0"/>
<androidx.constraintlayout.widget.ConstraintLayout <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:orientation="vertical"
android:layout_marginStart="4dp" android:layout_marginStart="4dp"
android:layout_marginEnd="4dp" android:layout_marginEnd="4dp"
android:minHeight="48dp"> android:minHeight="48dp"
android:gravity="center">
<LinearLayout <TextView
android:id="@+id/prefMsgLowRingtone_container" android:id="@+id/tvMsgLowRingtone"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:text="@string/str_notificationsound"
app:layout_constraintBottom_toBottomOf="parent" android:textColor="#000" />
app:layout_constraintTop_toTopOf="parent">
<TextView <TextView
android:id="@+id/tvMsgLowRingtone" android:id="@+id/prefMsgLowRingtone_value"
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:minWidth="64dp"
android:textColor="#000" /> android:spinnerMode="dialog"
android:text="Whatever"
tools:ignore="HardcodedText" />
<TextView </LinearLayout>
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 <View
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -217,23 +193,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/prefMsgLowRepeatSound"
android:layout_marginEnd="4dp" 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:minHeight="48dp"> android:layout_marginStart="4dp"
android:layout_marginEnd="4dp"
<Switch android:minHeight="48dp" />
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 <View
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -242,23 +209,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/prefMsgLowEnableLED"
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"> android:layout_marginStart="4dp"
android:layout_marginEnd="4dp"
<Switch android:minHeight="48dp" />
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 +266,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,23 +299,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: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"
<Switch android:minHeight="48dp" />
android:id="@+id/prefMsgNormEnableSound"
android:text="@string/str_msg_enablesound"
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"
@@ -374,39 +315,32 @@
android:layout_marginTop="2dp" android:layout_marginTop="2dp"
android:background="#c0c0c0"/> android:background="#c0c0c0"/>
<androidx.constraintlayout.widget.ConstraintLayout <LinearLayout
android:id="@+id/prefMsgNormRingtone_container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginStart="4dp" android:layout_marginStart="4dp"
android:layout_marginEnd="4dp" android:layout_marginEnd="4dp"
android:minHeight="48dp"> android:minHeight="48dp"
android:gravity="center">
<LinearLayout <TextView
android:id="@+id/prefMsgNormRingtone_container" 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:orientation="vertical" android:text="@string/str_notificationsound"
app:layout_constraintBottom_toBottomOf="parent" android:textColor="#000" />
app:layout_constraintTop_toTopOf="parent">
<TextView <TextView
android:id="@+id/tvMsgNormRingtone" android:id="@+id/prefMsgNormRingtone_value"
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:minWidth="64dp"
android:textColor="#000" /> android:spinnerMode="dialog"
android:text="Whatever"
<TextView tools:ignore="HardcodedText" />
android:id="@+id/prefMsgNormRingtone_value" </LinearLayout>
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,23 +349,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/prefMsgNormRepeatSound"
android:layout_marginEnd="4dp" 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:minHeight="48dp"> android:layout_marginStart="4dp"
android:layout_marginEnd="4dp"
<Switch android:minHeight="48dp" />
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 <View
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -440,23 +365,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"> android:layout_marginStart="4dp"
android:layout_marginEnd="4dp"
<Switch android:minHeight="48dp" />
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 +422,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,23 +454,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: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"
<Switch android:minHeight="48dp" />
android:id="@+id/prefMsgHighEnableSound"
android:text="@string/str_msg_enablesound"
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"
@@ -572,39 +470,34 @@
android:layout_marginTop="2dp" android:layout_marginTop="2dp"
android:background="#c0c0c0"/> android:background="#c0c0c0"/>
<androidx.constraintlayout.widget.ConstraintLayout <LinearLayout
android:id="@+id/prefMsgHighRingtone_container"
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"
android:orientation="vertical"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout <TextView
android:id="@+id/prefMsgHighRingtone_container" 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:orientation="vertical" android:text="@string/str_notificationsound"
app:layout_constraintBottom_toBottomOf="parent" android:textColor="#000" />
app:layout_constraintTop_toTopOf="parent">
<TextView <TextView
android:id="@+id/tvMsgHighRingtone" android:id="@+id/prefMsgHighRingtone_value"
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:minWidth="64dp"
android:textColor="#000" /> android:spinnerMode="dialog"
android:text="Whatever"
<TextView tools:ignore="HardcodedText" />
android:id="@+id/prefMsgHighRingtone_value" </LinearLayout>
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,23 +506,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/prefMsgHighRepeatSound"
android:layout_marginEnd="4dp" 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:minHeight="48dp"> android:layout_marginStart="4dp"
android:layout_marginEnd="4dp"
<Switch android:minHeight="48dp" />
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 <View
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -638,23 +522,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"> android:layout_marginStart="4dp"
android:layout_marginEnd="4dp"
<Switch android:minHeight="48dp" />
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 +579,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>

View File

@@ -18,9 +18,9 @@
<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 notifications 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>

View File

@@ -1,3 +1,3 @@
#Sun Nov 11 19:37:57 CET 2018 #Mon Nov 12 13:06:09 CET 2018
VERSION_NAME=0.0.5 VERSION_NAME=0.0.6
VERSION_CODE=5 VERSION_CODE=6

BIN
data/icon_web.pdn Normal file

Binary file not shown.

47
web/ack.php Normal file
View File

@@ -0,0 +1,47 @@
<?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]);
echo json_encode(
[
'success' => true,
'prev_ack' => $datas[0]['ack'],
'new_ack' => 1,
'message' => 'ok'
]);
return 0;

View File

@@ -193,6 +193,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,

BIN
web/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

BIN
web/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

View File

@@ -9,6 +9,8 @@
<!--<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>
<form id="mainpnl"> <form id="mainpnl">

View File

@@ -8,6 +8,8 @@
<!--<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"> <div id="mainpnl">

View File

@@ -8,6 +8,8 @@
<!--<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"> <div id="mainpnl">
@@ -100,6 +102,10 @@
<td data-label="Statuscode">403 (Forbidden)</td> <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> <td data-label="Explanation">The user has exceeded its daily quota - wait 24 hours or upgrade your account</td>
</tr> </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> <tr>
<td data-label="Statuscode">500 (Internal Server Error)</td> <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> <td data-label="Explanation">There was an internal error while sending your data - try again later</td>

View File

@@ -8,6 +8,8 @@
<!--<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>

View File

@@ -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);
@@ -33,11 +33,12 @@ if ($data['quota_day'] === null || $data['quota_day'] !== date("Y-m-d")) $quota=
echo json_encode( echo json_encode(
[ [
'success' => true, 'success' => true,
'user_id' => $user_id, 'user_id' => $user_id,
'quota' => $quota, 'quota' => $quota,
'quota_max' => Statics::quota_max($is_pro), 'quota_max' => Statics::quota_max($is_pro),
'is_pro' => $is_pro, 'is_pro' => $is_pro,
'message' => 'ok' 'fcm_token_set' => ($data['fcm_token'] != null),
'message' => 'ok'
]); ]);
return 0; return 0;

View File

@@ -123,7 +123,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 +136,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,10 +184,10 @@ 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'];
} }
function api_return($http_code, $message) function api_return($http_code, $message)
@@ -201,12 +200,13 @@ function api_return($http_code, $message)
/** /**
* @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 +215,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);
}
//#################################################################################################################

View File

@@ -15,15 +15,29 @@ $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');
$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();
echo json_encode( echo json_encode(
[ [
'success' => true, 'success' => true,

View File

@@ -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`)
) );

View File

@@ -29,9 +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, json_encode(['success' => false, 'error' => 1104, '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, json_encode(['success' => false, 'error' => 1201, '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 (strlen($message) > 120) api_return(400, json_encode(['success' => false, 'error' => 1202, 'errhighlight' => 103, 'message' => 'Title too long (120 characters)']));
if (strlen($content) > 10000) api_return(400, json_encode(['success' => false, 'error' => 1203, 'errhighlight' => 104, 'message' => 'Content too long (10000 characters)'])); if (strlen($content) > 10000) api_return(400, json_encode(['success' => false, 'error' => 1203, 'errhighlight' => 104, 'message' => 'Content too long (10000 characters)']));
if ($usrmsgid != null && strlen($usrmsgid) > 64) api_return(400, json_encode(['success' => false, 'error' => 1204, 'errhighlight' => -1, 'message' => 'MessageID too long (64 characters)']));
//------------------------------------------------------------------ //------------------------------------------------------------------
@@ -54,6 +55,11 @@ try
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, json_encode(['success' => false, 'error' => 2101, 'errhighlight' => -1, 'message' => 'Daily quota reached ('.Statics::quota_max($data['is_pro']).')']));
if ($fcm == null || $fcm == '' || $fcm == false)
{
api_return(412, json_encode(['success' => false, 'error' => 1401, 'errhighlight' => -1, 'message' => 'No device linked with this account']));
}
//------------------------------------------------------------------ //------------------------------------------------------------------
if ($usrmsgid != null) if ($usrmsgid != null)
@@ -79,6 +85,22 @@ try
//------------------------------------------------------------------ //------------------------------------------------------------------
$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(
[ [
@@ -97,6 +119,7 @@ try
'priority' => $priority, 'priority' => $priority,
'timestamp' => time(), 'timestamp' => time(),
'usr_msg_id' => $usrmsgid, 'usr_msg_id' => $usrmsgid,
'scn_msg_id' => $scn_msg_id,
] ]
]); ]);
$header= $header=
@@ -112,28 +135,24 @@ 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, json_encode(['success' => false, 'error' => 9902, '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, json_encode(['success' => false, 'error' => 9901, '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, $pdo->commit();
't' => $message,
'c' => $content,
'p' => $priority,
'fmid' => try_json($httpresult, ['results', 'message_id']),
'umid' => $usrmsgid,
]);
api_return(200, json_encode( api_return(200, json_encode(
[ [
@@ -145,9 +164,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, json_encode(['success' => false, 'error' => 9903, 'errhighlight' => -1, 'message' => 'PHP script threw exception.'."\n\n".'Exception: ' . $e->getMessage()]));
} }

View File

@@ -34,6 +34,8 @@ $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]);
@@ -51,9 +53,14 @@ if ($fcm_token === null)
} }
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]);
$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]);
echo json_encode( echo json_encode(
[ [
'success' => true, 'success' => true,

View File

@@ -46,15 +46,14 @@ if ($ispro)
$stmt->execute(['uid' => $user_id, 'ptk' => $pro_token]); $stmt->execute(['uid' => $user_id, 'ptk' => $pro_token]);
echo json_encode( echo json_encode(
[ [
'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; return 0;
} }
else else
@@ -65,14 +64,13 @@ else
$stmt->execute(['uid' => $user_id]); $stmt->execute(['uid' => $user_id]);
echo json_encode( echo json_encode(
[ [
'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; return 0;
} }