From e5252210108441a4ce56029318a18a3192e36402 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Schw=C3=B6rer?= Date: Tue, 11 Dec 2018 12:25:10 +0100 Subject: [PATCH] Show Querylog via hidden shit --- android/app/build.gradle | 6 +- android/app/src/main/AndroidManifest.xml | 38 ++- .../simplecloudnotifier/SCNApp.java | 7 + .../simplecloudnotifier/model/LogLevel.java | 58 ++++ .../simplecloudnotifier/model/QueryLog.java | 69 +++++ .../model/ServerCommunication.java | 261 ++++++++++++------ .../model/SingleQuery.java | 82 ++++++ .../util/MaxHeightScrollView.java | 56 ++++ .../view/MainActivity.java | 22 ++ .../view/debug/QueryLogActivity.java | 44 +++ .../view/debug/QueryLogAdapter.java | 66 +++++ .../view/debug/SingleQueryLogActivity.java | 38 +++ .../res/drawable-mdpi/simple_black_border.xml | 5 + .../app/src/main/res/layout/activity_main.xml | 4 +- .../src/main/res/layout/activity_querylog.xml | 13 + .../res/layout/activity_singlequerylog.xml | 240 ++++++++++++++++ .../src/main/res/layout/adapter_querylog.xml | 50 ++++ android/app/src/main/res/values/attrs.xml | 8 + android/app/src/main/res/values/dimens.xml | 1 + android/app/src/main/res/values/strings.xml | 1 + android/app/src/main/res/values/styles.xml | 4 + 21 files changed, 970 insertions(+), 103 deletions(-) create mode 100644 android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/model/LogLevel.java create mode 100644 android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/model/QueryLog.java create mode 100644 android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/model/SingleQuery.java create mode 100644 android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/util/MaxHeightScrollView.java create mode 100644 android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/view/debug/QueryLogActivity.java create mode 100644 android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/view/debug/QueryLogAdapter.java create mode 100644 android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/view/debug/SingleQueryLogActivity.java create mode 100644 android/app/src/main/res/drawable-mdpi/simple_black_border.xml create mode 100644 android/app/src/main/res/layout/activity_querylog.xml create mode 100644 android/app/src/main/res/layout/activity_singlequerylog.xml create mode 100644 android/app/src/main/res/layout/adapter_querylog.xml create mode 100644 android/app/src/main/res/values/attrs.xml diff --git a/android/app/build.gradle b/android/app/build.gradle index 697c783..5de6c95 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -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' diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 46aa758..e75e26e 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,43 +1,59 @@ - - + - + - - - - + + + + - + - + + + \ No newline at end of file diff --git a/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/SCNApp.java b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/SCNApp.java index f327e08..a6548fe 100644 --- a/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/SCNApp.java +++ b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/SCNApp.java @@ -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; @@ -98,3 +99,9 @@ public class SCNApp extends Application implements LifecycleObserver isBackground = false; } } + +//TODO: Collapse on click again +//TODO: Share button on expand +//TODO: Delete button on expand +//TODO: Config for collapsed line count +//TODO: Sometimes ads but promode diff --git a/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/model/LogLevel.java b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/model/LogLevel.java new file mode 100644 index 0000000..a2b7d00 --- /dev/null +++ b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/model/LogLevel.java @@ -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; // ???? + } +} diff --git a/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/model/QueryLog.java b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/model/QueryLog.java new file mode 100644 index 0000000..7394f02 --- /dev/null +++ b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/model/QueryLog.java @@ -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 = 64; + + 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 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 get() + { + List 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()); + } + } +} 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 6779fdb..f11119a 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 @@ -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()); + } + } } diff --git a/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/model/SingleQuery.java b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/model/SingleQuery.java new file mode 100644 index 0000000..3561cf4 --- /dev/null +++ b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/model/SingleQuery.java @@ -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) + ); + } +} diff --git a/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/util/MaxHeightScrollView.java b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/util/MaxHeightScrollView.java new file mode 100644 index 0000000..e217478 --- /dev/null +++ b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/util/MaxHeightScrollView.java @@ -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()); + } +} diff --git a/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/view/MainActivity.java b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/view/MainActivity.java index 01f61a3..5314cd7 100644 --- a/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/view/MainActivity.java +++ b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/view/MainActivity.java @@ -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)); + } } diff --git a/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/view/debug/QueryLogActivity.java b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/view/debug/QueryLogActivity.java new file mode 100644 index 0000000..782e112 --- /dev/null +++ b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/view/debug/QueryLogActivity.java @@ -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); + } + }); + + + } + +} diff --git a/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/view/debug/QueryLogAdapter.java b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/view/debug/QueryLogAdapter.java new file mode 100644 index 0000000..9459d70 --- /dev/null +++ b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/view/debug/QueryLogAdapter.java @@ -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 +{ + 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; + } +} diff --git a/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/view/debug/SingleQueryLogActivity.java b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/view/debug/SingleQueryLogActivity.java new file mode 100644 index 0000000..8e77467 --- /dev/null +++ b/android/app/src/main/java/com/blackforestbytes/simplecloudnotifier/view/debug/SingleQueryLogActivity.java @@ -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.findViewById(R.id.tvQL_Timestamp).setText(q.Timestamp.toString(DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"))); + this.findViewById(R.id.tvQL_Level).setText(q.Level.toUIString()); + this.findViewById(R.id.tvQL_Level).setTextColor(q.Level.getColor()); + this.findViewById(R.id.tvQL_Name).setText(q.Name); + this.findViewById(R.id.tvQL_URL).setText(q.URL.replace("?", "\r\n?").replace("&", "\r\n&")); + this.findViewById(R.id.tvQL_Response).setText(CompactJsonFormatter.formatJSON(q.Response, 999)); + this.findViewById(R.id.tvQL_ResponseCode).setText(Integer.toString(q.ResponseCode)); + this.findViewById(R.id.tvQL_ExceptionString).setText(q.ExceptionString); + } +} diff --git a/android/app/src/main/res/drawable-mdpi/simple_black_border.xml b/android/app/src/main/res/drawable-mdpi/simple_black_border.xml new file mode 100644 index 0000000..78ad158 --- /dev/null +++ b/android/app/src/main/res/drawable-mdpi/simple_black_border.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/layout/activity_main.xml b/android/app/src/main/res/layout/activity_main.xml index 40d5509..71dc8ea 100644 --- a/android/app/src/main/res/layout/activity_main.xml +++ b/android/app/src/main/res/layout/activity_main.xml @@ -2,10 +2,8 @@ + android:layout_height="match_parent"> + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/layout/activity_singlequerylog.xml b/android/app/src/main/res/layout/activity_singlequerylog.xml new file mode 100644 index 0000000..2216e5c --- /dev/null +++ b/android/app/src/main/res/layout/activity_singlequerylog.xml @@ -0,0 +1,240 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/layout/adapter_querylog.xml b/android/app/src/main/res/layout/adapter_querylog.xml new file mode 100644 index 0000000..fb951d7 --- /dev/null +++ b/android/app/src/main/res/layout/adapter_querylog.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/values/attrs.xml b/android/app/src/main/res/values/attrs.xml new file mode 100644 index 0000000..d1c31cd --- /dev/null +++ b/android/app/src/main/res/values/attrs.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/values/dimens.xml b/android/app/src/main/res/values/dimens.xml index 939fe4d..40f1022 100644 --- a/android/app/src/main/res/values/dimens.xml +++ b/android/app/src/main/res/values/dimens.xml @@ -6,4 +6,5 @@ 10dp 30dp 90dp + 16dp \ No newline at end of file diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 8a1c359..f250e22 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -35,4 +35,5 @@ Volume icon Play test sound DELETE + QueryLogActivity diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml index 2bcf132..96e5894 100644 --- a/android/app/src/main/res/values/styles.xml +++ b/android/app/src/main/res/values/styles.xml @@ -10,4 +10,8 @@ #FF4081 +