diff --git a/android/.idea/jarRepositories.xml b/android/.idea/jarRepositories.xml new file mode 100644 index 0000000..ddeabf5 --- /dev/null +++ b/android/.idea/jarRepositories.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/app/build.gradle b/android/app/build.gradle index 5de6c95..a6da5d1 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -1,7 +1,7 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 28 + compileSdkVersion 30 def versionPropsFile = file('version.properties') def vNumber @@ -16,7 +16,7 @@ android { defaultConfig { applicationId "com.blackforestbytes.simplecloudnotifier" minSdkVersion 21 - targetSdkVersion 28 + targetSdkVersion 30 versionCode vNumber versionName vName } @@ -35,78 +35,82 @@ android { dependencies { 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.constraintlayout:constraintlayout:1.1.3' - implementation 'androidx.recyclerview:recyclerview:1.0.0' - implementation 'androidx.recyclerview:recyclerview:1.0.0' - implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0' + implementation 'androidx.constraintlayout:constraintlayout:2.0.4' + implementation 'androidx.recyclerview:recyclerview:1.1.0' + implementation 'androidx.recyclerview:recyclerview:1.1.0' + implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' - implementation 'com.google.android.material:material:1.0.0' - 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.2' - implementation 'com.android.billingclient:billing:1.2' + implementation 'com.google.android.material:material:1.2.1' + implementation 'com.google.firebase:firebase-core:18.0.0' + implementation 'com.google.firebase:firebase-messaging:21.0.0' + implementation 'com.google.android.gms:play-services-ads:19.5.0' + 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.DeweyReed:UltimateMusicPicker:2.0.0" 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' -task updateVersion << { - def lastTag = ['git', 'describe', "--abbrev=0", "--tags"].execute().text.trim() +tasks.register("updateVersion") { + group = 'Custom' - def versionPropsFile = file('version.properties') - if (!versionPropsFile.canRead()) throw new FileNotFoundException("Could not read version.properties!") - Properties versionProps = new Properties() - new FileInputStream(versionPropsFile).withCloseable { fis -> versionProps.load(fis) } + doLast { + def lastTag = ['git', 'describe', "--abbrev=0", "--tags"].execute().text.trim() - def matcher = lastTag =~ /^v([0-9]+)\.([0-9]+)\.([0-9]+)$/ + def versionPropsFile = file('version.properties') + if (!versionPropsFile.canRead()) throw new FileNotFoundException("Could not read version.properties!") + Properties versionProps = new Properties() + new FileInputStream(versionPropsFile).withCloseable { fis -> versionProps.load(fis) } - if (!matcher.matches()) throw new Exception("Last Tag ('" + lastTag + "') has invalid format :(") + def matcher = lastTag =~ /^v([0-9]+)\.([0-9]+)\.([0-9]+)$/ - def vName = (matcher[0][1] as Integer) + "." + (matcher[0][2] as Integer) + "." + (matcher[0][3] as Integer) - def vCode = versionProps['VERSION_CODE'] as Integer + if (!matcher.matches()) throw new Exception("Last Tag ('" + lastTag + "') has invalid format :(") - if (new File(".do_publish_beta_release").exists()) new File(".do_publish_beta_release").delete() - if (new File(".do_publish_prod_release").exists()) new File(".do_publish_prod_release").delete() + def vName = (matcher[0][1] as Integer) + "." + (matcher[0][2] as Integer) + "." + (matcher[0][3] as Integer) + def vCode = versionProps['VERSION_CODE'] as Integer - if (vName == versionProps['VERSION_NAME'].toString()) { - println "This version was already built - skip deployment" - } else if (vName.endsWith(".0")) { - println "" - println "=====================================================================" - println "=====================================================================" - println "(!) This is a new PRODUCTION release - create deployment trigger file" - println "=====================================================================" - println "=====================================================================" - println "" + if (new File(".do_publish_beta_release").exists()) new File(".do_publish_beta_release").delete() + if (new File(".do_publish_prod_release").exists()) new File(".do_publish_prod_release").delete() - vCode++ - new File(".do_publish_prod_release").createNewFile() + if (vName == versionProps['VERSION_NAME'].toString()) { + println "This version was already built - skip deployment" + } else if (vName.endsWith(".0")) { + println "" + println "=====================================================================" + println "=====================================================================" + println "(!) This is a new PRODUCTION release - create deployment trigger file" + println "=====================================================================" + println "=====================================================================" + println "" - versionProps['VERSION_NAME'] = vName.toString() - versionProps['VERSION_CODE'] = vCode.toString() + vCode++ + new File(".do_publish_prod_release").createNewFile() - versionPropsFile.newWriter().withCloseable { w -> versionProps.store(w, null) } - } else { - println "" - println "===============================================================" - println "(!) This is a new beta release - create deployment trigger file" - println "===============================================================" - println "" + versionProps['VERSION_NAME'] = vName.toString() + versionProps['VERSION_CODE'] = vCode.toString() - vCode++ - new File(".do_publish_beta_release").createNewFile() + versionPropsFile.newWriter().withCloseable { w -> versionProps.store(w, null) } + } else { + println "" + println "===============================================================" + println "(!) This is a new beta release - create deployment trigger file" + println "===============================================================" + println "" - versionProps['VERSION_NAME'] = vName.toString() - versionProps['VERSION_CODE'] = vCode.toString() + vCode++ + new File(".do_publish_beta_release").createNewFile() - versionPropsFile.newWriter().withCloseable { w -> versionProps.store(w, null) } + versionProps['VERSION_NAME'] = vName.toString() + versionProps['VERSION_CODE'] = vCode.toString() + + versionPropsFile.newWriter().withCloseable { w -> versionProps.store(w, null) } + } } } diff --git a/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/model/ServerCommunication.java b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/model/ServerCommunication.java index d2e18be..558e384 100644 --- a/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/model/ServerCommunication.java +++ b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/model/ServerCommunication.java @@ -68,6 +68,7 @@ public class ServerCommunication if (!json_bool(json, "success")) { SCNApp.showToast(json_str(json, "message"), 4000); + handleNonSuccess("register", call, response, r); return; } @@ -134,6 +135,7 @@ public class ServerCommunication if (!json_bool(json, "success")) { SCNApp.showToast(json_str(json, "message"), 4000); + handleNonSuccess("update<1>", call, response, r); return; } @@ -200,6 +202,7 @@ public class ServerCommunication if (!json_bool(json, "success")) { SCNApp.showToast(json_str(json, "message"), 4000); + handleNonSuccess("update<2>", call, response, r); return; } @@ -269,6 +272,7 @@ public class ServerCommunication if (!json_bool(json, "success")) { SCNApp.showToast(json_str(json, "message"), 4000); + handleNonSuccess("info", call, response, r); int errid = json.optInt("errid", 0); @@ -356,6 +360,7 @@ public class ServerCommunication if (!json_bool(json, "success")) { SCNApp.showToast(json_str(json, "message"), 4000); + handleNonSuccess("requery", call, response, r); return; } @@ -420,8 +425,7 @@ public class ServerCommunication String r = Str.Empty; try (ResponseBody responseBody = response.body()) { - if (!response.isSuccessful()) - throw new IOException("Unexpected code " + response); + if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); if (responseBody == null) throw new IOException("No response"); r = responseBody.string(); @@ -431,6 +435,7 @@ public class ServerCommunication if (!json_bool(json, "success")) { SCNApp.showToast(json_str(json, "message"), 4000); + handleNonSuccess("upgrade", call, response, r); return; } @@ -492,7 +497,11 @@ public class ServerCommunication 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); } @@ -542,6 +551,7 @@ public class ServerCommunication if (!json_bool(json, "success")) { SCNApp.showToast(json_str(json, "message"), 4000); + handleNonSuccess("expand", call, response, r); return; } @@ -621,6 +631,28 @@ public class ServerCommunication } } + 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) + { + 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()); diff --git a/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/service/IABService.java b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/service/IABService.java index 206b787..54333d9 100644 --- a/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/service/IABService.java +++ b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/service/IABService.java @@ -9,8 +9,12 @@ import android.widget.Toast; import com.android.billingclient.api.BillingClient; import com.android.billingclient.api.BillingClientStateListener; import com.android.billingclient.api.BillingFlowParams; +import com.android.billingclient.api.BillingResult; import com.android.billingclient.api.Purchase; 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.lib.datatypes.Tuple2; import com.blackforestbytes.simplecloudnotifier.lib.datatypes.Tuple3; @@ -20,11 +24,15 @@ import com.blackforestbytes.simplecloudnotifier.model.SCNSettings; import com.blackforestbytes.simplecloudnotifier.view.MainActivity; 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.Map; +import java.util.Objects; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import static androidx.constraintlayout.widget.Constraints.TAG; @@ -58,7 +66,7 @@ public class IABService implements PurchasesUpdatedListener private final List purchases = new ArrayList<>(); private boolean _isInitialized = false; - private Map _localCache= new HashMap<>(); + private final Map _localCache= new HashMap<>(); public IABService(Context c) { @@ -72,6 +80,7 @@ public class IABService implements PurchasesUpdatedListener .build(); startServiceConnection(this::queryPurchases, false); + startServiceConnection(this::querySkuDetails, false); } public void reloadPrefs() @@ -126,9 +135,9 @@ public class IABService implements PurchasesUpdatedListener Purchase.PurchasesResult purchasesResult = client.queryPurchases(BillingClient.SkuType.INAPP); 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, false); } @@ -150,17 +159,35 @@ public class IABService implements PurchasesUpdatedListener executeServiceRequest(queryToExecute, false); } + public void querySkuDetails() { + } + public void purchase(Activity a, String id) { - executeServiceRequest(() -> - { - BillingFlowParams flowParams = BillingFlowParams - .newBuilder() - .setSku(id) - .setType(BillingClient.SkuType.INAPP) // SkuType.SUB for subscription - .build(); - client.launchBillingFlow(a, flowParams); - }, true); + 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(() -> + { + BillingFlowParams flowParams = BillingFlowParams + .newBuilder() + .setSkuDetails(skuDetailsList.get(0)) + .build(); + client.launchBillingFlow(a, flowParams); + }, true); + }); + }; + executeServiceRequest(queryRequest, false); + } private void executeServiceRequest(Func0to0 runnable, final boolean userRequest) @@ -186,16 +213,16 @@ public class IABService implements PurchasesUpdatedListener } @Override - public void onPurchasesUpdated(int responseCode, @Nullable List purchases) + public void onPurchasesUpdated(@NonNull BillingResult billingResult, @Nullable List purchases) { - if (responseCode == BillingClient.BillingResponse.OK && purchases != null) + if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && purchases != null) { for (Purchase purchase : purchases) { 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) { @@ -228,9 +255,9 @@ public class IABService implements PurchasesUpdatedListener client.startConnection(new BillingClientStateListener() { @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; if (executeOnSuccess != null) executeOnSuccess.invoke(); diff --git a/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/view/SettingsFragment.java b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/view/SettingsFragment.java index 43dbb75..db9535a 100644 --- a/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/view/SettingsFragment.java +++ b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/view/SettingsFragment.java @@ -3,17 +3,12 @@ package com.blackforestbytes.simplecloudnotifier.view; import android.annotation.SuppressLint; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; import android.graphics.Color; import android.media.AudioAttributes; import android.media.AudioManager; import android.media.MediaPlayer; -import android.media.Ringtone; -import android.media.RingtoneManager; import android.net.Uri; -import android.os.Build; import android.os.Bundle; -import android.os.Environment; import android.text.Editable; import android.util.Log; import android.view.LayoutInflater; @@ -30,11 +25,12 @@ import android.widget.Switch; import android.widget.TextView; import android.widget.Toast; -import com.android.billingclient.api.Purchase; -import com.blackforestbytes.simplecloudnotifier.BuildConfig; +import androidx.annotation.NonNull; +import androidx.core.content.FileProvider; +import androidx.fragment.app.Fragment; + import com.blackforestbytes.simplecloudnotifier.R; import com.blackforestbytes.simplecloudnotifier.SCNApp; -import com.blackforestbytes.simplecloudnotifier.lib.android.ThreadUtils; import com.blackforestbytes.simplecloudnotifier.lib.lambda.FI; import com.blackforestbytes.simplecloudnotifier.lib.string.Str; import com.blackforestbytes.simplecloudnotifier.model.SCNSettings; @@ -47,12 +43,8 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; -import java.util.Date; import java.util.Map; -import androidx.annotation.NonNull; -import androidx.core.content.FileProvider; -import androidx.fragment.app.Fragment; import top.defaults.colorpicker.ColorPickerPopup; import xyz.aprildown.ultimatemusicpicker.MusicPickerListener; import xyz.aprildown.ultimatemusicpicker.UltimateMusicPicker; diff --git a/android/build.gradle b/android/build.gradle index d21b66f..653a5fa 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -7,8 +7,8 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.2.1' - classpath 'com.google.gms:google-services:4.2.0' + classpath 'com.android.tools.build:gradle:4.1.0' + classpath 'com.google.gms:google-services:4.3.4' } } diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 7465871..5c71486 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Wed Sep 26 22:10:14 CEST 2018 +#Tue Nov 03 14:10:19 CET 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME 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