19 Commits

Author SHA1 Message Date
ce641bf7d2 Update dependencies 2020-11-03 14:41:20 +01:00
f1c7314dca better message for ERR::CONTENT_TOO_LONG 2020-08-03 13:25:49 +02:00
019408dc6d use LONGTEXT for content column 2020-04-21 10:45:03 +02:00
jenkins
cdba3540c2 [Jenkins] Increment version 2020-03-05 15:29:40 +00:00
885d997ff3 fix exceptions in register.php 2020-03-05 16:26:58 +01:00
5118ab3cbf Fix notifications not click-able 2020-03-05 16:20:25 +01:00
jenkins
2812377f5c [Jenkins] Increment version 2020-01-05 22:19:08 +00:00
93b40a9c7f skip version code 20 2020-01-05 23:18:20 +01:00
b95ddcc811 backup/export und import 2020-01-05 22:59:57 +01:00
90ba3c1134 fix api urls 2020-01-05 22:33:58 +01:00
05174958b2 Added php example 2019-06-30 21:46:28 +02:00
jenkins
24be9b2013 [Jenkins] Increment version 2018-12-14 22:09:01 +01:00
29ce4b727c More fixes for paid mode 2018-12-14 22:07:43 +01:00
jenkins
f178019ffe [Jenkins] Increment version 2018-12-14 19:32:22 +01:00
0a1b948042 Stupid bug -.- 2018-12-14 19:23:36 +01:00
jenkins
74cbfb235e [Jenkins] Increment version 2018-12-14 18:38:12 +01:00
63c4104a89 Add recieved messages to ComLog 2018-12-14 18:35:51 +01:00
2597287b1e Fixed wrong pro-mode reset 2018-12-14 18:30:03 +01:00
jenkins
316156f0f0 [Jenkins] Increment version 2018-12-11 13:55:38 +01:00
27 changed files with 711 additions and 243 deletions

View File

@@ -1,85 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="WizardSettings">
<option name="children">
<map>
<entry key="imageWizard">
<value>
<PersistentState>
<option name="children">
<map>
<entry key="imageAssetPanel">
<value>
<PersistentState>
<option name="children">
<map>
<entry key="launcherLegacy">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="assetType" value="IMAGE" />
<entry key="cropped" value="true" />
<entry key="iconShape" value="NONE" />
<entry key="imageAsset" value="F:\Eigene Dateien\Dropbox\Programming\Java\AndroidStudioProjects\SimpleCloudNotifier\data\icon_512_nobox.png" />
<entry key="outputName" value="ic_notification_full" />
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="notification">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="assetType" value="IMAGE" />
<entry key="imageAsset" value="F:\Eigene Dateien\Dropbox\Programming\Java\AndroidStudioProjects\SimpleCloudNotifier\data\icon_512_transparent.png" />
<entry key="outputName" value="ic_notification_white" />
</map>
</option>
</PersistentState>
</value>
</entry>
</map>
</option>
<option name="values">
<map>
<entry key="outputIconType" value="LAUNCHER_LEGACY" />
</map>
</option>
</PersistentState>
</value>
</entry>
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="vectorWizard">
<value>
<PersistentState>
<option name="children">
<map>
<entry key="vectorAssetStep">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="assetSourceType" value="FILE" />
<entry key="outputName" value="ic_share" />
<entry key="sourceFile" value="C:\Users\Mike\Downloads\baseline-share-24px.svg" />
</map>
</option>
</PersistentState>
</value>
</entry>
</map>
</option>
</PersistentState>
</value>
</entry>
</map>
</option>
</component>
</project>

View File

@@ -1,29 +1,113 @@
<component name="ProjectCodeStyleConfiguration"> <component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173"> <code_scheme name="Project" version="173">
<Objective-C-extensions> <codeStyleSettings language="XML">
<file> <arrangement>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" /> <rules>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" /> <section>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" /> <rule>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" /> <match>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" /> <AND>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" /> <NAME>xmlns:android</NAME>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" /> <XML_ATTRIBUTE />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" /> <XML_NAMESPACE>^$</XML_NAMESPACE>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" /> </AND>
</file> </match>
<class> </rule>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" /> </section>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" /> <section>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" /> <rule>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" /> <match>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" /> <AND>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" /> <NAME>xmlns:.*</NAME>
</class> <XML_ATTRIBUTE />
<extensions> <XML_NAMESPACE>^$</XML_NAMESPACE>
<pair source="cpp" header="h" fileNamingConvention="NONE" /> </AND>
<pair source="c" header="h" fileNamingConvention="NONE" /> </match>
</extensions> <order>BY_NAME</order>
</Objective-C-extensions> </rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>ANDROID_ATTRIBUTE_ORDER</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
</code_scheme> </code_scheme>
</component> </component>

40
android/.idea/jarRepositories.xml generated Normal file
View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="maven2" />
<option name="name" value="maven2" />
<option name="url" value="https://dl.bintray.com/gericop/maven" />
</remote-repository>
<remote-repository>
<option name="id" value="maven3" />
<option name="name" value="maven3" />
<option name="url" value="https://maven.google.com" />
</remote-repository>
<remote-repository>
<option name="id" value="BintrayJCenter" />
<option name="name" value="BintrayJCenter" />
<option name="url" value="https://jcenter.bintray.com/" />
</remote-repository>
<remote-repository>
<option name="id" value="maven" />
<option name="name" value="maven" />
<option name="url" value="https://jitpack.io" />
</remote-repository>
<remote-repository>
<option name="id" value="Google" />
<option name="name" value="Google" />
<option name="url" value="https://dl.google.com/dl/android/maven2/" />
</remote-repository>
</component>
</project>

View File

