Compare commits
22 Commits
Author | SHA1 | Date | |
---|---|---|---|
29ce4b727c
|
|||
![]() |
f178019ffe | ||
0a1b948042
|
|||
![]() |
74cbfb235e | ||
63c4104a89
|
|||
2597287b1e
|
|||
![]() |
316156f0f0 | ||
5286e869cc
|
|||
d6dcf28d89
|
|||
1d983b9ac0
|
|||
e525221010
|
|||
4cde4703f2
|
|||
4a07e58c16
|
|||
b12356575a
|
|||
77f571de7d
|
|||
![]() |
f5eef7563b | ||
741c09b1e4
|
|||
0c0d7d181f
|
|||
9304da9422
|
|||
![]() |
d7afdd00f2 | ||
b780ccea1c
|
|||
![]() |
5d8e871871 |
4
android/.idea/assetWizardSettings.xml
generated
4
android/.idea/assetWizardSettings.xml
generated
@@ -67,8 +67,8 @@
|
||||
<option name="values">
|
||||
<map>
|
||||
<entry key="assetSourceType" value="FILE" />
|
||||
<entry key="outputName" value="ic_garbage" />
|
||||
<entry key="sourceFile" value="C:\Users\Mike\Downloads\garbage.svg" />
|
||||
<entry key="outputName" value="ic_share" />
|
||||
<entry key="sourceFile" value="C:\Users\Mike\Downloads\baseline-share-24px.svg" />
|
||||
</map>
|
||||
</option>
|
||||
</PersistentState>
|
||||
|
@@ -43,15 +43,17 @@ dependencies {
|
||||
implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'
|
||||
|
||||
implementation 'com.google.android.material:material:1.0.0'
|
||||
implementation 'com.google.firebase:firebase-core:16.0.4'
|
||||
implementation 'com.google.firebase:firebase-core:16.0.6'
|
||||
implementation 'com.google.firebase:firebase-messaging:17.3.4'
|
||||
implementation 'com.google.android.gms:play-services-ads:17.1.0'
|
||||
implementation 'com.google.android.gms:play-services-ads:17.1.2'
|
||||
implementation 'com.android.billingclient:billing:1.2'
|
||||
|
||||
implementation 'com.squareup.okhttp3:okhttp:3.10.0'
|
||||
implementation 'com.github.kenglxn.QRGen:android:2.5.0'
|
||||
implementation "com.github.DeweyReed:UltimateMusicPicker:2.0.0"
|
||||
implementation 'com.github.duanhong169:colorpicker:1.1.5'
|
||||
|
||||
implementation 'net.danlew:android.joda:2.9.9.2'
|
||||
}
|
||||
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
|
@@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.blackforestbytes.simplecloudnotifier">
|
||||
|
||||
@@ -10,34 +9,51 @@
|
||||
<uses-permission android:name="com.android.vending.BILLING" />
|
||||
|
||||
<application
|
||||
android:name=".SCNApp"
|
||||
android:allowBackup="false"
|
||||
android:name="SCNApp"
|
||||
android:icon="@drawable/icon"
|
||||
android:label="@string/app_name"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme"
|
||||
tools:ignore="GoogleAppIndexingWarning">
|
||||
|
||||
<activity android:name=".view.MainActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<meta-data android:name="com.google.firebase.messaging.default_notification_icon" android:resource="@drawable/icon" />
|
||||
<meta-data android:name="com.google.firebase.messaging.default_notification_color" android:resource="@color/colorAccent" />
|
||||
<meta-data android:name="com.google.android.gms.ads.AD_MANAGER_APP" android:value="true"/>
|
||||
<meta-data android:name="com.google.android.gms.ads.APPLICATION_ID" android:value="ca-app-pub-3320562328966175~7579972005"/>
|
||||
<meta-data
|
||||
android:name="com.google.firebase.messaging.default_notification_icon"
|
||||
android:resource="@drawable/icon" />
|
||||
<meta-data
|
||||
android:name="com.google.firebase.messaging.default_notification_color"
|
||||
android:resource="@color/colorAccent" />
|
||||
<meta-data
|
||||
android:name="com.google.android.gms.ads.AD_MANAGER_APP"
|
||||
android:value="true" />
|
||||
<meta-data
|
||||
android:name="com.google.android.gms.ads.APPLICATION_ID"
|
||||
android:value="ca-app-pub-3320562328966175~7579972005" />
|
||||
|
||||
<service android:name=".service.FBMService" android:exported="false">
|
||||
<service
|
||||
android:name=".service.FBMService"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="com.google.firebase.MESSAGING_EVENT" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<receiver android:name=".service.BroadcastReceiverService" android:exported="false" />
|
||||
<receiver
|
||||
android:name=".service.BroadcastReceiverService"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name=".view.debug.QueryLogActivity"
|
||||
android:label="@string/title_activity_query_log"
|
||||
android:theme="@style/AppTheme" />
|
||||
<activity android:name=".view.debug.SingleQueryLogActivity"></activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
@@ -5,6 +5,7 @@ import android.content.Context;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.android.billingclient.api.BillingClient;
|
||||
import com.blackforestbytes.simplecloudnotifier.model.QueryLog;
|
||||
import com.blackforestbytes.simplecloudnotifier.view.AccountFragment;
|
||||
import com.blackforestbytes.simplecloudnotifier.view.MainActivity;
|
||||
import com.blackforestbytes.simplecloudnotifier.view.TabAdapter;
|
||||
@@ -99,4 +100,5 @@ public class SCNApp extends Application implements LifecycleObserver
|
||||
}
|
||||
}
|
||||
|
||||
//TODO TabLayout indicator does not corretly animate when directly clicking on tabs
|
||||
//TODO: Config for collapsed line count
|
||||
//TODO: Sometimes ads but promode
|
||||
|
@@ -9,6 +9,8 @@ import java.util.TimeZone;
|
||||
|
||||
public class CMessage
|
||||
{
|
||||
public boolean IsExpandedInAdapter = false;
|
||||
|
||||
public final long SCN_ID;
|
||||
public final long Timestamp;
|
||||
public final String Title;
|
||||
|
@@ -0,0 +1,58 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.model;
|
||||
|
||||
|
||||
import android.graphics.Color;
|
||||
|
||||
public enum LogLevel
|
||||
{
|
||||
DEBUG,
|
||||
INFO,
|
||||
WARN,
|
||||
ERROR;
|
||||
|
||||
public String toUIString()
|
||||
{
|
||||
switch (this)
|
||||
{
|
||||
case DEBUG: return "Debug";
|
||||
case INFO: return "Info";
|
||||
case WARN: return "Warning";
|
||||
case ERROR: return "Error";
|
||||
default: return "???";
|
||||
}
|
||||
}
|
||||
|
||||
public int getColor()
|
||||
{
|
||||
switch (this)
|
||||
{
|
||||
case DEBUG: return Color.GRAY;
|
||||
case WARN: return Color.rgb(171, 145, 68);
|
||||
case INFO: return Color.BLACK;
|
||||
case ERROR: return Color.RED;
|
||||
default: return Color.MAGENTA;
|
||||
}
|
||||
}
|
||||
|
||||
public int asInt()
|
||||
{
|
||||
switch (this)
|
||||
{
|
||||
case DEBUG: return 0;
|
||||
case WARN: return 1;
|
||||
case INFO: return 2;
|
||||
case ERROR: return 3;
|
||||
default: return 999;
|
||||
}
|
||||
}
|
||||
|
||||
public static LogLevel fromInt(int i)
|
||||
{
|
||||
if (i == 0) return LogLevel.DEBUG;
|
||||
if (i == 1) return LogLevel.WARN;
|
||||
if (i == 2) return LogLevel.INFO;
|
||||
if (i == 3) return LogLevel.ERROR;
|
||||
|
||||
return LogLevel.ERROR; // ????
|
||||
}
|
||||
}
|
@@ -0,0 +1,69 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.model;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.util.Log;
|
||||
|
||||
import com.blackforestbytes.simplecloudnotifier.SCNApp;
|
||||
import com.blackforestbytes.simplecloudnotifier.lib.collections.CollectionHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class QueryLog
|
||||
{
|
||||
private final static int MAX_HISTORY_SIZE = 192;
|
||||
|
||||
private static QueryLog _instance;
|
||||
public static QueryLog instance() { if (_instance == null) synchronized (QueryLog.class) { if (_instance == null) _instance = new QueryLog(); } return _instance; }
|
||||
|
||||
private QueryLog(){ load(); }
|
||||
|
||||
private final List<SingleQuery> history = new ArrayList<>();
|
||||
|
||||
public synchronized void add(SingleQuery r)
|
||||
{
|
||||
history.add(r);
|
||||
while (history.size() > MAX_HISTORY_SIZE) history.remove(0);
|
||||
|
||||
save();
|
||||
}
|
||||
|
||||
public synchronized List<SingleQuery> get()
|
||||
{
|
||||
List<SingleQuery> r = new ArrayList<>(history);
|
||||
CollectionHelper.sort_inplace(r, (o1, o2) -> (-1) * o1.Timestamp.compareTo(o2.Timestamp));
|
||||
return r;
|
||||
}
|
||||
|
||||
public synchronized void save()
|
||||
{
|
||||
SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("QueryLog", Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor e = sharedPref.edit();
|
||||
|
||||
e.clear();
|
||||
|
||||
e.putInt("history_count", history.size());
|
||||
|
||||
for (int i = 0; i < history.size(); i++) history.get(i).save(e, "message["+(i+1000)+"]");
|
||||
|
||||
e.apply();
|
||||
}
|
||||
|
||||
public synchronized void load()
|
||||
{
|
||||
try
|
||||
{
|
||||
Context c = SCNApp.getContext();
|
||||
SharedPreferences sharedPref = c.getSharedPreferences("QueryLog", Context.MODE_PRIVATE);
|
||||
int count = sharedPref.getInt("history_count", 0);
|
||||
for (int i=0; i < count; i++) history.add(SingleQuery.load(sharedPref, "message["+(i+1000)+"]"));
|
||||
|
||||
CollectionHelper.sort_inplace(history, (o1, o2) -> (-1) * o1.Timestamp.compareTo(o2.Timestamp));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.e("SC:QL:Load", e.toString());
|
||||
}
|
||||
}
|
||||
}
|
@@ -6,8 +6,8 @@ import android.content.SharedPreferences;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.billingclient.api.Purchase;
|
||||
import com.blackforestbytes.simplecloudnotifier.SCNApp;
|
||||
import com.blackforestbytes.simplecloudnotifier.lib.datatypes.Tuple3;
|
||||
import com.blackforestbytes.simplecloudnotifier.lib.string.Str;
|
||||
import com.blackforestbytes.simplecloudnotifier.service.IABService;
|
||||
import com.google.firebase.iid.FirebaseInstanceId;
|
||||
@@ -52,7 +52,8 @@ public class SCNSettings
|
||||
|
||||
public boolean Enabled = true;
|
||||
public int LocalCacheSize = 500;
|
||||
public boolean EnableDeleteSwipe = true;
|
||||
public boolean EnableDeleteSwipe = false;
|
||||
public int PreviewLineCount = 6;
|
||||
|
||||
public final NotificationSettings PriorityLow = new NotificationSettings(PriorityEnum.LOW);
|
||||
public final NotificationSettings PriorityNorm = new NotificationSettings(PriorityEnum.NORMAL);
|
||||
@@ -77,6 +78,7 @@ public class SCNSettings
|
||||
Enabled = sharedPref.getBoolean("app_enabled", Enabled);
|
||||
LocalCacheSize = sharedPref.getInt("local_cache_size", LocalCacheSize);
|
||||
EnableDeleteSwipe = sharedPref.getBoolean("do_del_swipe", EnableDeleteSwipe);
|
||||
PreviewLineCount = sharedPref.getInt("preview_line_count", PreviewLineCount);
|
||||
|
||||
PriorityLow.EnableLED = sharedPref.getBoolean("priority_low:enabled_led", PriorityLow.EnableLED);
|
||||
PriorityLow.EnableSound = sharedPref.getBoolean("priority_low:enabled_sound", PriorityLow.EnableSound);
|
||||
@@ -120,10 +122,14 @@ public class SCNSettings
|
||||
e.putString( "user_key", user_key);
|
||||
e.putString( "fcm_token_local", fcm_token_local);
|
||||
e.putString( "fcm_token_server", fcm_token_server);
|
||||
e.putBoolean("promode_local", promode_local);
|
||||
e.putBoolean("promode_server", promode_server);
|
||||
e.putString( "promode_token", promode_token);
|
||||
|
||||
e.putBoolean("app_enabled", Enabled);
|
||||
e.putInt( "local_cache_size", LocalCacheSize);
|
||||
e.putBoolean("do_del_swipe", EnableDeleteSwipe);
|
||||
e.putInt( "preview_line_count", PreviewLineCount);
|
||||
|
||||
e.putBoolean("priority_low:enabled_led", PriorityLow.EnableLED);
|
||||
e.putBoolean("priority_low:enabled_sound", PriorityLow.EnableSound);
|
||||
@@ -240,14 +246,17 @@ public class SCNSettings
|
||||
|
||||
public void updateProState(View loader)
|
||||
{
|
||||
Purchase purch = IABService.inst().getPurchaseCached(IABService.IAB_PRO_MODE);
|
||||
boolean promode_real = (purch != null);
|
||||
Tuple3<Boolean, Boolean, String> state = IABService.inst().getPurchaseCachedExtended(IABService.IAB_PRO_MODE);
|
||||
if (!state.Item2) return; // not initialized
|
||||
|
||||
boolean promode_real = state.Item1;
|
||||
|
||||
if (promode_real != promode_local || promode_real != promode_server)
|
||||
{
|
||||
promode_local = promode_real;
|
||||
promode_token = promode_real ? state.Item3 : "";
|
||||
save();
|
||||
|
||||
promode_token = promode_real ? purch.getPurchaseToken() : "";
|
||||
updateProStateOnServer(loader);
|
||||
}
|
||||
}
|
||||
|
@@ -4,12 +4,11 @@ import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import com.blackforestbytes.simplecloudnotifier.SCNApp;
|
||||
import com.blackforestbytes.simplecloudnotifier.lib.datatypes.Tuple5;
|
||||
import com.blackforestbytes.simplecloudnotifier.lib.lambda.Func1to0;
|
||||
import com.blackforestbytes.simplecloudnotifier.lib.lambda.Func5to0;
|
||||
import com.blackforestbytes.simplecloudnotifier.lib.string.Str;
|
||||
import com.blackforestbytes.simplecloudnotifier.service.FBMService;
|
||||
|
||||
import org.joda.time.Instant;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
@@ -48,20 +47,20 @@ public class ServerCommunication
|
||||
@Override
|
||||
public void onFailure(Call call, IOException e)
|
||||
{
|
||||
Log.e("SC:register", e.toString());
|
||||
SCNApp.showToast("Communication with server failed", 4000);
|
||||
handleError("register", call, null, Str.Empty, true, e);
|
||||
SCNApp.runOnUiThread(() -> { if (loader!=null)loader.setVisibility(View.GONE); });
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(Call call, Response response)
|
||||
{
|
||||
String r = Str.Empty;
|
||||
try (ResponseBody responseBody = response.body())
|
||||
{
|
||||
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
|
||||
if (responseBody == null) throw new IOException("No response");
|
||||
|
||||
String r = responseBody.string();
|
||||
r = responseBody.string();
|
||||
Log.d("Server::Response", request.url().toString()+"\n"+r);
|
||||
|
||||
JSONObject json = (JSONObject) new JSONTokener(r).nextValue();
|
||||
@@ -81,11 +80,12 @@ public class ServerCommunication
|
||||
SCNSettings.inst().save();
|
||||
|
||||
SCNApp.refreshAccountTab();
|
||||
|
||||
handleSuccess("register", call, response, r);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.e("SC:register", e.toString());
|
||||
SCNApp.showToast("Communication with server failed", 4000);
|
||||
handleError("register", call, response, r, false, e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -96,8 +96,7 @@ public class ServerCommunication
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.e("SC:register", e.toString());
|
||||
SCNApp.showToast("Communication with server failed", 4000);
|
||||
handleError("register", null, null, Str.Empty, false, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,20 +113,20 @@ public class ServerCommunication
|
||||
@Override
|
||||
public void onFailure(Call call, IOException e)
|
||||
{
|
||||
Log.e("SC:update_1", e.toString());
|
||||
SCNApp.showToast("Communication with server failed", 4000);
|
||||
handleError("update<1>", call, null, Str.Empty, true, e);
|
||||
SCNApp.runOnUiThread(() -> { if (loader!=null)loader.setVisibility(View.GONE); });
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(Call call, Response response)
|
||||
{
|
||||
String r = Str.Empty;
|
||||
try (ResponseBody responseBody = response.body())
|
||||
{
|
||||
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
|
||||
if (responseBody == null) throw new IOException("No response");
|
||||
|
||||
String r = responseBody.string();
|
||||
r = responseBody.string();
|
||||
Log.d("Server::Response", request.url().toString()+"\n"+r);
|
||||
|
||||
JSONObject json = (JSONObject) new JSONTokener(r).nextValue();
|
||||
@@ -147,10 +146,12 @@ public class ServerCommunication
|
||||
SCNSettings.inst().save();
|
||||
|
||||
SCNApp.refreshAccountTab();
|
||||
|
||||
handleSuccess("update<1>", call, response, r);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.e("SC:update_1", e.toString());
|
||||
handleError("update<1>", call, response, r, false, e);
|
||||
SCNApp.showToast("Communication with server failed", 4000);
|
||||
}
|
||||
finally
|
||||
@@ -162,8 +163,7 @@ public class ServerCommunication
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.e("SC:update_1", e.toString());
|
||||
SCNApp.showToast("Communication with server failed", 4000);
|
||||
handleError("update<1>", null, null, Str.Empty, false, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,19 +177,23 @@ public class ServerCommunication
|
||||
|
||||
client.newCall(request).enqueue(new Callback() {
|
||||
@Override
|
||||
public void onFailure(Call call, IOException e) {
|
||||
Log.e("SC:update_2", e.toString());
|
||||
public void onFailure(Call call, IOException e)
|
||||
{
|
||||
handleError("update<1>", call, null, Str.Empty, true, e);
|
||||
SCNApp.showToast("Communication with server failed", 4000);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(Call call, Response response) {
|
||||
try (ResponseBody responseBody = response.body()) {
|
||||
public void onResponse(Call call, Response response)
|
||||
{
|
||||
String r = Str.Empty;
|
||||
try (ResponseBody responseBody = response.body())
|
||||
{
|
||||
if (!response.isSuccessful())
|
||||
throw new IOException("Unexpected code " + response);
|
||||
if (responseBody == null) throw new IOException("No response");
|
||||
|
||||
String r = responseBody.string();
|
||||
r = responseBody.string();
|
||||
Log.d("Server::Response", request.url().toString()+"\n"+r);
|
||||
|
||||
JSONObject json = (JSONObject) new JSONTokener(r).nextValue();
|
||||
@@ -207,10 +211,16 @@ public class ServerCommunication
|
||||
SCNSettings.inst().save();
|
||||
|
||||
SCNApp.refreshAccountTab();
|
||||
} catch (Exception e) {
|
||||
Log.e("SC:update_2", e.toString());
|
||||
|
||||
handleSuccess("update<2>", call, response, r);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
handleError("update<2>", call, response, r, false, e);
|
||||
SCNApp.showToast("Communication with server failed", 4000);
|
||||
} finally {
|
||||
}
|
||||
finally
|
||||
{
|
||||
SCNApp.runOnUiThread(() -> {
|
||||
if (loader != null) loader.setVisibility(View.GONE);
|
||||
});
|
||||
@@ -220,8 +230,7 @@ public class ServerCommunication
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.e("SC:update_2", e.toString());
|
||||
SCNApp.showToast("Communication with server failed", 4000);
|
||||
handleError("update<2>", null, null, Str.Empty, false, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,21 +245,23 @@ public class ServerCommunication
|
||||
client.newCall(request).enqueue(new Callback() {
|
||||
@Override
|
||||
public void onFailure(Call call, IOException e) {
|
||||
Log.e("SC:info", e.toString());
|
||||
SCNApp.showToast("Communication with server failed", 4000);
|
||||
handleError("info", call, null, Str.Empty, true, e);
|
||||
SCNApp.runOnUiThread(() -> {
|
||||
if (loader != null) loader.setVisibility(View.GONE);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(Call call, Response response) {
|
||||
try (ResponseBody responseBody = response.body()) {
|
||||
public void onResponse(Call call, Response response)
|
||||
{
|
||||
String r = Str.Empty;
|
||||
try (ResponseBody responseBody = response.body())
|
||||
{
|
||||
if (!response.isSuccessful())
|
||||
throw new IOException("Unexpected code " + response);
|
||||
if (responseBody == null) throw new IOException("No response");
|
||||
|
||||
String r = responseBody.string();
|
||||
r = responseBody.string();
|
||||
Log.d("Server::Response", request.url().toString()+"\n"+r);
|
||||
|
||||
JSONObject json = (JSONObject) new JSONTokener(r).nextValue();
|
||||
@@ -290,21 +301,23 @@ public class ServerCommunication
|
||||
|
||||
if (json_int(json, "unack_count")>0) ServerCommunication.requery(id, key, loader);
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e("SC:info", e.toString());
|
||||
handleSuccess("info", call, response, r);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
handleError("info", call, response, r, false, e);
|
||||
SCNApp.showToast("Communication with server failed", 4000);
|
||||
} finally {
|
||||
SCNApp.runOnUiThread(() -> {
|
||||
if (loader != null) loader.setVisibility(View.GONE);
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
SCNApp.runOnUiThread(() -> { if (loader != null) loader.setVisibility(View.GONE); });
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.e("SC:info", e.toString());
|
||||
SCNApp.showToast("Communication with server failed", 4000);
|
||||
handleError("info", null, null, Str.Empty, false, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -319,21 +332,23 @@ public class ServerCommunication
|
||||
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);
|
||||
handleError("requery", call, null, Str.Empty, true, e);
|
||||
SCNApp.runOnUiThread(() -> {
|
||||
if (loader != null) loader.setVisibility(View.GONE);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(Call call, Response response) {
|
||||
try (ResponseBody responseBody = response.body()) {
|
||||
public void onResponse(Call call, Response response)
|
||||
{
|
||||
String r = Str.Empty;
|
||||
try (ResponseBody responseBody = response.body())
|
||||
{
|
||||
if (!response.isSuccessful())
|
||||
throw new IOException("Unexpected code " + response);
|
||||
if (responseBody == null) throw new IOException("No response");
|
||||
|
||||
String r = responseBody.string();
|
||||
r = responseBody.string();
|
||||
Log.d("Server::Response", request.url().toString()+"\n"+r);
|
||||
|
||||
JSONObject json = (JSONObject) new JSONTokener(r).nextValue();
|
||||
@@ -359,10 +374,15 @@ public class ServerCommunication
|
||||
FBMService.recieveData(time, title, content, prio, scn_id, true);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e("SC:info", e.toString());
|
||||
handleSuccess("requery", call, response, r);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
handleError("requery", call, response, r, false, e);
|
||||
SCNApp.showToast("Communication with server failed", 4000);
|
||||
} finally {
|
||||
}
|
||||
finally
|
||||
{
|
||||
SCNApp.runOnUiThread(() -> {
|
||||
if (loader != null) loader.setVisibility(View.GONE);
|
||||
});
|
||||
@@ -372,8 +392,7 @@ public class ServerCommunication
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.e("SC:requery", e.toString());
|
||||
SCNApp.showToast("Communication with server failed", 4000);
|
||||
handleError("requery", null, null, Str.Empty, false, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -387,21 +406,25 @@ public class ServerCommunication
|
||||
.url(BASE_URL + "upgrade.php?user_id=" + id + "&user_key=" + key + "&pro=" + pro + "&pro_token=" + URLEncoder.encode(pro_token, "utf-8"))
|
||||
.build();
|
||||
|
||||
client.newCall(request).enqueue(new Callback() {
|
||||
client.newCall(request).enqueue(new Callback()
|
||||
{
|
||||
@Override
|
||||
public void onFailure(Call call, IOException e) {
|
||||
Log.e("SC:upgrade", e.toString());
|
||||
SCNApp.showToast("Communication with server failed", 4000);
|
||||
public void onFailure(Call call, IOException e)
|
||||
{
|
||||
handleError("upgrade", call, null, Str.Empty, true, e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(Call call, Response response) {
|
||||
try (ResponseBody responseBody = response.body()) {
|
||||
public void onResponse(Call call, Response response)
|
||||
{
|
||||
String r = Str.Empty;
|
||||
try (ResponseBody responseBody = response.body())
|
||||
{
|
||||
if (!response.isSuccessful())
|
||||
throw new IOException("Unexpected code " + response);
|
||||
if (responseBody == null) throw new IOException("No response");
|
||||
|
||||
String r = responseBody.string();
|
||||
r = responseBody.string();
|
||||
Log.d("Server::Response", request.url().toString()+"\n"+r);
|
||||
|
||||
JSONObject json = (JSONObject) new JSONTokener(r).nextValue();
|
||||
@@ -418,10 +441,15 @@ public class ServerCommunication
|
||||
SCNSettings.inst().save();
|
||||
|
||||
SCNApp.refreshAccountTab();
|
||||
} catch (Exception e) {
|
||||
Log.e("SC:upgrade", e.toString());
|
||||
SCNApp.showToast("Communication with server failed", 4000);
|
||||
} finally {
|
||||
|
||||
handleSuccess("upgrade", call, response, r);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
handleError("upgrade", call, response, r, false, e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
SCNApp.runOnUiThread(() -> { if (loader != null) loader.setVisibility(View.GONE); });
|
||||
}
|
||||
}
|
||||
@@ -429,8 +457,7 @@ public class ServerCommunication
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
SCNApp.showToast("Communication with server failed", 4000);
|
||||
handleError("upgrade", null, null, Str.Empty, false, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -442,37 +469,43 @@ public class ServerCommunication
|
||||
.url(BASE_URL + "ack.php?user_id=" + id + "&user_key=" + key + "&scn_msg_id=" + msg_scn_id)
|
||||
.build();
|
||||
|
||||
client.newCall(request).enqueue(new Callback() {
|
||||
client.newCall(request).enqueue(new Callback()
|
||||
{
|
||||
@Override
|
||||
public void onFailure(Call call, IOException e) {
|
||||
Log.e("SC:ack", e.toString());
|
||||
public void onFailure(Call call, IOException e)
|
||||
{
|
||||
handleError("ack", call, null, Str.Empty, true, e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(Call call, Response response)
|
||||
{
|
||||
try (ResponseBody responseBody = response.body()) {
|
||||
String r = Str.Empty;
|
||||
try (ResponseBody responseBody = response.body())
|
||||
{
|
||||
if (!response.isSuccessful())
|
||||
throw new IOException("Unexpected code " + response);
|
||||
if (responseBody == null) throw new IOException("No response");
|
||||
|
||||
String r = responseBody.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);
|
||||
handleSuccess("ack", call, response, r);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
handleError("ack", call, response, r, false, e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.e("SC:ack", e.toString());
|
||||
handleError("ack", null, null, Str.Empty, false, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -487,21 +520,21 @@ public class ServerCommunication
|
||||
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);
|
||||
});
|
||||
handleError("expand", call, null, Str.Empty, true, e);
|
||||
SCNApp.runOnUiThread(() -> { if (loader != null) loader.setVisibility(View.GONE); });
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(Call call, Response response) {
|
||||
try (ResponseBody responseBody = response.body()) {
|
||||
public void onResponse(Call call, Response response)
|
||||
{
|
||||
String r = Str.Empty;
|
||||
try (ResponseBody responseBody = response.body())
|
||||
{
|
||||
if (!response.isSuccessful())
|
||||
throw new IOException("Unexpected code " + response);
|
||||
if (responseBody == null) throw new IOException("No response");
|
||||
|
||||
String r = responseBody.string();
|
||||
r = responseBody.string();
|
||||
Log.d("Server::Response", request.url().toString()+"\n"+r);
|
||||
|
||||
JSONObject json = (JSONObject) new JSONTokener(r).nextValue();
|
||||
@@ -522,21 +555,22 @@ public class ServerCommunication
|
||||
|
||||
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);
|
||||
});
|
||||
handleSuccess("expand", call, response, r);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
handleError("expand", call, response, r, false, e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
SCNApp.runOnUiThread(() -> { if (loader != null) loader.setVisibility(View.GONE); });
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.e("SC:expand", e.toString());
|
||||
SCNApp.showToast("Communication with server failed", 4000);
|
||||
handleError("expand", null, null, Str.Empty, false, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -564,4 +598,57 @@ public class ServerCommunication
|
||||
{
|
||||
return o.getString(key);
|
||||
}
|
||||
|
||||
private static void handleSuccess(String source, Call call, Response resp, String respBody)
|
||||
{
|
||||
Log.d("SC:"+source, respBody);
|
||||
|
||||
try
|
||||
{
|
||||
Instant i = Instant.now();
|
||||
String s = source;
|
||||
String u = call.request().url().toString();
|
||||
int rc = resp.code();
|
||||
String r = respBody;
|
||||
LogLevel l = LogLevel.INFO;
|
||||
|
||||
SingleQuery q = new SingleQuery(l, i, s, u, r, rc, "SUCCESS");
|
||||
QueryLog.instance().add(q);
|
||||
}
|
||||
catch (Exception e2)
|
||||
{
|
||||
Log.e("SC:HandleSuccess", e2.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private static void handleError(String source, Call call, Response resp, String respBody, boolean isio, Exception e)
|
||||
{
|
||||
Log.e("SC:"+source, e.toString());
|
||||
|
||||
if (isio)
|
||||
{
|
||||
SCNApp.showToast("Can't connect to server", 3000);
|
||||
}
|
||||
else
|
||||
{
|
||||
SCNApp.showToast("Communication with server failed", 4000);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Instant i = Instant.now();
|
||||
String s = source;
|
||||
String u = (call==null)?Str.Empty:call.request().url().toString();
|
||||
int rc = (resp==null)?-1:resp.code();
|
||||
String r = respBody;
|
||||
LogLevel l = isio?LogLevel.WARN:LogLevel.ERROR;
|
||||
|
||||
SingleQuery q = new SingleQuery(l, i, s, u, r, rc, e.toString());
|
||||
QueryLog.instance().add(q);
|
||||
}
|
||||
catch (Exception e2)
|
||||
{
|
||||
Log.e("SC:HandleError", e2.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,82 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.model;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.BaseBundle;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.blackforestbytes.simplecloudnotifier.lib.string.Str;
|
||||
|
||||
import org.joda.time.Instant;
|
||||
|
||||
public class SingleQuery
|
||||
{
|
||||
public final Instant Timestamp;
|
||||
|
||||
public final LogLevel Level;
|
||||
public final String Name;
|
||||
public final String URL;
|
||||
public final String Response;
|
||||
public final int ResponseCode;
|
||||
public final String ExceptionString;
|
||||
|
||||
public SingleQuery(LogLevel l, Instant i, String n, String u, String r, int rc, String e)
|
||||
{
|
||||
Level=l;
|
||||
Timestamp=i;
|
||||
Name=n;
|
||||
URL=u;
|
||||
Response=r;
|
||||
ResponseCode=rc;
|
||||
ExceptionString=e;
|
||||
}
|
||||
|
||||
public void save(SharedPreferences.Editor e, String base)
|
||||
{
|
||||
e.putInt(base+".Level", Level.asInt());
|
||||
e.putLong(base+".Timestamp", Timestamp.getMillis());
|
||||
e.putString(base+".Name", Name);
|
||||
e.putString(base+".URL", URL);
|
||||
e.putString(base+".Response", Response);
|
||||
e.putInt(base+".ResponseCode", ResponseCode);
|
||||
e.putString(base+".ExceptionString", ExceptionString);
|
||||
}
|
||||
|
||||
public void save(BaseBundle e, String base)
|
||||
{
|
||||
e.putInt(base+".Level", Level.asInt());
|
||||
e.putLong(base+".Timestamp", Timestamp.getMillis());
|
||||
e.putString(base+".Name", Name);
|
||||
e.putString(base+".URL", URL);
|
||||
e.putString(base+".Response", Response);
|
||||
e.putInt(base+".ResponseCode", ResponseCode);
|
||||
e.putString(base+".ExceptionString", ExceptionString);
|
||||
}
|
||||
|
||||
public static SingleQuery load(SharedPreferences e, String base)
|
||||
{
|
||||
return new SingleQuery
|
||||
(
|
||||
LogLevel.fromInt(e.getInt(base+".Level", 0)),
|
||||
new Instant(e.getLong(base+".Timestamp", 0)),
|
||||
e.getString(base+".Name", Str.Empty),
|
||||
e.getString(base+".URL", Str.Empty),
|
||||
e.getString(base+".Response", Str.Empty),
|
||||
e.getInt(base+".ResponseCode", -1),
|
||||
e.getString(base+".ExceptionString", Str.Empty)
|
||||
);
|
||||
}
|
||||
|
||||
public static SingleQuery load(BaseBundle e, String base)
|
||||
{
|
||||
return new SingleQuery
|
||||
(
|
||||
LogLevel.fromInt(e.getInt(base+".Level", 0)),
|
||||
new Instant(e.getLong(base+".Timestamp", 0)),
|
||||
e.getString(base+".Name", Str.Empty),
|
||||
e.getString(base+".URL", Str.Empty),
|
||||
e.getString(base+".Response", Str.Empty),
|
||||
e.getInt(base+".ResponseCode", -1),
|
||||
e.getString(base+".ExceptionString", Str.Empty)
|
||||
);
|
||||
}
|
||||
}
|
@@ -6,14 +6,22 @@ import android.widget.Toast;
|
||||
import com.blackforestbytes.simplecloudnotifier.SCNApp;
|
||||
import com.blackforestbytes.simplecloudnotifier.lib.datatypes.Tuple4;
|
||||
import com.blackforestbytes.simplecloudnotifier.lib.datatypes.Tuple5;
|
||||
import com.blackforestbytes.simplecloudnotifier.lib.string.Str;
|
||||
import com.blackforestbytes.simplecloudnotifier.model.CMessage;
|
||||
import com.blackforestbytes.simplecloudnotifier.model.CMessageList;
|
||||
import com.blackforestbytes.simplecloudnotifier.model.LogLevel;
|
||||
import com.blackforestbytes.simplecloudnotifier.model.PriorityEnum;
|
||||
import com.blackforestbytes.simplecloudnotifier.model.QueryLog;
|
||||
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
|
||||
import com.blackforestbytes.simplecloudnotifier.model.ServerCommunication;
|
||||
import com.blackforestbytes.simplecloudnotifier.model.SingleQuery;
|
||||
import com.google.android.gms.common.util.JsonUtils;
|
||||
import com.google.firebase.messaging.FirebaseMessagingService;
|
||||
import com.google.firebase.messaging.RemoteMessage;
|
||||
|
||||
import org.joda.time.Instant;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class FBMService extends FirebaseMessagingService
|
||||
{
|
||||
@Override
|
||||
@@ -42,6 +50,10 @@ public class FBMService extends FirebaseMessagingService
|
||||
long scn_id = Long.parseLong(remoteMessage.getData().get("scn_msg_id"));
|
||||
boolean trimmed = Boolean.parseBoolean(remoteMessage.getData().get("trimmed"));
|
||||
|
||||
|
||||
SingleQuery q = new SingleQuery(LogLevel.INFO, Instant.now(), "FBM<recieve>", Str.Empty, new JSONObject(remoteMessage.getData()).toString(), 0, "SUCCESS");
|
||||
QueryLog.instance().add(q);
|
||||
|
||||
if (trimmed)
|
||||
{
|
||||
ServerCommunication.expand(SCNSettings.inst().user_id, SCNSettings.inst().user_key, scn_id, null, (i1, i2, i3, i4, i5) -> recieveData(i4, i1, i2, i3, i5, false));
|
||||
|
@@ -2,6 +2,7 @@ package com.blackforestbytes.simplecloudnotifier.service;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
@@ -11,13 +12,18 @@ import com.android.billingclient.api.BillingFlowParams;
|
||||
import com.android.billingclient.api.Purchase;
|
||||
import com.android.billingclient.api.PurchasesUpdatedListener;
|
||||
import com.blackforestbytes.simplecloudnotifier.SCNApp;
|
||||
import com.blackforestbytes.simplecloudnotifier.lib.datatypes.Tuple2;
|
||||
import com.blackforestbytes.simplecloudnotifier.lib.datatypes.Tuple3;
|
||||
import com.blackforestbytes.simplecloudnotifier.lib.lambda.Func0to0;
|
||||
import com.blackforestbytes.simplecloudnotifier.lib.string.Str;
|
||||
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
|
||||
import com.blackforestbytes.simplecloudnotifier.view.MainActivity;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Dictionary;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
@@ -45,12 +51,21 @@ public class IABService implements PurchasesUpdatedListener
|
||||
}
|
||||
}
|
||||
|
||||
public enum SimplePurchaseState { YES, NO, UNINITIALIZED }
|
||||
|
||||
private BillingClient client;
|
||||
private boolean isServiceConnected;
|
||||
private final List<Purchase> purchases = new ArrayList<>();
|
||||
private boolean _isInitialized = false;
|
||||
|
||||
private Map<String, Boolean> _localCache= new HashMap<>();
|
||||
|
||||
public IABService(Context c)
|
||||
{
|
||||
_isInitialized = false;
|
||||
|
||||
loadCache();
|
||||
|
||||
client = BillingClient
|
||||
.newBuilder(c)
|
||||
.setListener(this)
|
||||
@@ -59,6 +74,45 @@ public class IABService implements PurchasesUpdatedListener
|
||||
startServiceConnection(this::queryPurchases, false);
|
||||
}
|
||||
|
||||
private void loadCache()
|
||||
{
|
||||
_localCache.clear();
|
||||
SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("IAB", Context.MODE_PRIVATE);
|
||||
int count = sharedPref.getInt("c", 0);
|
||||
for (int i=0; i < count; i++)
|
||||
{
|
||||
String k = sharedPref.getString("["+i+"]->key", null);
|
||||
boolean v = sharedPref.getBoolean("["+i+"]->value", false);
|
||||
if (k==null)continue;
|
||||
_localCache.put(k, v);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveCache()
|
||||
{
|
||||
SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("IAB", Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor= sharedPref.edit();
|
||||
|
||||
editor.putInt("c", _localCache.size());
|
||||
int i = 0;
|
||||
for (Map.Entry<String, Boolean> e : _localCache.entrySet())
|
||||
{
|
||||
editor.putString("["+i+"]->key", e.getKey());
|
||||
editor.putBoolean("["+i+"]->value", e.getValue());
|
||||
i++;
|
||||
}
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
private synchronized void updateCache(String k, boolean v)
|
||||
{
|
||||
if (_localCache.containsKey(k) && _localCache.get(k)==v) return;
|
||||
|
||||
_localCache.put(k, v);
|
||||
saveCache();
|
||||
}
|
||||
|
||||
public void queryPurchases()
|
||||
{
|
||||
Func0to0 queryToExecute = () ->
|
||||
@@ -71,10 +125,12 @@ public class IABService implements PurchasesUpdatedListener
|
||||
{
|
||||
for (Purchase p : purchasesResult.getPurchasesList())
|
||||
{
|
||||
handlePurchase(p);
|
||||
handlePurchase(p, false);
|
||||
}
|
||||
|
||||
boolean newProMode = getPurchaseCached(IAB_PRO_MODE) != null;
|
||||
_isInitialized = true;
|
||||
|
||||
boolean newProMode = getPurchaseCachedSimple(IAB_PRO_MODE);
|
||||
if (newProMode != SCNSettings.inst().promode_local)
|
||||
{
|
||||
refreshProModeListener();
|
||||
@@ -102,7 +158,8 @@ public class IABService implements PurchasesUpdatedListener
|
||||
}, true);
|
||||
}
|
||||
|
||||
private void executeServiceRequest(Func0to0 runnable, final boolean userRequest) {
|
||||
private void executeServiceRequest(Func0to0 runnable, final boolean userRequest)
|
||||
{
|
||||
if (isServiceConnected)
|
||||
{
|
||||
runnable.invoke();
|
||||
@@ -130,28 +187,31 @@ public class IABService implements PurchasesUpdatedListener
|
||||
{
|
||||
for (Purchase purchase : purchases)
|
||||
{
|
||||
handlePurchase(purchase);
|
||||
handlePurchase(purchase, true);
|
||||
}
|
||||
}
|
||||
else if (responseCode == BillingClient.BillingResponse.ITEM_ALREADY_OWNED && purchases != null)
|
||||
{
|
||||
for (Purchase purchase : purchases)
|
||||
{
|
||||
handlePurchase(purchase);
|
||||
handlePurchase(purchase, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handlePurchase(Purchase purchase)
|
||||
private void handlePurchase(Purchase purchase, boolean triggerUpdate)
|
||||
{
|
||||
Log.d(TAG, "Got a verified purchase: " + purchase);
|
||||
|
||||
purchases.add(purchase);
|
||||
|
||||
refreshProModeListener();
|
||||
if (triggerUpdate) refreshProModeListener();
|
||||
|
||||
updateCache(purchase.getSku(), true);
|
||||
}
|
||||
|
||||
private void refreshProModeListener() {
|
||||
private void refreshProModeListener()
|
||||
{
|
||||
MainActivity ma = SCNApp.getMainActivity();
|
||||
if (ma != null) ma.adpTabs.tab3.updateProState();
|
||||
if (ma != null) ma.adpTabs.tab1.updateProState();
|
||||
@@ -183,13 +243,31 @@ public class IABService implements PurchasesUpdatedListener
|
||||
});
|
||||
}
|
||||
|
||||
public Purchase getPurchaseCached(String id)
|
||||
public boolean getPurchaseCachedSimple(String id)
|
||||
{
|
||||
for (Purchase p : purchases)
|
||||
{
|
||||
if (Str.equals(p.getSku(), id)) return p;
|
||||
return getPurchaseCachedExtended(id).Item1;
|
||||
}
|
||||
|
||||
return null;
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public Tuple3<Boolean, Boolean, String> getPurchaseCachedExtended(String id)
|
||||
{
|
||||
// <state, initialized, token>
|
||||
|
||||
if (!_isInitialized)
|
||||
{
|
||||
if (_localCache.containsKey(id) && _localCache.get(id)) return new Tuple3<>(true, false, Str.Empty);
|
||||
}
|
||||
|
||||
for (Purchase p : purchases)
|
||||
{
|
||||
if (Str.equals(p.getSku(), id))
|
||||
{
|
||||
updateCache(id, true);
|
||||
return new Tuple3<>(true, true, p.getPurchaseToken());
|
||||
}
|
||||
}
|
||||
|
||||
updateCache(id, false);
|
||||
return new Tuple3<>(false, true, Str.Empty);
|
||||
}
|
||||
}
|
||||
|
@@ -65,7 +65,8 @@ public class NotificationService
|
||||
channel0.setDescription("Push notifications from the server with low priority.\nGo to the in-app settings to configure ringtone, volume and vibrations");
|
||||
channel0.setSound(null, null);
|
||||
channel0.setVibrationPattern(null);
|
||||
channel0.setLightColor(Color.BLUE);
|
||||
channel0.setLightColor(Color.CYAN);
|
||||
channel0.enableLights(true);
|
||||
notifman.createNotificationChannel(channel0);
|
||||
}
|
||||
}
|
||||
@@ -77,7 +78,8 @@ public class NotificationService
|
||||
channel1.setDescription("Push notifications from the server with low priority.\nGo to the in-app settings to configure ringtone, volume and vibrations");
|
||||
channel1.setSound(null, null);
|
||||
channel1.setVibrationPattern(null);
|
||||
channel1.setLightColor(Color.BLUE);
|
||||
channel1.setLightColor(Color.CYAN);
|
||||
channel1.enableLights(true);
|
||||
notifman.createNotificationChannel(channel1);
|
||||
}
|
||||
}
|
||||
@@ -85,11 +87,13 @@ public class NotificationService
|
||||
NotificationChannel channel2 = notifman.getNotificationChannel(CHANNEL_P2_ID);
|
||||
if (channel2 == null)
|
||||
{
|
||||
channel2 = new NotificationChannel(CHANNEL_P1_ID, "Push notifications (high priority)", NotificationManager.IMPORTANCE_DEFAULT);
|
||||
channel2 = new NotificationChannel(CHANNEL_P2_ID, "Push notifications (high priority)", NotificationManager.IMPORTANCE_DEFAULT);
|
||||
channel2.setDescription("Push notifications from the server with low priority.\nGo to the in-app settings to configure ringtone, volume and vibrations");
|
||||
channel2.setSound(null, null);
|
||||
channel2.setVibrationPattern(null);
|
||||
channel2.setLightColor(Color.BLUE);
|
||||
channel2.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
|
||||
channel2.setLightColor(Color.CYAN);
|
||||
channel2.enableLights(true);
|
||||
notifman.createNotificationChannel(channel2);
|
||||
}
|
||||
}
|
||||
@@ -115,9 +119,9 @@ public class NotificationService
|
||||
{
|
||||
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));
|
||||
v.vibrate(VibrationEffect.createOneShot(500, VibrationEffect.DEFAULT_AMPLITUDE));
|
||||
} else {
|
||||
v.vibrate(1500);
|
||||
v.vibrate(500);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -254,7 +258,7 @@ public class NotificationService
|
||||
if (ns.EnableVibration)
|
||||
{
|
||||
Vibrator v = (Vibrator) SCNApp.getContext().getSystemService(Context.VIBRATOR_SERVICE);
|
||||
v.vibrate(VibrationEffect.createOneShot(1500, VibrationEffect.DEFAULT_AMPLITUDE));
|
||||
v.vibrate(VibrationEffect.createOneShot(500, VibrationEffect.DEFAULT_AMPLITUDE));
|
||||
}
|
||||
|
||||
//if (ns.EnableLED) { } // no LED in Android-O -- configure via Channel
|
||||
|
@@ -0,0 +1,56 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.TypedValue;
|
||||
import android.widget.ScrollView;
|
||||
|
||||
import com.blackforestbytes.simplecloudnotifier.R;
|
||||
|
||||
public class MaxHeightScrollView extends ScrollView
|
||||
{
|
||||
public int maxHeight = Integer.MAX_VALUE;//dp
|
||||
|
||||
public MaxHeightScrollView(Context context)
|
||||
{
|
||||
super(context);
|
||||
}
|
||||
|
||||
public MaxHeightScrollView(Context context, AttributeSet attrs)
|
||||
{
|
||||
super(context, attrs);
|
||||
|
||||
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.MaxHeightScrollView, 0, 0);
|
||||
try {
|
||||
maxHeight = a.getInteger(R.styleable.MaxHeightScrollView_maxHeightOverride, Integer.MAX_VALUE);
|
||||
} finally {
|
||||
a.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
public MaxHeightScrollView(Context context, AttributeSet attrs, int defStyleAttr)
|
||||
{
|
||||
super(context, attrs, defStyleAttr);
|
||||
|
||||
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.MaxHeightScrollView, 0, 0);
|
||||
try {
|
||||
maxHeight = a.getInteger(R.styleable.MaxHeightScrollView_maxHeightOverride, Integer.MAX_VALUE);
|
||||
} finally {
|
||||
a.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
|
||||
{
|
||||
heightMeasureSpec = MeasureSpec.makeMeasureSpec(dpToPx(getResources(), maxHeight), MeasureSpec.AT_MOST);
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
}
|
||||
|
||||
private int dpToPx(Resources res, int dp)
|
||||
{
|
||||
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, res.getDisplayMetrics());
|
||||
}
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.util;
|
||||
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
|
||||
public abstract class TextChangedListener<T> implements TextWatcher {
|
||||
private T target;
|
||||
|
||||
public TextChangedListener(T target) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
this.onTextChanged(target, s);
|
||||
}
|
||||
|
||||
public abstract void onTextChanged(T target, Editable s);
|
||||
}
|
@@ -1,14 +1,21 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.view;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.icu.text.SymbolTable;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.blackforestbytes.simplecloudnotifier.R;
|
||||
import com.blackforestbytes.simplecloudnotifier.SCNApp;
|
||||
import com.blackforestbytes.simplecloudnotifier.model.CMessageList;
|
||||
import com.blackforestbytes.simplecloudnotifier.model.QueryLog;
|
||||
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
|
||||
import com.blackforestbytes.simplecloudnotifier.service.IABService;
|
||||
import com.blackforestbytes.simplecloudnotifier.service.NotificationService;
|
||||
import com.blackforestbytes.simplecloudnotifier.view.debug.QueryLogActivity;
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
@@ -24,6 +31,8 @@ public class MainActivity extends AppCompatActivity
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
QueryLog.instance();
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
@@ -33,6 +42,7 @@ public class MainActivity extends AppCompatActivity
|
||||
layoutRoot = findViewById(R.id.layoutRoot);
|
||||
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
toolbar.setOnClickListener(this::onToolbackClicked);
|
||||
setSupportActionBar(toolbar);
|
||||
|
||||
ViewPager viewPager = findViewById(R.id.pager);
|
||||
@@ -81,4 +91,16 @@ public class MainActivity extends AppCompatActivity
|
||||
CMessageList.inst().fullSave();
|
||||
IABService.inst().destroy();
|
||||
}
|
||||
|
||||
private int clickCount = 0;
|
||||
private long lastClick = 0;
|
||||
private void onToolbackClicked(View v)
|
||||
{
|
||||
long now = System.currentTimeMillis();
|
||||
if (now - lastClick > 200) clickCount=0;
|
||||
clickCount++;
|
||||
lastClick = now;
|
||||
|
||||
if (clickCount == 4) startActivity(new Intent(this, QueryLogActivity.class));
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.view;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -9,8 +10,11 @@ import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.blackforestbytes.simplecloudnotifier.R;
|
||||
import com.blackforestbytes.simplecloudnotifier.SCNApp;
|
||||
import com.blackforestbytes.simplecloudnotifier.model.CMessage;
|
||||
import com.blackforestbytes.simplecloudnotifier.model.CMessageList;
|
||||
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Collections;
|
||||
@@ -53,7 +57,7 @@ public class MessageAdapter extends RecyclerView.Adapter
|
||||
{
|
||||
CMessage msg = CMessageList.inst().tryGetFromBack(position);
|
||||
MessagePresenter view = (MessagePresenter) holder;
|
||||
view.setMessage(msg);
|
||||
view.setMessage(msg, position);
|
||||
|
||||
viewHolders.put(view, true);
|
||||
}
|
||||
@@ -110,7 +114,11 @@ public class MessageAdapter extends RecyclerView.Adapter
|
||||
public RelativeLayout viewForeground;
|
||||
public RelativeLayout viewBackground;
|
||||
|
||||
public MaterialButton btnShare;
|
||||
public MaterialButton btnDelete;
|
||||
|
||||
private CMessage data;
|
||||
private int datapos;
|
||||
|
||||
MessagePresenter(View itemView)
|
||||
{
|
||||
@@ -121,6 +129,8 @@ public class MessageAdapter extends RecyclerView.Adapter
|
||||
ivPriority = itemView.findViewById(R.id.ivPriority);
|
||||
viewForeground = itemView.findViewById(R.id.layoutFront);
|
||||
viewBackground = itemView.findViewById(R.id.layoutBack);
|
||||
btnShare = itemView.findViewById(R.id.btnShare);
|
||||
btnDelete = itemView.findViewById(R.id.btnDelete);
|
||||
|
||||
itemView.setOnClickListener(this);
|
||||
tvTimestamp.setOnClickListener(this);
|
||||
@@ -128,9 +138,22 @@ public class MessageAdapter extends RecyclerView.Adapter
|
||||
tvMessage.setOnClickListener(this);
|
||||
ivPriority.setOnClickListener(this);
|
||||
viewForeground.setOnClickListener(this);
|
||||
|
||||
btnShare.setOnClickListener(v ->
|
||||
{
|
||||
if (data == null) return;
|
||||
Intent sharingIntent = new Intent(android.content.Intent.ACTION_SEND);
|
||||
sharingIntent.setType("text/plain");
|
||||
sharingIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, data.Title);
|
||||
sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, data.Content);
|
||||
SCNApp.getMainActivity().startActivity(Intent.createChooser(sharingIntent, "Share message"));
|
||||
|
||||
});
|
||||
btnDelete.setOnClickListener(v -> { if (data != null) SCNApp.getMainActivity().adpTabs.tab1.deleteMessage(datapos); });
|
||||
|
||||
}
|
||||
|
||||
void setMessage(CMessage msg)
|
||||
void setMessage(CMessage msg, int pos)
|
||||
{
|
||||
tvTimestamp.setText(msg.formatTimestamp());
|
||||
tvTitle.setText(msg.Title);
|
||||
@@ -155,21 +178,49 @@ public class MessageAdapter extends RecyclerView.Adapter
|
||||
}
|
||||
|
||||
data = msg;
|
||||
datapos = pos;
|
||||
|
||||
if (msg.IsExpandedInAdapter) expand(true); else collapse(true);
|
||||
}
|
||||
|
||||
private void expand(boolean force)
|
||||
{
|
||||
if (data != null && data.IsExpandedInAdapter && !force) return;
|
||||
if (data != null) data.IsExpandedInAdapter = true;
|
||||
if (tvMessage != null) tvMessage.setMaxLines(9999);
|
||||
if (btnDelete != null) btnDelete.setVisibility(View.VISIBLE);
|
||||
if (btnShare != null) btnShare.setVisibility(View.VISIBLE);
|
||||
|
||||
}
|
||||
|
||||
private int norm(int i) { return (i<=0)?0:((i>9999)?9999:i); }
|
||||
|
||||
private void collapse(boolean force)
|
||||
{
|
||||
if (data != null && !data.IsExpandedInAdapter && !force) return;
|
||||
if (data != null) data.IsExpandedInAdapter = false;
|
||||
if (tvMessage != null) tvMessage.setMaxLines(norm(SCNSettings.inst().PreviewLineCount));
|
||||
if (btnDelete != null) btnDelete.setVisibility(View.GONE);
|
||||
if (btnShare != null) btnShare.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v)
|
||||
{
|
||||
if (data.IsExpandedInAdapter)
|
||||
{
|
||||
collapse(false);
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
holder.collapse(false);
|
||||
}
|
||||
|
||||
tvMessage.setMaxLines(9999);
|
||||
expand(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -58,7 +58,7 @@ public class NotificationsFragment extends Fragment implements MessageAdapterTou
|
||||
|
||||
public void updateProState()
|
||||
{
|
||||
if (adView != null) adView.setVisibility(IABService.inst().getPurchaseCached(IABService.IAB_PRO_MODE) != null ? View.GONE : View.VISIBLE);
|
||||
if (adView != null) adView.setVisibility(IABService.inst().getPurchaseCachedSimple(IABService.IAB_PRO_MODE) ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -66,9 +66,15 @@ public class NotificationsFragment extends Fragment implements MessageAdapterTou
|
||||
{
|
||||
if (viewHolder instanceof MessageAdapter.MessagePresenter)
|
||||
{
|
||||
final int deletedIndex = viewHolder.getAdapterPosition();
|
||||
deleteMessage(viewHolder.getAdapterPosition());
|
||||
}
|
||||
}
|
||||
|
||||
final CMessage deletedItem = adpMessages.removeItem(viewHolder.getAdapterPosition());
|
||||
public void deleteMessage(int pos)
|
||||
{
|
||||
final int deletedIndex = pos;
|
||||
|
||||
final CMessage deletedItem = adpMessages.removeItem(pos);
|
||||
String name = deletedItem.Title;
|
||||
|
||||
Snackbar snackbar = Snackbar.make(SCNApp.getMainActivity().layoutRoot, name + " removed", Snackbar.LENGTH_LONG);
|
||||
@@ -76,5 +82,9 @@ public class NotificationsFragment extends Fragment implements MessageAdapterTou
|
||||
snackbar.setActionTextColor(Color.YELLOW);
|
||||
snackbar.show();
|
||||
}
|
||||
|
||||
public void updateDeleteSwipeEnabled()
|
||||
{
|
||||
if (touchHelper != null) touchHelper.updateEnabled();
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.view;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.media.AudioAttributes;
|
||||
@@ -10,6 +11,7 @@ import android.media.RingtoneManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -17,6 +19,7 @@ import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.Spinner;
|
||||
@@ -32,6 +35,7 @@ import com.blackforestbytes.simplecloudnotifier.lib.lambda.FI;
|
||||
import com.blackforestbytes.simplecloudnotifier.lib.string.Str;
|
||||
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
|
||||
import com.blackforestbytes.simplecloudnotifier.service.IABService;
|
||||
import com.blackforestbytes.simplecloudnotifier.util.TextChangedListener;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -51,6 +55,7 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
|
||||
private TextView prefUpgradeAccount_msg;
|
||||
private TextView prefUpgradeAccount_info;
|
||||
private Switch prefEnableDeleteSwipe;
|
||||
private EditText prefPreviewLineCount;
|
||||
|
||||
private Switch prefMsgLowEnableSound;
|
||||
private TextView prefMsgLowRingtone_value;
|
||||
@@ -117,6 +122,7 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
|
||||
prefUpgradeAccount_msg = v.findViewById(R.id.prefUpgradeAccount2);
|
||||
prefUpgradeAccount_info = v.findViewById(R.id.prefUpgradeAccount_info);
|
||||
prefEnableDeleteSwipe = v.findViewById(R.id.prefEnableDeleteSwipe);
|
||||
prefPreviewLineCount = v.findViewById(R.id.prefPreviewLineCount);
|
||||
|
||||
prefMsgLowEnableSound = v.findViewById(R.id.prefMsgLowEnableSound);
|
||||
prefMsgLowRingtone_value = v.findViewById(R.id.prefMsgLowRingtone_value);
|
||||
@@ -153,8 +159,13 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
|
||||
prefMsgHighForceVolume = v.findViewById(R.id.prefMsgHighForceVolume);
|
||||
prefMsgHighVolume = v.findViewById(R.id.prefMsgHighVolume);
|
||||
prefMsgHighVolumeTest = v.findViewById(R.id.btnHighVolumeTest);
|
||||
|
||||
ArrayAdapter<Integer> plcsa = new ArrayAdapter<>(v.getContext(), android.R.layout.simple_spinner_item, SCNSettings.CHOOSABLE_CACHE_SIZES);
|
||||
plcsa.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||
prefLocalCacheSize.setAdapter(plcsa);
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
private void updateUI()
|
||||
{
|
||||
SCNSettings s = SCNSettings.inst();
|
||||
@@ -163,15 +174,13 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
|
||||
|
||||
if (prefAppEnabled.isChecked() != s.Enabled) prefAppEnabled.setChecked(s.Enabled);
|
||||
if (prefEnableDeleteSwipe.isChecked() != s.EnableDeleteSwipe) prefEnableDeleteSwipe.setChecked(s.EnableDeleteSwipe);
|
||||
if (!prefPreviewLineCount.getText().toString().equals(Integer.toString(s.PreviewLineCount))) prefPreviewLineCount.setText(Integer.toString(s.PreviewLineCount));
|
||||
|
||||
prefUpgradeAccount.setVisibility( SCNSettings.inst().promode_local ? View.GONE : View.VISIBLE);
|
||||
prefUpgradeAccount_info.setVisibility(SCNSettings.inst().promode_local ? View.GONE : View.VISIBLE);
|
||||
prefUpgradeAccount_msg.setVisibility( SCNSettings.inst().promode_local ? View.VISIBLE : View.GONE );
|
||||
|
||||
ArrayAdapter<Integer> plcsa = new ArrayAdapter<>(c, android.R.layout.simple_spinner_item, SCNSettings.CHOOSABLE_CACHE_SIZES);
|
||||
plcsa.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||
prefLocalCacheSize.setAdapter(plcsa);
|
||||
prefLocalCacheSize.setSelection(getCacheSizeIndex(s.LocalCacheSize));
|
||||
if (prefLocalCacheSize.getSelectedItemPosition() != getCacheSizeIndex(s.LocalCacheSize)) prefLocalCacheSize.setSelection(getCacheSizeIndex(s.LocalCacheSize));
|
||||
|
||||
if (prefMsgLowEnableSound.isChecked() != s.PriorityLow.EnableSound) prefMsgLowEnableSound.setChecked(s.PriorityLow.EnableSound);
|
||||
if (!prefMsgLowRingtone_value.getText().equals(s.PriorityLow.SoundName)) prefMsgLowRingtone_value.setText(s.PriorityLow.SoundName);
|
||||
@@ -219,6 +228,12 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
|
||||
|
||||
prefAppEnabled.setOnCheckedChangeListener((a,b) -> { boolean prev=s.Enabled; s.Enabled=b; saveAndUpdate(); updateEnabled(prev, b); });
|
||||
prefEnableDeleteSwipe.setOnCheckedChangeListener((a,b) -> { s.EnableDeleteSwipe=b; saveAndUpdate(); });
|
||||
prefPreviewLineCount.addTextChangedListener(new TextChangedListener<EditText>(prefPreviewLineCount) {
|
||||
@Override
|
||||
public void onTextChanged(EditText target, Editable ed) {
|
||||
if (!ed.toString().isEmpty()) try { s.PreviewLineCount=Integer.parseInt(ed.toString()); saveAndUpdate(); } catch (Exception e) { /* */ }
|
||||
}
|
||||
});
|
||||
|
||||
prefLocalCacheSize.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener()
|
||||
{
|
||||
@@ -341,7 +356,7 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
|
||||
{
|
||||
SCNSettings.inst().save();
|
||||
updateUI();
|
||||
SCNApp.getMainActivity().adpTabs.tab1.touchHelper.updateEnabled();
|
||||
SCNApp.getMainActivity().adpTabs.tab1.updateDeleteSwipeEnabled();
|
||||
}
|
||||
|
||||
private void onUpgradeAccount()
|
||||
@@ -351,11 +366,11 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
|
||||
|
||||
public void updateProState()
|
||||
{
|
||||
Purchase p = IABService.inst().getPurchaseCached(IABService.IAB_PRO_MODE);
|
||||
boolean pmode = IABService.inst().getPurchaseCachedSimple(IABService.IAB_PRO_MODE);
|
||||
|
||||
if (prefUpgradeAccount != null) prefUpgradeAccount.setVisibility( p != null ? View.GONE : View.VISIBLE);
|
||||
if (prefUpgradeAccount_info != null) prefUpgradeAccount_info.setVisibility(p != null ? View.GONE : View.VISIBLE);
|
||||
if (prefUpgradeAccount_msg != null) prefUpgradeAccount_msg.setVisibility( p != null ? View.VISIBLE : View.GONE );
|
||||
if (prefUpgradeAccount != null) prefUpgradeAccount.setVisibility( pmode ? View.GONE : View.VISIBLE);
|
||||
if (prefUpgradeAccount_info != null) prefUpgradeAccount_info.setVisibility(pmode ? View.GONE : View.VISIBLE);
|
||||
if (prefUpgradeAccount_msg != null) prefUpgradeAccount_msg.setVisibility( pmode ? View.VISIBLE : View.GONE );
|
||||
}
|
||||
|
||||
private int getCacheSizeIndex(int value)
|
||||
|
@@ -0,0 +1,44 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.view.debug;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.blackforestbytes.simplecloudnotifier.model.QueryLog;
|
||||
import com.blackforestbytes.simplecloudnotifier.model.SingleQuery;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import android.widget.ListView;
|
||||
|
||||
import com.blackforestbytes.simplecloudnotifier.R;
|
||||
|
||||
public class QueryLogActivity extends AppCompatActivity
|
||||
{
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_querylog);
|
||||
|
||||
ListView lvMain = findViewById(R.id.lvQueryList);
|
||||
SingleQuery[] arr = QueryLog.instance().get().toArray(new SingleQuery[0]);
|
||||
QueryLogAdapter a = new QueryLogAdapter(this, arr);
|
||||
lvMain.setAdapter(a);
|
||||
|
||||
lvMain.setOnItemClickListener((parent, view, position, id) ->
|
||||
{
|
||||
if (position >= 0 && position < arr.length)
|
||||
{
|
||||
Intent i = new Intent(QueryLogActivity.this, SingleQueryLogActivity.class);
|
||||
Bundle b = new Bundle();
|
||||
arr[position].save(b, "data");
|
||||
i.putExtra("query", b);
|
||||
startActivity(i);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,66 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.view.debug;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import androidx.annotation.NonNull;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.blackforestbytes.simplecloudnotifier.R;
|
||||
import com.blackforestbytes.simplecloudnotifier.model.SingleQuery;
|
||||
|
||||
import org.joda.time.format.DateTimeFormat;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
|
||||
public class QueryLogAdapter extends ArrayAdapter<SingleQuery>
|
||||
{
|
||||
public static DateTimeFormatter UI_FULLTIME_FORMATTER = DateTimeFormat.forPattern("HH:mm:ss");
|
||||
|
||||
public QueryLogAdapter(@NonNull Context context, @NonNull SingleQuery[] objects)
|
||||
{
|
||||
super(context, R.layout.adapter_querylog, objects);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public View getView(int position, View convertView, @NonNull ViewGroup parent)
|
||||
{
|
||||
View v = convertView;
|
||||
|
||||
if (v == null) {
|
||||
LayoutInflater vi;
|
||||
vi = LayoutInflater.from(getContext());
|
||||
v = vi.inflate(R.layout.adapter_querylog, parent, false);
|
||||
}
|
||||
|
||||
SingleQuery p = getItem(position);
|
||||
|
||||
if (p != null)
|
||||
{
|
||||
TextView tt1 = v.findViewById(R.id.list_item_debuglogrow_time);
|
||||
if (tt1 != null) tt1.setText(p.Timestamp.toString(UI_FULLTIME_FORMATTER));
|
||||
if (tt1 != null) tt1.setTextColor(Color.BLACK);
|
||||
|
||||
TextView tt2 = v.findViewById(R.id.list_item_debuglogrow_level);
|
||||
if (tt2 != null) tt2.setText(p.Level.toUIString());
|
||||
if (tt2 != null) tt2.setTextColor(Color.BLACK);
|
||||
|
||||
TextView tt3 = v.findViewById(R.id.list_item_debuglogrow_info);
|
||||
if (tt3 != null) tt3.setText("");
|
||||
if (tt3 != null) tt3.setTextColor(Color.BLUE);
|
||||
|
||||
TextView tt4 = v.findViewById(R.id.list_item_debuglogrow_id);
|
||||
if (tt4 != null) tt4.setText(p.Name);
|
||||
if (tt4 != null) tt4.setTextColor(p.Level.getColor());
|
||||
|
||||
TextView tt5 = v.findViewById(R.id.list_item_debuglogrow_message);
|
||||
if (tt5 != null) tt5.setText(p.ExceptionString.length()> 40 ? p.ExceptionString.substring(0, 40-3)+"..." : p.ExceptionString);
|
||||
if (tt5 != null) tt5.setTextColor(p.Level.getColor());
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
}
|
@@ -0,0 +1,38 @@
|
||||
package com.blackforestbytes.simplecloudnotifier.view.debug;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.os.Bundle;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.blackforestbytes.simplecloudnotifier.R;
|
||||
import com.blackforestbytes.simplecloudnotifier.lib.string.CompactJsonFormatter;
|
||||
import com.blackforestbytes.simplecloudnotifier.model.SingleQuery;
|
||||
|
||||
import org.joda.time.format.DateTimeFormat;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class SingleQueryLogActivity extends AppCompatActivity
|
||||
{
|
||||
@Override
|
||||
@SuppressLint("SetTextI18n")
|
||||
protected void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_singlequerylog);
|
||||
|
||||
SingleQuery q = SingleQuery.load(getIntent().getBundleExtra("query"), "data");
|
||||
|
||||
this.<TextView>findViewById(R.id.tvQL_Timestamp).setText(q.Timestamp.toString(DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")));
|
||||
this.<TextView>findViewById(R.id.tvQL_Level).setText(q.Level.toUIString());
|
||||
this.<TextView>findViewById(R.id.tvQL_Level).setTextColor(q.Level.getColor());
|
||||
this.<TextView>findViewById(R.id.tvQL_Name).setText(q.Name);
|
||||
this.<TextView>findViewById(R.id.tvQL_URL).setText(q.URL.replace("?", "\r\n?").replace("&", "\r\n&"));
|
||||
this.<TextView>findViewById(R.id.tvQL_Response).setText(CompactJsonFormatter.formatJSON(q.Response, 999));
|
||||
this.<TextView>findViewById(R.id.tvQL_ResponseCode).setText(Integer.toString(q.ResponseCode));
|
||||
this.<TextView>findViewById(R.id.tvQL_ExceptionString).setText(q.ExceptionString);
|
||||
}
|
||||
}
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" >
|
||||
<solid android:color="@android:color/white" />
|
||||
<stroke android:width="1dip" android:color="#888888"/>
|
||||
</shape>
|
10
android/app/src/main/res/drawable/ic_share_small.xml
Normal file
10
android/app/src/main/res/drawable/ic_share_small.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<vector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="12sp"
|
||||
android:height="12sp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z"/>
|
||||
</vector>
|
16
android/app/src/main/res/drawable/ic_trash_small.xml
Normal file
16
android/app/src/main/res/drawable/ic_trash_small.xml
Normal file
@@ -0,0 +1,16 @@
|
||||
<vector
|
||||
android:height="12sp"
|
||||
android:width="12sp"
|
||||
android:viewportHeight="53"
|
||||
android:viewportWidth="53"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M42.943,6H33.5V3c0,-1.654 -1.346,-3 -3,-3h-8c-1.654,0 -3,1.346 -3,3v3h-9.443C8.096,6 6.5,7.596 6.5,9.557V14h2h36h2V9.557C46.5,7.596 44.904,6 42.943,6zM31.5,6h-10V3c0,-0.552 0.449,-1 1,-1h8c0.551,0 1,0.448 1,1V6z"/>
|
||||
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M8.5,49.271C8.5,51.327 10.173,53 12.229,53h28.541c2.057,0 3.729,-1.673 3.729,-3.729V16h-36V49.271z"/>
|
||||
|
||||
</vector>
|
@@ -1,31 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout
|
||||
android:id="@+id/layoutRoot"
|
||||
<RelativeLayout android:id="@+id/layoutRoot"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:showIn="@layout/activity_main">
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentTop="true"
|
||||
app:titleTextColor="@color/colorOnPrimary"
|
||||
android:background="?attr/colorPrimary"
|
||||
android:elevation="6dp"
|
||||
android:minHeight="?attr/actionBarSize"
|
||||
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
|
||||
android:minHeight="?attr/actionBarSize" />
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/tab_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/toolbar"
|
||||
app:titleTextColor="@color/colorOnPrimary"
|
||||
app:tabTextColor="@color/colorOnPrimary"
|
||||
app:tabSelectedTextColor="@color/colorSecondary"
|
||||
android:background="?attr/colorPrimary"
|
||||
android:elevation="6dp"
|
||||
android:minHeight="?attr/actionBarSize"
|
||||
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"/>
|
||||
android:minHeight="?attr/actionBarSize" />
|
||||
|
||||
<androidx.viewpager.widget.ViewPager
|
||||
android:id="@+id/pager"
|
||||
|
13
android/app/src/main/res/layout/activity_querylog.xml
Normal file
13
android/app/src/main/res/layout/activity_querylog.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout
|
||||
android:id="@+id/layoutRoot"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ListView
|
||||
android:id="@+id/lvQueryList"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</RelativeLayout>
|
240
android/app/src/main/res/layout/activity_singlequerylog.xml
Normal file
240
android/app/src/main/res/layout/activity_singlequerylog.xml
Normal file
@@ -0,0 +1,240 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:padding="4sp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:textAlignment="center"
|
||||
android:textStyle="bold"
|
||||
android:layout_margin="2dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:text="Server Query" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:layout_margin="2dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="100dp"
|
||||
android:text="Timestamp" />
|
||||
|
||||
<com.blackforestbytes.simplecloudnotifier.util.MaxHeightScrollView
|
||||
app:maxHeightOverride="100"
|
||||
android:background="@drawable/simple_black_border"
|
||||
android:layout_margin="2dip"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fillViewport="true">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvQL_Timestamp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:padding="1dip"
|
||||
android:textIsSelectable="true"
|
||||
android:text="" />
|
||||
|
||||
</com.blackforestbytes.simplecloudnotifier.util.MaxHeightScrollView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:layout_margin="2dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="100dp"
|
||||
android:text="Level" />
|
||||
|
||||
<com.blackforestbytes.simplecloudnotifier.util.MaxHeightScrollView
|
||||
app:maxHeightOverride="100"
|
||||
android:background="@drawable/simple_black_border"
|
||||
android:layout_margin="2dip"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fillViewport="true">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvQL_Level"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:padding="1dip"
|
||||
android:textIsSelectable="true"
|
||||
android:text="" />
|
||||
|
||||
</com.blackforestbytes.simplecloudnotifier.util.MaxHeightScrollView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:layout_margin="2dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="100dp"
|
||||
android:text="Name" />
|
||||
|
||||
<com.blackforestbytes.simplecloudnotifier.util.MaxHeightScrollView
|
||||
app:maxHeightOverride="100"
|
||||
android:layout_margin="2dip"
|
||||
android:background="@drawable/simple_black_border"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fillViewport="true">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvQL_Name"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:padding="1dip"
|
||||
android:textIsSelectable="true"
|
||||
android:text="" />
|
||||
|
||||
</com.blackforestbytes.simplecloudnotifier.util.MaxHeightScrollView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:layout_margin="2dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="100dp"
|
||||
android:text="URL" />
|
||||
|
||||
<com.blackforestbytes.simplecloudnotifier.util.MaxHeightScrollView
|
||||
app:maxHeightOverride="100"
|
||||
android:layout_margin="2dip"
|
||||
android:background="@drawable/simple_black_border"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fillViewport="true">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvQL_URL"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:padding="1dip"
|
||||
android:textIsSelectable="true"
|
||||
android:text=""/>
|
||||
|
||||
</com.blackforestbytes.simplecloudnotifier.util.MaxHeightScrollView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:layout_margin="2dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="100dp"
|
||||
android:text="ResponeCode" />
|
||||
|
||||
<com.blackforestbytes.simplecloudnotifier.util.MaxHeightScrollView
|
||||
app:maxHeightOverride="64"
|
||||
android:layout_margin="2dip"
|
||||
android:background="@drawable/simple_black_border"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fillViewport="true">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvQL_ResponseCode"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:padding="1dip"
|
||||
android:textIsSelectable="true"
|
||||
android:text="" />
|
||||
|
||||
</com.blackforestbytes.simplecloudnotifier.util.MaxHeightScrollView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:layout_margin="2dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="100dp"
|
||||
android:text="Response" />
|
||||
|
||||
|
||||
<com.blackforestbytes.simplecloudnotifier.util.MaxHeightScrollView
|
||||
app:maxHeightOverride="100"
|
||||
android:layout_margin="2dip"
|
||||
android:background="@drawable/simple_black_border"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fillViewport="true">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvQL_Response"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:padding="1dip"
|
||||
android:textIsSelectable="true"
|
||||
android:text="" />
|
||||
|
||||
</com.blackforestbytes.simplecloudnotifier.util.MaxHeightScrollView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:layout_margin="2dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="100dp"
|
||||
android:text="Exception" />
|
||||
|
||||
<com.blackforestbytes.simplecloudnotifier.util.MaxHeightScrollView
|
||||
app:maxHeightOverride="100"
|
||||
android:layout_margin="2dip"
|
||||
android:background="@drawable/simple_black_border"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fillViewport="true">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvQL_ExceptionString"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:padding="1dip"
|
||||
android:textIsSelectable="true"
|
||||
android:text="" />
|
||||
|
||||
</com.blackforestbytes.simplecloudnotifier.util.MaxHeightScrollView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</FrameLayout>
|
50
android/app/src/main/res/layout/adapter_querylog.xml
Normal file
50
android/app/src/main/res/layout/adapter_querylog.xml
Normal file
@@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/list_item_imagerow"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal" >
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="96dp"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/list_item_debuglogrow_time"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/list_item_debuglogrow_level"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/list_item_debuglogrow_info"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/list_item_debuglogrow_id"
|
||||
android:textStyle="bold"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/list_item_debuglogrow_message"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
</LinearLayout>
|
@@ -198,19 +198,27 @@
|
||||
app:layout_constraintBottom_toTopOf="@+id/btnAccountReset"
|
||||
app:layout_constraintTop_toBottomOf="@+id/ic_img_quota" />
|
||||
|
||||
<Button
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnAccountReset"
|
||||
app:cornerRadius="0dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:backgroundTint="#fa315b"
|
||||
android:text="@string/str_reset_account"
|
||||
app:layout_constraintBottom_toTopOf="@+id/btnClearLocalStorage" />
|
||||
|
||||
<Button
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnClearLocalStorage"
|
||||
app:cornerRadius="0dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Clear Messages"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:backgroundTint="#607D8B"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
|
@@ -95,6 +95,46 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="48dp" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:background="#c0c0c0"/>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="48dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvPreviewLineCount"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/str_previewlinecount"
|
||||
android:textColor="#000"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/prefPreviewLineCount"
|
||||
app:layout_constraintHorizontal_bias="0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<EditText
|
||||
android:minWidth="64dp"
|
||||
android:id="@+id/prefPreviewLineCount"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="number"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:importantForAutofill="no"
|
||||
tools:ignore="LabelFor,UnusedAttribute" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
@@ -111,8 +151,10 @@
|
||||
android:gravity="center|center"
|
||||
android:minHeight="48dp">
|
||||
|
||||
<Button
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/prefUpgradeAccount"
|
||||
app:cornerRadius="0dp"
|
||||
android:backgroundTint="#4CAF50"
|
||||
android:text="@string/str_upgrade_account"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
@@ -108,6 +108,43 @@
|
||||
android:paddingTop="3dp"
|
||||
android:contentDescription="@string/desc_priority_icon" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnShare"
|
||||
style="@style/Widget.MaterialComponents.Button.Icon"
|
||||
app:cornerRadius="0dp"
|
||||
app:icon="@drawable/ic_share_small"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="25dp"
|
||||
android:layout_marginEnd="8sp"
|
||||
android:insetTop="0dp"
|
||||
android:insetBottom="0dp"
|
||||
android:insetLeft="0dp"
|
||||
android:insetRight="0dp"
|
||||
android:textSize="12sp"
|
||||
card_view:layout_constraintTop_toBottomOf="@id/tvMessage"
|
||||
app:layout_constraintRight_toLeftOf="@id/btnDelete"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:backgroundTint="#03A9F4"
|
||||
android:text="Share" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnDelete"
|
||||
style="@style/Widget.MaterialComponents.Button.Icon"
|
||||
app:cornerRadius="0dp"
|
||||
app:icon="@drawable/ic_trash_small"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="25dp"
|
||||
android:insetTop="0dp"
|
||||
android:insetBottom="0dp"
|
||||
android:insetLeft="0dp"
|
||||
android:insetRight="0dp"
|
||||
android:textSize="12sp"
|
||||
card_view:layout_constraintTop_toBottomOf="@id/tvMessage"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:backgroundTint="#F44336"
|
||||
android:text="Delete"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
8
android/app/src/main/res/values/attrs.xml
Normal file
8
android/app/src/main/res/values/attrs.xml
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<declare-styleable name="MaxHeightScrollView">
|
||||
<attr name="maxHeightOverride" format="integer" />
|
||||
</declare-styleable>
|
||||
|
||||
</resources>
|
@@ -6,6 +6,9 @@
|
||||
<color name="colorHeader">#3F51B5</color>
|
||||
<color name="colorHeaderForeground">#FFFFFF</color>
|
||||
|
||||
<color name="colorOnPrimary">#ecf0f1</color>
|
||||
<color name="colorSecondary">#FF5722</color>
|
||||
|
||||
<color name="colorBlack">#000</color>
|
||||
|
||||
<color name="bg_row_background">#fa315b</color>
|
||||
|
@@ -6,4 +6,5 @@
|
||||
<dimen name="padd_10">10dp</dimen>
|
||||
<dimen name="ic_delete">30dp</dimen>
|
||||
<dimen name="thumbnail">90dp</dimen>
|
||||
<dimen name="fab_margin">16dp</dimen>
|
||||
</resources>
|
@@ -30,9 +30,11 @@
|
||||
<string name="str_enable_vibration">Enable notification vibration</string>
|
||||
<string name="str_upgrade_account">Upgrade account</string>
|
||||
<string name="str_deleteswipe">Delete messages by swiping left</string>
|
||||
<string name="str_previewlinecount">Number of visibile lines in collapsed messages</string>
|
||||
<string name="str_promode">Thank you for supporting the app and using the pro mode</string>
|
||||
<string name="str_promode_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>
|
||||
<string name="title_activity_query_log">QueryLogActivity</string>
|
||||
</resources>
|
||||
|
@@ -1,13 +1,20 @@
|
||||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
<style name="AppTheme" parent="Theme.MaterialComponents.Light.NoActionBar">
|
||||
<!-- your app branding color for the app bar -->
|
||||
<item name="colorPrimary">#3F51B5</item>
|
||||
|
||||
<!-- darker variant for the status bar and contextual app bars -->
|
||||
<item name="colorPrimaryDark">#303F9F</item>
|
||||
|
||||
<!-- theme UI controls like checkboxes and text fields -->
|
||||
<item name="colorAccent">#FF4081</item>
|
||||
<item name="colorAccent">#FF5722</item>
|
||||
<item name="colorSecondary">#FF5722</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.MaterialComponents.Dark.ActionBar" />
|
||||
|
||||
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.MaterialComponents.Light" />
|
||||
|
||||
</resources>
|
||||
|
@@ -1,3 +1,3 @@
|
||||
#Sun Nov 18 03:24:23 CET 2018
|
||||
VERSION_NAME=0.0.13
|
||||
VERSION_CODE=13
|
||||
#Fri Dec 14 19:31:54 CET 2018
|
||||
VERSION_NAME=1.5.0
|
||||
VERSION_CODE=19
|
||||
|
@@ -21,6 +21,7 @@ user_key="????????????????????????????????????????????????????????????????"
|
||||
|
||||
title=$1
|
||||
content=""
|
||||
sendtime=$(date +%s)
|
||||
|
||||
if [ "$#" -gt 1 ]; then
|
||||
content=$2
|
||||
@@ -37,7 +38,7 @@ 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 "user_id=$user_id" -d "user_key=$user_key" -d "title=$title" -d "timestamp=$sendtime" \
|
||||
-d "content=$content" -d "priority=$priority" -d "msg_id=$usr_msg_id" \
|
||||
https://scn.blackforestbytes.com/send.php)
|
||||
|
||||
|
@@ -45,7 +45,7 @@ api_return(200,
|
||||
'body' => $msg['content'],
|
||||
'trimmed' => false,
|
||||
'priority' => $msg['priority'],
|
||||
'timestamp' => strtotime($msg['timestamp']),
|
||||
'timestamp' => $msg['sendtime'],
|
||||
'usr_msg_id' => $msg['usr_message_id'],
|
||||
'scn_msg_id' => $msg['scn_message_id'],
|
||||
],
|
||||
|
@@ -16,6 +16,7 @@ class ERR
|
||||
const TITLE_TOO_LONG = 1202;
|
||||
const CONTENT_TOO_LONG = 1203;
|
||||
const USR_MSG_ID_TOO_LONG = 1204;
|
||||
const TIMESTAMP_OUT_OF_RANGE = 1205;
|
||||
|
||||
const USER_NOT_FOUND = 1301;
|
||||
const USER_AUTH_FAILED = 1302;
|
||||
|
@@ -28,7 +28,7 @@ if ($data['user_key'] !== $user_key) die(json_encode(['success' => false, 'errid
|
||||
|
||||
//-------------------
|
||||
|
||||
$stmt = $pdo->prepare('SELECT * FROM messages WHERE ack=0 AND sender_user_id=:uid ORDER BY `timestamp` DESC LIMIT 16');
|
||||
$stmt = $pdo->prepare('SELECT * FROM messages WHERE ack=0 AND sender_user_id=:uid ORDER BY `timestamp_real` DESC LIMIT 16');
|
||||
$stmt->execute(['uid' => $user_id]);
|
||||
$nonacks_sql = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
@@ -41,7 +41,7 @@ foreach ($nonacks_sql as $nack)
|
||||
'title' => $nack['title'],
|
||||
'body' => $nack['content'],
|
||||
'priority' => $nack['priority'],
|
||||
'timestamp' => strtotime($nack['timestamp']),
|
||||
'timestamp' => $nack['sendtime'],
|
||||
'usr_msg_id' => $nack['usr_message_id'],
|
||||
'scn_msg_id' => $nack['scn_message_id'],
|
||||
];
|
||||
|
@@ -23,12 +23,13 @@ CREATE TABLE `messages`
|
||||
`scn_message_id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||
`sender_user_id` INT(11) NOT NULL,
|
||||
|
||||
`timestamp` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`timestamp_real` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`ack` BIT NOT NULL DEFAULT 0,
|
||||
|
||||
`title` VARCHAR(256) NOT NULL,
|
||||
`content` VARCHAR(12288) NULL,
|
||||
`priority` INT(11) NOT NULL,
|
||||
`sendtime` BIGINT UNSIGNED NOT NULL,
|
||||
|
||||
`fcm_message_id` VARCHAR(256) NULL,
|
||||
`usr_message_id` VARCHAR(256) NULL,
|
||||
|
@@ -198,6 +198,22 @@ if (file_exists('/var/www/openwebanalytics/owa_php.php'))
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<h2>Custom Time</h2>
|
||||
<div class="section">
|
||||
<p>
|
||||
You can modify the displayed timestamp of a message by sending the <code>timestamp</code> parameter. The format must be a valid UNIX timestamp (elapsed seconds since 1970-01-01 GMT)
|
||||
</p>
|
||||
<p>
|
||||
The custom timestamp must be within 48 hours of the current time. This parameter is only intended to supply a more precise value in case the message sending was delayed.
|
||||
</p>
|
||||
<pre>curl \
|
||||
--data "user_id={userid}" \
|
||||
--data "user_key={userkey}" \
|
||||
--data "title={message_title}" \
|
||||
--data "timestamp={unix_timestamp}" \
|
||||
https://scn.blackforestbytes.com/send.php</pre>
|
||||
</div>
|
||||
|
||||
<h2>Bash script example</h2>
|
||||
<div class="section">
|
||||
<p>
|
||||
@@ -227,6 +243,7 @@ user_key=<span style="color:#2a00ff; ">"????????????????????????????????????????
|
||||
|
||||
title=$1
|
||||
content=<span style="color:#2a00ff; ">""</span>
|
||||
sendtime=$(date +%s)
|
||||
|
||||
<span style="color:#7f0055; font-weight:bold; ">if</span> [ <span style="color:#2a00ff; ">"$#"</span> -gt 1 ]; <span style="color:#7f0055; font-weight:bold; ">then</span>
|
||||
content=$2
|
||||
@@ -243,7 +260,7 @@ 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; ">user_id</span><span style="color:#2a00ff; ">=</span><span style="color:#2a00ff; ">$user_id</span><span style="color:#2a00ff; ">"</span> -d <span style="color:#2a00ff; ">"</span><span style="color:#2a00ff; ">user_key</span><span style="color:#2a00ff; ">=</span><span style="color:#2a00ff; ">$user_key</span><span style="color:#2a00ff; ">"</span> -d <span style="color:#2a00ff; ">"</span><span style="color:#2a00ff; ">title</span><span style="color:#2a00ff; ">=</span><span style="color:#2a00ff; ">$title</span><span style="color:#2a00ff; ">"</span> -d <span style="color:#2a00ff; ">"</span><span style="color:#2a00ff; ">timestamp</span><span style="color:#2a00ff; ">=</span><span style="color:#2a00ff; ">$sendtime</span><span style="color:#2a00ff; ">"</span> <span style="color:#2a00ff; ">\</span>
|
||||
-d <span style="color:#2a00ff; ">"</span><span style="color:#2a00ff; ">content</span><span style="color:#2a00ff; ">=</span><span style="color:#2a00ff; ">$content</span><span style="color:#2a00ff; ">"</span> -d <span style="color:#2a00ff; ">"</span><span style="color:#2a00ff; ">priority</span><span style="color:#2a00ff; ">=</span><span style="color:#2a00ff; ">$priority</span><span style="color:#2a00ff; ">"</span> -d <span style="color:#2a00ff; ">"</span><span style="color:#2a00ff; ">msg_id</span><span style="color:#2a00ff; ">=</span><span style="color:#2a00ff; ">$usr_msg_id</span><span style="color:#2a00ff; ">"</span> <span style="color:#2a00ff; ">\</span>
|
||||
https:<span style="color:#3f3fbf; ">/</span><span style="color:#3f3fbf; ">/scn.blackforestbytes.com/send.php</span>)
|
||||
|
||||
|
11
web/send.php
11
web/send.php
@@ -5,8 +5,6 @@ include_once 'api/model.php';
|
||||
try
|
||||
{
|
||||
|
||||
//------------------------------------------------------------------
|
||||
//sleep(1);
|
||||
//------------------------------------------------------------------
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') api_return(400, ['success' => false, 'error' => ERR::REQ_METHOD, 'errhighlight' => -1, 'message' => 'Invalid request method (must be POST)']);
|
||||
@@ -19,16 +17,18 @@ try
|
||||
|
||||
//------------------------------------------------------------------
|
||||
|
||||
|
||||
$user_id = $INPUT['user_id'];
|
||||
$user_key = $INPUT['user_key'];
|
||||
$message = $INPUT['title'];
|
||||
$content = isset($INPUT['content']) ? $INPUT['content'] : '';
|
||||
$priority = isset($INPUT['priority']) ? $INPUT['priority'] : '1';
|
||||
$usrmsgid = isset($INPUT['msg_id']) ? $INPUT['msg_id'] : null;
|
||||
$time = isset($INPUT['timestamp']) ? $INPUT['timestamp'] : time();
|
||||
|
||||
//------------------------------------------------------------------
|
||||
|
||||
if (abs($time - time()) > 60*60*24*2) api_return(400, ['success' => false, 'error' => ERR::TIMESTAMP_OUT_OF_RANGE, 'errhighlight' => -1, 'message' => 'The timestamp mus be within 24 hours of now()']);
|
||||
|
||||
if ($priority !== '0' && $priority !== '1' && $priority !== '2') api_return(400, ['success' => false, 'error' => ERR::INVALID_PRIO, 'errhighlight' => 105, 'message' => 'Invalid priority']);
|
||||
|
||||
if (strlen(trim($message)) == 0) api_return(400, ['success' => false, 'error' => ERR::NO_TITLE, 'errhighlight' => 103, 'message' => 'No title specified']);
|
||||
@@ -95,13 +95,14 @@ 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 = $pdo->prepare('INSERT INTO messages (sender_user_id, title, content, priority, sendtime, fcm_message_id, usr_message_id) VALUES (:suid, :t, :c, :p, :ts, :fmid, :umid)');
|
||||
$stmt->execute(
|
||||
[
|
||||
'suid' => $user_id,
|
||||
't' => $message,
|
||||
'c' => $content,
|
||||
'p' => $priority,
|
||||
'ts' => $time,
|
||||
'fmid' => null,
|
||||
'umid' => $usrmsgid,
|
||||
]);
|
||||
@@ -125,7 +126,7 @@ try
|
||||
'body' => str_limit($content, 1900),
|
||||
'trimmed' => (strlen($content) > 1900),
|
||||
'priority' => $priority,
|
||||
'timestamp' => time(),
|
||||
'timestamp' => $time,
|
||||
'usr_msg_id' => $usrmsgid,
|
||||
'scn_msg_id' => $scn_msg_id,
|
||||
]
|
||||
|
Reference in New Issue
Block a user