@@ -1,7 +1,7 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
android { android {
compileSdkVersion 28 compileSdkVersion 30
def versionPropsFile = file('version.properties') def versionPropsFile = file('version.properties')
def vNumber def vNumber
@@ -16,7 +16,7 @@ android {
defaultConfig { defaultConfig {
applicationId "com.blackforestbytes.simplecloudnotifier" applicationId "com.blackforestbytes.simplecloudnotifier"
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 28 targetSdkVersion 30
versionCode vNumber versionCode vNumber
versionName vName versionName vName
} }
@@ -35,30 +35,33 @@ android {
dependencies { dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar']) implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.0.2' implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.recyclerview:recyclerview:1.0.0' implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation 'androidx.recyclerview:recyclerview:1.0.0' implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0' implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
implementation 'com.google.android.material:material:1.0.0' implementation 'com.google.android.material:material:1.2.1'
implementation 'com.google.firebase:firebase-core:16.0.6' implementation 'com.google.firebase:firebase-core:18.0.0'
implementation 'com.google.firebase:firebase-messaging:17.3.4' implementation 'com.google.firebase:firebase-messaging:21.0.0'
implementation 'com.google.android.gms:play-services-ads:17.1.2' implementation 'com.google.android.gms:play-services-ads:19.5.0'
implementation 'com.android.billingclient:billing:1.2' implementation 'com.android.billingclient:billing:3.0.1'
implementation 'com.squareup.okhttp3:okhttp:3.10.0' implementation 'com.squareup.okhttp3:okhttp:4.9.0'
implementation 'com.github.kenglxn.QRGen:android:2.5.0' implementation 'com.github.kenglxn.QRGen:android:2.5.0'
implementation "com.github.DeweyReed:UltimateMusicPicker:2.0.0" implementation "com.github.DeweyReed:UltimateMusicPicker:2.0.0"
implementation 'com.github.duanhong169:colorpicker:1.1.5' implementation 'com.github.duanhong169:colorpicker:1.1.5'
implementation 'net.danlew:android.joda:2.9.9.2' implementation 'net.danlew:android.joda:2.10.7.1'
} }
apply plugin: 'com.google.gms.google-services' apply plugin: 'com.google.gms.google-services'
task updateVersion << { tasks.register("updateVersion") {
group = 'Custom'
doLast {
def lastTag = ['git', 'describe', "--abbrev=0", "--tags"].execute().text.trim() def lastTag = ['git', 'describe', "--abbrev=0", "--tags"].execute().text.trim()
def versionPropsFile = file('version.properties') def versionPropsFile = file('version.properties')
@@ -109,4 +112,5 @@ task updateVersion << {
versionPropsFile.newWriter().withCloseable { w -> versionProps.store(w, null) } versionPropsFile.newWriter().withCloseable { w -> versionProps.store(w, null) }
} }
}
} }

View File

@@ -24,6 +24,15 @@
</intent-filter> </intent-filter>
</activity> </activity>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.blackforestbytes.simplecloudnotifier.fileprovider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/filepaths" />
</provider>
<meta-data <meta-data
android:name="com.google.firebase.messaging.default_notification_icon" android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@drawable/icon" /> android:resource="@drawable/icon" />
@@ -53,7 +62,7 @@
android:name=".view.debug.QueryLogActivity" android:name=".view.debug.QueryLogActivity"
android:label="@string/title_activity_query_log" android:label="@string/title_activity_query_log"
android:theme="@style/AppTheme" /> android:theme="@style/AppTheme" />
<activity android:name=".view.debug.SingleQueryLogActivity"></activity> <activity android:name=".view.debug.SingleQueryLogActivity" />
</application> </application>
</manifest> </manifest>

View File

@@ -35,6 +35,11 @@ public class CMessageList
} }
private CMessageList() private CMessageList()
{
reloadPrefs();
}
public void reloadPrefs()
{ {
synchronized (msg_lock) synchronized (msg_lock)
{ {

View File

@@ -15,9 +15,9 @@ public class QueryLog
private final static int MAX_HISTORY_SIZE = 192; private final static int MAX_HISTORY_SIZE = 192;
private static QueryLog _instance; private static QueryLog _instance;
public static QueryLog instance() { if (_instance == null) synchronized (QueryLog.class) { if (_instance == null) _instance = new QueryLog(); } return _instance; } public static QueryLog inst() { if (_instance == null) synchronized (QueryLog.class) { if (_instance == null) _instance = new QueryLog(); } return _instance; }
private QueryLog(){ load(); } private QueryLog(){ reloadPrefs(); }
private final List<SingleQuery> history = new ArrayList<>(); private final List<SingleQuery> history = new ArrayList<>();
@@ -50,7 +50,7 @@ public class QueryLog
e.apply(); e.apply();
} }
public synchronized void load() public synchronized void reloadPrefs()
{ {
try try
{ {

View File

@@ -6,8 +6,8 @@ import android.content.SharedPreferences;
import android.util.Log; import android.util.Log;
import android.view.View; import android.view.View;
import com.android.billingclient.api.Purchase;
import com.blackforestbytes.simplecloudnotifier.SCNApp; import com.blackforestbytes.simplecloudnotifier.SCNApp;
import com.blackforestbytes.simplecloudnotifier.lib.datatypes.Tuple3;
import com.blackforestbytes.simplecloudnotifier.lib.string.Str; import com.blackforestbytes.simplecloudnotifier.lib.string.Str;
import com.blackforestbytes.simplecloudnotifier.service.IABService; import com.blackforestbytes.simplecloudnotifier.service.IABService;
import com.google.firebase.iid.FirebaseInstanceId; import com.google.firebase.iid.FirebaseInstanceId;
@@ -62,6 +62,11 @@ public class SCNSettings
// ------------------------------------------------------------ // ------------------------------------------------------------
public SCNSettings() public SCNSettings()
{
reloadPrefs();
}
public void reloadPrefs()
{ {
SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("Config", Context.MODE_PRIVATE); SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("Config", Context.MODE_PRIVATE);
@@ -122,6 +127,9 @@ public class SCNSettings
e.putString( "user_key", user_key); e.putString( "user_key", user_key);
e.putString( "fcm_token_local", fcm_token_local); e.putString( "fcm_token_local", fcm_token_local);
e.putString( "fcm_token_server", fcm_token_server); e.putString( "fcm_token_server", fcm_token_server);
e.putBoolean("promode_local", promode_local);
e.putBoolean("promode_server", promode_server);
e.putString( "promode_token", promode_token);
e.putBoolean("app_enabled", Enabled); e.putBoolean("app_enabled", Enabled);
e.putInt( "local_cache_size", LocalCacheSize); e.putInt( "local_cache_size", LocalCacheSize);
@@ -243,14 +251,17 @@ public class SCNSettings
public void updateProState(View loader) public void updateProState(View loader)
{ {
Purchase purch = IABService.inst().getPurchaseCached(IABService.IAB_PRO_MODE); Tuple3<Boolean, Boolean, String> state = IABService.inst().getPurchaseCachedExtended(IABService.IAB_PRO_MODE);
boolean promode_real = (purch != null); if (!state.Item2) return; // not initialized
boolean promode_real = state.Item1;
if (promode_real != promode_local || promode_real != promode_server) if (promode_real != promode_local || promode_real != promode_server)
{ {
promode_local = promode_real; promode_local = promode_real;
promode_token = promode_real ? state.Item3 : "";
save();
promode_token = promode_real ? purch.getPurchaseToken() : "";
updateProStateOnServer(loader); updateProStateOnServer(loader);
} }
} }

View File

@@ -68,6 +68,7 @@ public class ServerCommunication
if (!json_bool(json, "success")) if (!json_bool(json, "success"))
{ {
SCNApp.showToast(json_str(json, "message"), 4000); SCNApp.showToast(json_str(json, "message"), 4000);
handleNonSuccess("register", call, response, r);
return; return;
} }
@@ -105,7 +106,7 @@ public class ServerCommunication
try try
{ {
Request request = new Request.Builder() Request request = new Request.Builder()
.url(BASE_URL + "updateFCMToken.php?user_id="+id+"&user_key="+key+"&fcm_token="+token) .url(BASE_URL + "update.php?user_id="+id+"&user_key="+key+"&fcm_token="+token)
.build(); .build();
client.newCall(request).enqueue(new Callback() client.newCall(request).enqueue(new Callback()
@@ -134,6 +135,7 @@ public class ServerCommunication
if (!json_bool(json, "success")) if (!json_bool(json, "success"))
{ {
SCNApp.showToast(json_str(json, "message"), 4000); SCNApp.showToast(json_str(json, "message"), 4000);
handleNonSuccess("update<1>", call, response, r);
return; return;
} }
@@ -172,7 +174,7 @@ public class ServerCommunication
try try
{ {
Request request = new Request.Builder() Request request = new Request.Builder()
.url(BASE_URL + "updateFCMToken.php?user_id=" + id + "&user_key=" + key) .url(BASE_URL + "update.php?user_id=" + id + "&user_key=" + key)
.build(); .build();
client.newCall(request).enqueue(new Callback() { client.newCall(request).enqueue(new Callback() {
@@ -200,6 +202,7 @@ public class ServerCommunication
if (!json_bool(json, "success")) { if (!json_bool(json, "success")) {
SCNApp.showToast(json_str(json, "message"), 4000); SCNApp.showToast(json_str(json, "message"), 4000);
handleNonSuccess("update<2>", call, response, r);
return; return;
} }
@@ -269,6 +272,7 @@ public class ServerCommunication
if (!json_bool(json, "success")) if (!json_bool(json, "success"))
{ {
SCNApp.showToast(json_str(json, "message"), 4000); SCNApp.showToast(json_str(json, "message"), 4000);
handleNonSuccess("info", call, response, r);
int errid = json.optInt("errid", 0); int errid = json.optInt("errid", 0);
@@ -356,6 +360,7 @@ public class ServerCommunication
if (!json_bool(json, "success")) if (!json_bool(json, "success"))
{ {
SCNApp.showToast(json_str(json, "message"), 4000); SCNApp.showToast(json_str(json, "message"), 4000);
handleNonSuccess("requery", call, response, r);
return; return;
} }
@@ -420,8 +425,7 @@ public class ServerCommunication
String r = Str.Empty; String r = Str.Empty;
try (ResponseBody responseBody = response.body()) try (ResponseBody responseBody = response.body())
{ {
if (!response.isSuccessful()) if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
throw new IOException("Unexpected code " + response);
if (responseBody == null) throw new IOException("No response"); if (responseBody == null) throw new IOException("No response");
r = responseBody.string(); r = responseBody.string();
@@ -431,6 +435,7 @@ public class ServerCommunication
if (!json_bool(json, "success")) { if (!json_bool(json, "success")) {
SCNApp.showToast(json_str(json, "message"), 4000); SCNApp.showToast(json_str(json, "message"), 4000);
handleNonSuccess("upgrade", call, response, r);
return; return;
} }
@@ -492,7 +497,11 @@ public class ServerCommunication
JSONObject json = (JSONObject) new JSONTokener(r).nextValue(); JSONObject json = (JSONObject) new JSONTokener(r).nextValue();
if (!json_bool(json, "success")) SCNApp.showToast(json_str(json, "message"), 4000); if (!json_bool(json, "success"))
{
SCNApp.showToast(json_str(json, "message"), 4000);
handleNonSuccess("ack", call, response, r);
}
handleSuccess("ack", call, response, r); handleSuccess("ack", call, response, r);
} }
@@ -542,6 +551,7 @@ public class ServerCommunication
if (!json_bool(json, "success")) if (!json_bool(json, "success"))
{ {
SCNApp.showToast(json_str(json, "message"), 4000); SCNApp.showToast(json_str(json, "message"), 4000);
handleNonSuccess("expand", call, response, r);
return; return;
} }
@@ -613,7 +623,29 @@ public class ServerCommunication
LogLevel l = LogLevel.INFO; LogLevel l = LogLevel.INFO;
SingleQuery q = new SingleQuery(l, i, s, u, r, rc, "SUCCESS"); SingleQuery q = new SingleQuery(l, i, s, u, r, rc, "SUCCESS");
QueryLog.instance().add(q); QueryLog.inst().add(q);
}
catch (Exception e2)
{
Log.e("SC:HandleSuccess", e2.toString());
}
}
private static void handleNonSuccess(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.WARN;
SingleQuery q = new SingleQuery(l, i, s, u, r, rc, "NON-SUCCESS");
QueryLog.inst().add(q);
} }
catch (Exception e2) catch (Exception e2)
{ {
@@ -644,7 +676,7 @@ public class ServerCommunication
LogLevel l = isio?LogLevel.WARN:LogLevel.ERROR; LogLevel l = isio?LogLevel.WARN:LogLevel.ERROR;
SingleQuery q = new SingleQuery(l, i, s, u, r, rc, e.toString()); SingleQuery q = new SingleQuery(l, i, s, u, r, rc, e.toString());
QueryLog.instance().add(q); QueryLog.inst().add(q);
} }
catch (Exception e2) catch (Exception e2)
{ {

View File

@@ -4,16 +4,21 @@ import android.util.Log;
import android.widget.Toast; import android.widget.Toast;
import com.blackforestbytes.simplecloudnotifier.SCNApp; import com.blackforestbytes.simplecloudnotifier.SCNApp;
import com.blackforestbytes.simplecloudnotifier.lib.datatypes.Tuple4; import com.blackforestbytes.simplecloudnotifier.lib.string.Str;
import com.blackforestbytes.simplecloudnotifier.lib.datatypes.Tuple5;
import com.blackforestbytes.simplecloudnotifier.model.CMessage; import com.blackforestbytes.simplecloudnotifier.model.CMessage;
import com.blackforestbytes.simplecloudnotifier.model.CMessageList; import com.blackforestbytes.simplecloudnotifier.model.CMessageList;
import com.blackforestbytes.simplecloudnotifier.model.LogLevel;
import com.blackforestbytes.simplecloudnotifier.model.PriorityEnum; import com.blackforestbytes.simplecloudnotifier.model.PriorityEnum;
import com.blackforestbytes.simplecloudnotifier.model.QueryLog;
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings; import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
import com.blackforestbytes.simplecloudnotifier.model.ServerCommunication; import com.blackforestbytes.simplecloudnotifier.model.ServerCommunication;
import com.blackforestbytes.simplecloudnotifier.model.SingleQuery;
import com.google.firebase.messaging.FirebaseMessagingService; import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage; import com.google.firebase.messaging.RemoteMessage;
import org.joda.time.Instant;
import org.json.JSONObject;
public class FBMService extends FirebaseMessagingService public class FBMService extends FirebaseMessagingService
{ {
@Override @Override
@@ -42,6 +47,10 @@ public class FBMService extends FirebaseMessagingService
long scn_id = Long.parseLong(remoteMessage.getData().get("scn_msg_id")); long scn_id = Long.parseLong(remoteMessage.getData().get("scn_msg_id"));
boolean trimmed = Boolean.parseBoolean(remoteMessage.getData().get("trimmed")); 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.inst().add(q);
if (trimmed) 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)); 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));

View File

@@ -2,23 +2,37 @@ package com.blackforestbytes.simplecloudnotifier.service;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences;
import android.util.Log; import android.util.Log;
import android.widget.Toast; import android.widget.Toast;
import com.android.billingclient.api.BillingClient; import com.android.billingclient.api.BillingClient;
import com.android.billingclient.api.BillingClientStateListener; import com.android.billingclient.api.BillingClientStateListener;
import com.android.billingclient.api.BillingFlowParams; import com.android.billingclient.api.BillingFlowParams;
import com.android.billingclient.api.BillingResult;
import com.android.billingclient.api.Purchase; import com.android.billingclient.api.Purchase;
import com.android.billingclient.api.PurchasesUpdatedListener; import com.android.billingclient.api.PurchasesUpdatedListener;
import com.android.billingclient.api.SkuDetails;
import com.android.billingclient.api.SkuDetailsParams;
import com.android.billingclient.api.SkuDetailsResponseListener;
import com.blackforestbytes.simplecloudnotifier.SCNApp; import com.blackforestbytes.simplecloudnotifier.SCNApp;
import com.blackforestbytes.simplecloudnotifier.lib.datatypes.Tuple2;
import com.blackforestbytes.simplecloudnotifier.lib.datatypes.Tuple3;
import com.blackforestbytes.simplecloudnotifier.lib.lambda.Func0to0; import com.blackforestbytes.simplecloudnotifier.lib.lambda.Func0to0;
import com.blackforestbytes.simplecloudnotifier.lib.string.Str; import com.blackforestbytes.simplecloudnotifier.lib.string.Str;
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings; import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
import com.blackforestbytes.simplecloudnotifier.view.MainActivity; import com.blackforestbytes.simplecloudnotifier.view.MainActivity;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Objects;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import static androidx.constraintlayout.widget.Constraints.TAG; import static androidx.constraintlayout.widget.Constraints.TAG;
@@ -45,18 +59,72 @@ public class IABService implements PurchasesUpdatedListener
} }
} }
public enum SimplePurchaseState { YES, NO, UNINITIALIZED }
private BillingClient client; private BillingClient client;
private boolean isServiceConnected; private boolean isServiceConnected;
private final List<Purchase> purchases = new ArrayList<>(); private final List<Purchase> purchases = new ArrayList<>();
private boolean _isInitialized = false;
private final Map<String, Boolean> _localCache= new HashMap<>();
public IABService(Context c) public IABService(Context c)
{ {
_isInitialized = false;
loadCache();
client = BillingClient client = BillingClient
.newBuilder(c) .newBuilder(c)
.setListener(this) .setListener(this)
.build(); .build();
startServiceConnection(this::queryPurchases, false); startServiceConnection(this::queryPurchases, false);
startServiceConnection(this::querySkuDetails, false);
}
public void reloadPrefs()
{
loadCache();
}
private void loadCache()
{
_localCache.clear();
SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("IAB", Context.MODE_PRIVATE);
int count = sharedPref.getInt("c", 0);
for (int i=0; i < count; i++)
{
String k = sharedPref.getString("["+i+"]->key", null);
boolean v = sharedPref.getBoolean("["+i+"]->value", false);
if (k==null)continue;
_localCache.put(k, v);
}
}
private void saveCache()
{
SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("IAB", Context.MODE_PRIVATE);
SharedPreferences.Editor editor= sharedPref.edit();
editor.putInt("c", _localCache.size());
int i = 0;
for (Map.Entry<String, Boolean> e : _localCache.entrySet())
{
editor.putString("["+i+"]->key", e.getKey());
editor.putBoolean("["+i+"]->value", e.getValue());
i++;
}
editor.apply();
}
@SuppressWarnings("ConstantConditions")
private synchronized void updateCache(String k, boolean v)
{
if (_localCache.containsKey(k) && _localCache.get(k)==v) return;
_localCache.put(k, v);
saveCache();
} }
public void queryPurchases() public void queryPurchases()
@@ -67,14 +135,16 @@ public class IABService implements PurchasesUpdatedListener
Purchase.PurchasesResult purchasesResult = client.queryPurchases(BillingClient.SkuType.INAPP); Purchase.PurchasesResult purchasesResult = client.queryPurchases(BillingClient.SkuType.INAPP);
Log.i(TAG, "Querying purchases elapsed time: " + (System.currentTimeMillis() - time) + "ms"); Log.i(TAG, "Querying purchases elapsed time: " + (System.currentTimeMillis() - time) + "ms");
if (purchasesResult.getResponseCode() == BillingClient.BillingResponse.OK) if (purchasesResult.getResponseCode() == BillingClient.BillingResponseCode.OK)
{ {
for (Purchase p : purchasesResult.getPurchasesList()) for (Purchase p : Objects.requireNonNull(purchasesResult.getPurchasesList()))
{ {
handlePurchase(p); handlePurchase(p, false);
} }
boolean newProMode = getPurchaseCached(IAB_PRO_MODE) != null; _isInitialized = true;
boolean newProMode = getPurchaseCachedSimple(IAB_PRO_MODE);
if (newProMode != SCNSettings.inst().promode_local) if (newProMode != SCNSettings.inst().promode_local)
{ {
refreshProModeListener(); refreshProModeListener();
@@ -89,20 +159,39 @@ public class IABService implements PurchasesUpdatedListener
executeServiceRequest(queryToExecute, false); executeServiceRequest(queryToExecute, false);
} }
public void querySkuDetails() {
}
public void purchase(Activity a, String id) public void purchase(Activity a, String id)
{ {
Func0to0 queryRequest = () -> {
// Query the purchase async
SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
params.setSkusList(Collections.singletonList(id)).setType(BillingClient.SkuType.INAPP);
client.querySkuDetailsAsync(params.build(), (billingResult, skuDetailsList) ->
{
if (billingResult.getResponseCode() != BillingClient.BillingResponseCode.OK || skuDetailsList == null || skuDetailsList.size() != 1)
{
SCNApp.showToast("Could not find product", Toast.LENGTH_SHORT);
return;
}
executeServiceRequest(() -> executeServiceRequest(() ->
{ {
BillingFlowParams flowParams = BillingFlowParams BillingFlowParams flowParams = BillingFlowParams
.newBuilder() .newBuilder()
.setSku(id) .setSkuDetails(skuDetailsList.get(0))
.setType(BillingClient.SkuType.INAPP) // SkuType.SUB for subscription
.build(); .build();
client.launchBillingFlow(a, flowParams); client.launchBillingFlow(a, flowParams);
}, true); }, true);
});
};
executeServiceRequest(queryRequest, false);
} }
private void executeServiceRequest(Func0to0 runnable, final boolean userRequest) { private void executeServiceRequest(Func0to0 runnable, final boolean userRequest)
{
if (isServiceConnected) if (isServiceConnected)
{ {
runnable.invoke(); runnable.invoke();
@@ -124,34 +213,37 @@ public class IABService implements PurchasesUpdatedListener
} }
@Override @Override
public void onPurchasesUpdated(int responseCode, @Nullable List<Purchase> purchases) public void onPurchasesUpdated(@NonNull BillingResult billingResult, @Nullable List<Purchase> purchases)
{ {
if (responseCode == BillingClient.BillingResponse.OK && purchases != null) if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && purchases != null)
{ {
for (Purchase purchase : purchases) for (Purchase purchase : purchases)
{ {
handlePurchase(purchase); handlePurchase(purchase, true);
} }
} }
else if (responseCode == BillingClient.BillingResponse.ITEM_ALREADY_OWNED && purchases != null) else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED && purchases != null)
{ {
for (Purchase purchase : purchases) for (Purchase purchase : purchases)
{ {
handlePurchase(purchase); handlePurchase(purchase, true);
} }
} }
} }
private void handlePurchase(Purchase purchase) private void handlePurchase(Purchase purchase, boolean triggerUpdate)
{ {
Log.d(TAG, "Got a verified purchase: " + purchase); Log.d(TAG, "Got a verified purchase: " + purchase);
purchases.add(purchase); purchases.add(purchase);
refreshProModeListener(); if (triggerUpdate) refreshProModeListener();
updateCache(purchase.getSku(), true);
} }
private void refreshProModeListener() { private void refreshProModeListener()
{
MainActivity ma = SCNApp.getMainActivity(); MainActivity ma = SCNApp.getMainActivity();
if (ma != null) ma.adpTabs.tab3.updateProState(); if (ma != null) ma.adpTabs.tab3.updateProState();
if (ma != null) ma.adpTabs.tab1.updateProState(); if (ma != null) ma.adpTabs.tab1.updateProState();
@@ -163,9 +255,9 @@ public class IABService implements PurchasesUpdatedListener
client.startConnection(new BillingClientStateListener() client.startConnection(new BillingClientStateListener()
{ {
@Override @Override
public void onBillingSetupFinished(@BillingClient.BillingResponse int billingResponseCode) public void onBillingSetupFinished(@NonNull BillingResult billingResult)
{ {
if (billingResponseCode == BillingClient.BillingResponse.OK) if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK)
{ {
isServiceConnected = true; isServiceConnected = true;
if (executeOnSuccess != null) executeOnSuccess.invoke(); if (executeOnSuccess != null) executeOnSuccess.invoke();
@@ -183,13 +275,31 @@ public class IABService implements PurchasesUpdatedListener
}); });
} }
public Purchase getPurchaseCached(String id) public boolean getPurchaseCachedSimple(String id)
{ {
for (Purchase p : purchases) return getPurchaseCachedExtended(id).Item1;
{
if (Str.equals(p.getSku(), id)) return p;
} }
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);
} }
} }

View File

@@ -229,10 +229,10 @@ public class NotificationService
if (msg.Priority == PriorityEnum.NORMAL) mBuilder.setPriority(NotificationCompat.PRIORITY_DEFAULT); if (msg.Priority == PriorityEnum.NORMAL) mBuilder.setPriority(NotificationCompat.PRIORITY_DEFAULT);
if (msg.Priority == PriorityEnum.HIGH) mBuilder.setPriority(NotificationCompat.PRIORITY_HIGH); if (msg.Priority == PriorityEnum.HIGH) mBuilder.setPriority(NotificationCompat.PRIORITY_HIGH);
Intent intnt_click = new Intent(SCNApp.getContext(), BroadcastReceiverService.class); Intent intent = new Intent(ctxt, MainActivity.class);
intnt_click.putExtra(BroadcastReceiverService.ID_KEY, BroadcastReceiverService.NOTIF_SHOW_MAIN); PendingIntent pi = PendingIntent.getActivity(ctxt, 0, intent, 0);
PendingIntent pi = PendingIntent.getBroadcast(ctxt, 0, intnt_click, 0);
mBuilder.setContentIntent(pi); mBuilder.setContentIntent(pi);
NotificationManager mNotificationManager = (NotificationManager) ctxt.getSystemService(Context.NOTIFICATION_SERVICE); NotificationManager mNotificationManager = (NotificationManager) ctxt.getSystemService(Context.NOTIFICATION_SERVICE);
if (mNotificationManager == null) return; if (mNotificationManager == null) return;

View File

@@ -1,11 +1,13 @@
package com.blackforestbytes.simplecloudnotifier.view; package com.blackforestbytes.simplecloudnotifier.view;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.icu.text.SymbolTable; import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log;
import android.view.View; import android.view.View;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import com.blackforestbytes.simplecloudnotifier.R; import com.blackforestbytes.simplecloudnotifier.R;
@@ -23,6 +25,12 @@ import androidx.appcompat.widget.Toolbar;
import androidx.viewpager.widget.PagerAdapter; import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager; import androidx.viewpager.widget.ViewPager;
import java.io.File;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.util.Map;
import java.util.Set;
public class MainActivity extends AppCompatActivity public class MainActivity extends AppCompatActivity
{ {
public TabAdapter adpTabs; public TabAdapter adpTabs;
@@ -31,7 +39,7 @@ public class MainActivity extends AppCompatActivity
@Override @Override
protected void onCreate(Bundle savedInstanceState) protected void onCreate(Bundle savedInstanceState)
{ {
QueryLog.instance(); QueryLog.inst();
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); setContentView(R.layout.activity_main);
@@ -103,4 +111,114 @@ public class MainActivity extends AppCompatActivity
if (clickCount == 4) startActivity(new Intent(this, QueryLogActivity.class)); if (clickCount == 4) startActivity(new Intent(this, QueryLogActivity.class));
} }
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == 1991 && resultCode == RESULT_OK)
{
Uri uri = data.getData(); //The uri with the location of the file
Context ctxt = this;
try
{
ObjectInputStream stream = new ObjectInputStream(getContentResolver().openInputStream(uri));
Map<String, ?> d1 = (Map<String, ?>)stream.readObject();
Map<String, ?> d2 = (Map<String, ?>)stream.readObject();
Map<String, ?> d3 = (Map<String, ?>)stream.readObject();
Map<String, ?> d4 = (Map<String, ?>)stream.readObject();
stream.close();
runOnUiThread(() ->
{
SharedPreferences.Editor e1 = ctxt.getSharedPreferences("Config", Context.MODE_PRIVATE).edit();
SharedPreferences.Editor e2 = ctxt.getSharedPreferences("IAB", Context.MODE_PRIVATE).edit();
SharedPreferences.Editor e3 = ctxt.getSharedPreferences("CMessageList", Context.MODE_PRIVATE).edit();
SharedPreferences.Editor e4 = ctxt.getSharedPreferences("QueryLog", Context.MODE_PRIVATE).edit();
e1.clear();
for (Map.Entry<String, ?> entry : d1.entrySet())
{
if (entry.getValue() instanceof String) e1.putString(entry.getKey(), (String)entry.getValue());
if (entry.getValue() instanceof Boolean) e1.putBoolean(entry.getKey(), (Boolean)entry.getValue());
if (entry.getValue() instanceof Float) e1.putFloat(entry.getKey(), (Float)entry.getValue());
if (entry.getValue() instanceof Integer) e1.putInt(entry.getKey(), (Integer)entry.getValue());
if (entry.getValue() instanceof Long) e1.putLong(entry.getKey(), (Long)entry.getValue());
if (entry.getValue() instanceof Set<?>) e1.putStringSet(entry.getKey(), (Set<String>)entry.getValue());
}
e2.clear();
for (Map.Entry<String, ?> entry : d2.entrySet())
{
if (entry.getValue() instanceof String) e2.putString(entry.getKey(), (String)entry.getValue());
if (entry.getValue() instanceof Boolean) e2.putBoolean(entry.getKey(), (Boolean)entry.getValue());
if (entry.getValue() instanceof Float) e2.putFloat(entry.getKey(), (Float)entry.getValue());
if (entry.getValue() instanceof Integer) e2.putInt(entry.getKey(), (Integer)entry.getValue());
if (entry.getValue() instanceof Long) e2.putLong(entry.getKey(), (Long)entry.getValue());
if (entry.getValue() instanceof Set<?>) e2.putStringSet(entry.getKey(), (Set<String>)entry.getValue());
}
e2.clear();
for (Map.Entry<String, ?> entry : d3.entrySet())
{
if (entry.getValue() instanceof String) e3.putString(entry.getKey(), (String)entry.getValue());
if (entry.getValue() instanceof Boolean) e3.putBoolean(entry.getKey(), (Boolean)entry.getValue());
if (entry.getValue() instanceof Float) e3.putFloat(entry.getKey(), (Float)entry.getValue());
if (entry.getValue() instanceof Integer) e3.putInt(entry.getKey(), (Integer)entry.getValue());
if (entry.getValue() instanceof Long) e3.putLong(entry.getKey(), (Long)entry.getValue());
if (entry.getValue() instanceof Set<?>) e3.putStringSet(entry.getKey(), (Set<String>)entry.getValue());
}
e4.clear();
for (Map.Entry<String, ?> entry : d4.entrySet())
{
if (entry.getValue() instanceof String) e4.putString(entry.getKey(), (String)entry.getValue());
if (entry.getValue() instanceof Boolean) e4.putBoolean(entry.getKey(), (Boolean)entry.getValue());
if (entry.getValue() instanceof Float) e4.putFloat(entry.getKey(), (Float)entry.getValue());
if (entry.getValue() instanceof Integer) e4.putInt(entry.getKey(), (Integer)entry.getValue());
if (entry.getValue() instanceof Long) e4.putLong(entry.getKey(), (Long)entry.getValue());
if (entry.getValue() instanceof Set<?>) e4.putStringSet(entry.getKey(), (Set<String>)entry.getValue());
}
e1.apply();
e2.apply();
e3.apply();
e4.apply();
SCNSettings.inst().reloadPrefs();
IABService.inst().reloadPrefs();
CMessageList.inst().reloadPrefs();
QueryLog.inst().reloadPrefs();
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ViewPager viewPager = findViewById(R.id.pager);
PagerAdapter adapter = adpTabs = new TabAdapter(getSupportFragmentManager());
viewPager.setAdapter(adapter);
TabLayout tabLayout = findViewById(R.id.tab_layout);
tabLayout.setupWithViewPager(viewPager);
SCNSettings.inst().work(this);
SCNApp.showToast("Backup imported", Toast.LENGTH_LONG);
finish();
});
}
catch (Exception e)
{
Log.e("Import:Err", e.toString());
SCNApp.showToast("Import failed", Toast.LENGTH_LONG);
}
}
}
} }

View File

@@ -58,7 +58,7 @@ public class NotificationsFragment extends Fragment implements MessageAdapterTou
public void updateProState() public void updateProState()
{ {
if (adView != null) adView.setVisibility(IABService.inst().getPurchaseCached(IABService.IAB_PRO_MODE) != null ? View.GONE : View.VISIBLE); if (adView != null) adView.setVisibility(IABService.inst().getPurchaseCachedSimple(IABService.IAB_PRO_MODE) ? View.GONE : View.VISIBLE);
} }
@Override @Override

View File

@@ -2,14 +2,12 @@ package com.blackforestbytes.simplecloudnotifier.view;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.graphics.Color; import android.graphics.Color;
import android.media.AudioAttributes; import android.media.AudioAttributes;
import android.media.AudioManager; import android.media.AudioManager;
import android.media.MediaPlayer; import android.media.MediaPlayer;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri; import android.net.Uri;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.text.Editable; import android.text.Editable;
import android.util.Log; import android.util.Log;
@@ -27,10 +25,12 @@ import android.widget.Switch;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import com.android.billingclient.api.Purchase; import androidx.annotation.NonNull;
import androidx.core.content.FileProvider;
import androidx.fragment.app.Fragment;
import com.blackforestbytes.simplecloudnotifier.R; import com.blackforestbytes.simplecloudnotifier.R;
import com.blackforestbytes.simplecloudnotifier.SCNApp; import com.blackforestbytes.simplecloudnotifier.SCNApp;
import com.blackforestbytes.simplecloudnotifier.lib.android.ThreadUtils;
import com.blackforestbytes.simplecloudnotifier.lib.lambda.FI; import com.blackforestbytes.simplecloudnotifier.lib.lambda.FI;
import com.blackforestbytes.simplecloudnotifier.lib.string.Str; import com.blackforestbytes.simplecloudnotifier.lib.string.Str;
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings; import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
@@ -39,10 +39,12 @@ import com.blackforestbytes.simplecloudnotifier.util.TextChangedListener;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.Map;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import top.defaults.colorpicker.ColorPickerPopup; import top.defaults.colorpicker.ColorPickerPopup;
import xyz.aprildown.ultimatemusicpicker.MusicPickerListener; import xyz.aprildown.ultimatemusicpicker.MusicPickerListener;
import xyz.aprildown.ultimatemusicpicker.UltimateMusicPicker; import xyz.aprildown.ultimatemusicpicker.UltimateMusicPicker;
@@ -93,6 +95,9 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
private SeekBar prefMsgHighVolume; private SeekBar prefMsgHighVolume;
private ImageView prefMsgHighVolumeTest; private ImageView prefMsgHighVolumeTest;
private Button prefBtnImport;
private Button prefBtnExport;
private int musicPickerSwitch = -1; private int musicPickerSwitch = -1;
private MediaPlayer[] mPlayers = new MediaPlayer[3]; private MediaPlayer[] mPlayers = new MediaPlayer[3];
@@ -160,6 +165,9 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
prefMsgHighVolume = v.findViewById(R.id.prefMsgHighVolume); prefMsgHighVolume = v.findViewById(R.id.prefMsgHighVolume);
prefMsgHighVolumeTest = v.findViewById(R.id.btnHighVolumeTest); prefMsgHighVolumeTest = v.findViewById(R.id.btnHighVolumeTest);
prefBtnExport = v.findViewById(R.id.prefExport);
prefBtnImport = v.findViewById(R.id.prefImport);
ArrayAdapter<Integer> plcsa = new ArrayAdapter<>(v.getContext(), android.R.layout.simple_spinner_item, SCNSettings.CHOOSABLE_CACHE_SIZES); 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); plcsa.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
prefLocalCacheSize.setAdapter(plcsa); prefLocalCacheSize.setAdapter(plcsa);
@@ -246,6 +254,9 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
prefUpgradeAccount.setOnClickListener(a -> onUpgradeAccount()); prefUpgradeAccount.setOnClickListener(a -> onUpgradeAccount());
prefBtnExport.setOnClickListener(a -> onExport());
prefBtnImport.setOnClickListener(a -> onImport());
prefMsgLowEnableSound.setOnCheckedChangeListener((a,b) -> { s.PriorityLow.EnableSound=b; saveAndUpdate(); }); prefMsgLowEnableSound.setOnCheckedChangeListener((a,b) -> { s.PriorityLow.EnableSound=b; saveAndUpdate(); });
prefMsgLowRingtone_container.setOnClickListener(a -> chooseRingtoneLow()); prefMsgLowRingtone_container.setOnClickListener(a -> chooseRingtoneLow());
prefMsgLowRepeatSound.setOnCheckedChangeListener((a,b) -> { s.PriorityLow.RepeatSound=b; saveAndUpdate(); }); prefMsgLowRepeatSound.setOnCheckedChangeListener((a,b) -> { s.PriorityLow.RepeatSound=b; saveAndUpdate(); });
@@ -277,6 +288,55 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
prefMsgHighVolumeTest.setOnClickListener((v) -> { if (s.PriorityHigh.ForceVolume) playTestSound(2, prefMsgHighVolumeTest, s.PriorityHigh.SoundSource, s.PriorityHigh.ForceVolumeValue); }); prefMsgHighVolumeTest.setOnClickListener((v) -> { if (s.PriorityHigh.ForceVolume) playTestSound(2, prefMsgHighVolumeTest, s.PriorityHigh.SoundSource, s.PriorityHigh.ForceVolumeValue); });
} }
private void onExport()
{
Context ctxt = getContext();
if (ctxt == null) return;
try
{
File outputDir = ctxt.getCacheDir(); // context being the Activity pointer
File outputFile = File.createTempFile("scn_export_", ".dat", outputDir);
ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream(outputFile));
Map<String, ?> d1 = ctxt.getSharedPreferences("Config", Context.MODE_PRIVATE).getAll();
Map<String, ?> d2 = ctxt.getSharedPreferences("IAB", Context.MODE_PRIVATE).getAll();
Map<String, ?> d3 = ctxt.getSharedPreferences("CMessageList", Context.MODE_PRIVATE).getAll();
Map<String, ?> d4 = ctxt.getSharedPreferences("QueryLog", Context.MODE_PRIVATE).getAll();
output.writeObject(d1);
output.writeObject(d2);
output.writeObject(d3);
output.writeObject(d4);
Intent intent = new Intent(Intent.ACTION_SEND);
Uri uri = FileProvider.getUriForFile(ctxt, "com.blackforestbytes.simplecloudnotifier.fileprovider", outputFile);
intent.putExtra(Intent.EXTRA_STREAM, uri);
intent.setType("*/*");
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(Intent.createChooser(intent, "Export"));
}
catch (IOException e)
{
Log.e("Export:Err", e.toString());
SCNApp.showToast("Export failed", Toast.LENGTH_LONG);
}
}
private void onImport()
{
SCNApp.getMainActivity().setContentView(R.layout.activity_main);
Intent intent = new Intent()
.setType("*/*")
.setAction(Intent.ACTION_GET_CONTENT);
((MainActivity)getActivity()).startActivityForResult(Intent.createChooser(intent, "Select a file"), 1991);
}
private void updateEnabled(boolean prev, boolean now) private void updateEnabled(boolean prev, boolean now)
{ {
if (!prev && now) if (!prev && now)
@@ -366,11 +426,11 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
public void updateProState() public void updateProState()
{ {
Purchase p = IABService.inst().getPurchaseCached(IABService.IAB_PRO_MODE); boolean pmode = IABService.inst().getPurchaseCachedSimple(IABService.IAB_PRO_MODE);
if (prefUpgradeAccount != null) prefUpgradeAccount.setVisibility( p != null ? View.GONE : View.VISIBLE); if (prefUpgradeAccount != null) prefUpgradeAccount.setVisibility( pmode ? View.GONE : View.VISIBLE);
if (prefUpgradeAccount_info != null) prefUpgradeAccount_info.setVisibility(p != null ? View.GONE : View.VISIBLE); if (prefUpgradeAccount_info != null) prefUpgradeAccount_info.setVisibility(pmode ? View.GONE : View.VISIBLE);
if (prefUpgradeAccount_msg != null) prefUpgradeAccount_msg.setVisibility( p != null ? View.VISIBLE : View.GONE ); if (prefUpgradeAccount_msg != null) prefUpgradeAccount_msg.setVisibility( pmode ? View.VISIBLE : View.GONE );
} }
private int getCacheSizeIndex(int value) private int getCacheSizeIndex(int value)

View File

@@ -22,7 +22,7 @@ public class QueryLogActivity extends AppCompatActivity
setContentView(R.layout.activity_querylog); setContentView(R.layout.activity_querylog);
ListView lvMain = findViewById(R.id.lvQueryList); ListView lvMain = findViewById(R.id.lvQueryList);
SingleQuery[] arr = QueryLog.instance().get().toArray(new SingleQuery[0]); SingleQuery[] arr = QueryLog.inst().get().toArray(new SingleQuery[0]);
QueryLogAdapter a = new QueryLogAdapter(this, arr); QueryLogAdapter a = new QueryLogAdapter(this, arr);
lvMain.setAdapter(a); lvMain.setAdapter(a);

View File

@@ -805,6 +805,24 @@
</androidx.cardview.widget.CardView> </androidx.cardview.widget.CardView>
<com.google.android.material.button.MaterialButton
android:id="@+id/prefExport"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="12dp"
app:cornerRadius="0dp"
android:backgroundTint="#444444"
android:text="@string/export_settings" />
<com.google.android.material.button.MaterialButton
android:id="@+id/prefImport"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="12dp"
app:cornerRadius="0dp"
android:backgroundTint="#666666"
android:text="@string/import_settings" />
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>

View File

@@ -37,4 +37,6 @@
<string name="play_test_sound">Play test sound</string> <string name="play_test_sound">Play test sound</string>
<string name="delete">DELETE</string> <string name="delete">DELETE</string>
<string name="title_activity_query_log">QueryLogActivity</string> <string name="title_activity_query_log">QueryLogActivity</string>
<string name="import_settings">Import settings</string>
<string name="export_settings">Export settings</string>
</resources> </resources>

View File

@@ -0,0 +1,7 @@
<paths>
<files-path path="/" name="files" />
<cache-path path="/" name="cache" />
<external-path path="/" name="external" />
<external-files-path path="/" name="external-files" />
<external-cache-path path="/" name="external-cache" />
</paths>

View File

@@ -1,3 +1,3 @@
#Fri Nov 23 19:40:03 CET 2018 #Thu Mar 05 15:29:10 UTC 2020
VERSION_NAME=1.2.0 VERSION_NAME=1.8.0
VERSION_CODE=16 VERSION_CODE=23

View File

@@ -7,8 +7,8 @@ buildscript {
jcenter() jcenter()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.2.1' classpath 'com.android.tools.build:gradle:4.1.0'
classpath 'com.google.gms:google-services:4.2.0' classpath 'com.google.gms:google-services:4.3.4'
} }
} }

View File

@@ -1,6 +1,6 @@
#Wed Sep 26 22:10:14 CEST 2018 #Tue Nov 03 14:10:19 CET 2020
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip

35
examples/scn_send.php Normal file
View File

@@ -0,0 +1,35 @@
<?php
/**
* @param string $title
* @param string $content
* @param int $priority
* @return bool
*/
function sendSCN($title, $content, $priority) {
global $config;
$data =
[
'user_id' => '', //TODO set your userid
'user_key' => '', //TODO set your userkey
'title' => $title,
'content' => $content,
'priority' => $priority,
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://simplecloudnotifier.blackforestbytes.com/send.php");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
curl_close($ch);
if ($result === false) return false;
$json = json_decode($result, true);
return $json['success'];
}

View File

@@ -166,6 +166,12 @@ function verifyOrderToken($tok)
{ {
// https://developers.google.com/android-publisher/api-ref/purchases/products/get // https://developers.google.com/android-publisher/api-ref/purchases/products/get
// if this does no longer work, you probably have to go through the initial OAuth process again
// 1. go to Postman do the [ https://accounts.google.com/o/oauth2/auth ] request (in browser) to get a new "code"
// 2. go to Postman do the [ Get Tokens ] request to get a new "access_token" and "access_token"
// 3. update these tokens in the server config.php
try try
{ {
$package = getConfig()['verify_api']['package_name']; $package = getConfig()['verify_api']['package_name'];

View File

@@ -24,17 +24,20 @@ if ($ispro)
$pdo->rollBack(); $pdo->rollBack();
die(json_encode(['success' => false, 'message' => 'Purchase token could not be verified'])); die(json_encode(['success' => false, 'message' => 'Purchase token could not be verified']));
} }
$stmt = $pdo->prepare('UPDATE users SET is_pro=0, pro_token=NULL WHERE user_id <> :uid AND pro_token = :ptk');
$stmt->execute(['uid' => $user_id, 'ptk' => $pro_token]);
} }
$stmt = $pdo->prepare('INSERT INTO users (user_key, fcm_token, is_pro, pro_token, timestamp_accessed) VALUES (:key, :token, :bpro, :spro, NOW())'); $stmt = $pdo->prepare('INSERT INTO users (user_key, fcm_token, is_pro, pro_token, timestamp_accessed) VALUES (:key, :token, :bpro, :spro, NOW())');
$stmt->execute(['key' => $user_key, 'token' => $fcmtoken, 'bpro' => $ispro, 'spro' => $ispro ? $pro_token : null]); $stmt->execute(['key' => $user_key, 'token' => $fcmtoken, 'bpro' => ($ispro ? 1 : 0), 'spro' => ($ispro ? $pro_token : null)]);
$user_id = $pdo->lastInsertId('user_id'); $user_id = $pdo->lastInsertId('user_id');
$stmt = $pdo->prepare('UPDATE users SET fcm_token=NULL WHERE user_id <> :uid AND fcm_token=:ft'); $stmt = $pdo->prepare('UPDATE users SET fcm_token=NULL WHERE user_id <> :uid AND fcm_token=:ft');
$stmt->execute(['uid' => $user_id, 'ft' => $fcm_token]); $stmt->execute(['uid' => $user_id, 'ft' => $fcmtoken]);
if ($ispro)
{
$stmt = $pdo->prepare('UPDATE users SET is_pro=0, pro_token=NULL WHERE user_id <> :uid AND pro_token = :ptk');
$stmt->execute(['uid' => $user_id, 'ptk' => $pro_token]);
}
$pdo->commit(); $pdo->commit();

View File

@@ -24,10 +24,10 @@ CREATE TABLE `messages`
`sender_user_id` INT(11) NOT NULL, `sender_user_id` INT(11) NOT NULL,
`timestamp_real` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, `timestamp_real` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`ack` BIT NOT NULL DEFAULT 0, `ack` TINYINT(1) NOT NULL DEFAULT 0,
`title` VARCHAR(256) NOT NULL, `title` VARCHAR(256) NOT NULL,
`content` VARCHAR(12288) NULL, `content` LONGTEXT NULL,
`priority` INT(11) NOT NULL, `priority` INT(11) NOT NULL,
`sendtime` BIGINT UNSIGNED NOT NULL, `sendtime` BIGINT UNSIGNED NOT NULL,

View File

@@ -52,7 +52,7 @@ try
//------------------------------------------------------------------ //------------------------------------------------------------------
if (strlen($message) > 120) api_return(400, ['success' => false, 'error' => ERR::TITLE_TOO_LONG, 'errhighlight' => 103, 'message' => 'Title too long (120 characters)']); if (strlen($message) > 120) api_return(400, ['success' => false, 'error' => ERR::TITLE_TOO_LONG, 'errhighlight' => 103, 'message' => 'Title too long (120 characters)']);
if (strlen($content) > Statics::contentlen_max($data['is_pro'])) api_return(400, ['success' => false, 'error' => ERR::CONTENT_TOO_LONG, 'errhighlight' => 104, 'message' => 'Content too long ('.Statics::contentlen_max($data['is_pro']).' characters)']); if (strlen($content) > Statics::contentlen_max($data['is_pro'])) api_return(400, ['success' => false, 'error' => ERR::CONTENT_TOO_LONG, 'errhighlight' => 104, 'message' => 'Content too long ('.strlen($content).' characters; max := '.Statics::contentlen_max($data['is_pro']).' characters)']);
//------------------------------------------------------------------ //------------------------------------------------------------------