Compare commits

..

No commits in common. "master" and "v0.0.9" have entirely different histories.

673 changed files with 2235 additions and 566086 deletions

View File

@ -1,101 +0,0 @@
# https://docs.gitea.com/next/usage/actions/quickstart
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions
# https://docs.github.com/en/actions/learn-github-actions/contexts#github-context
# Configurable with a few commit messages:
# - [skip-tests] Skip the test stage
# - [skip-deployment] Skip the deployment stage
# - [skip-ci] Skip all stages (the whole ci/cd)
#
name: Build Docker and Deploy
run-name: Build & Deploy ${{ gitea.ref }} on ${{ gitea.actor }}
on:
push:
branches: ['master']
jobs:
build_server:
name: Build Docker Container
runs-on: bfb-cicd-latest
if: >-
!contains(github.event.head_commit.message, '[skip-ci]') &&
!contains(github.event.head_commit.message, '[skip-deployment]')
steps:
- run: echo -n "${{ secrets.DOCKER_REG_PASS }}" | docker login registry.blackforestbytes.com -u docker --password-stdin
- name: Check out code
uses: actions/checkout@v3
- run: cd "${{ gitea.workspace }}/scnserver" && make clean
- run: cd "${{ gitea.workspace }}/scnserver" && make docker
- run: cd "${{ gitea.workspace }}/scnserver" && make push-docker
test_server:
name: Run Unit-Tests
runs-on: bfb-cicd-latest
if: >-
!contains(github.event.head_commit.message, '[skip-ci]') &&
!contains(github.event.head_commit.message, '[skip-tests]')
steps:
- name: Check out code
uses: actions/checkout@v3
- name: Get Commiter Info
id: commiter_info
run: |
echo "NAME=$( git log -n 1 --pretty=format:%an )" >> $GITHUB_OUTPUT
echo "MAIL=$( git log -n 1 --pretty=format:%ae )" >> $GITHUB_OUTPUT
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version-file: '${{ gitea.workspace }}/scnserver/go.mod'
cache: false
- name: Print Go Version
run: go version
- name: Run tests
run: cd "${{ gitea.workspace }}/scnserver" && make dgi && make swagger && SCN_TEST_LOGLEVEL=WARN make test
- name: Send failure mail
if: failure()
uses: dawidd6/action-send-mail@v3
with:
server_address: smtp.fastmail.com
server_port: 465
secure: true
username: ${{secrets.MAIL_USERNAME}}
password: ${{secrets.MAIL_PASSWORD}}
subject: Pipeline on '${{ gitea.repository }}' failed
to: ${{ steps.commiter_info.outputs.MAIL }}
from: Gitea Actions <gitea_actions@blackforestbytes.de>
body: "Go to https://gogs.blackforestbytes.com/${{ gitea.repository }}/actions"
deploy_server:
name: Deploy to Server
needs: [build_server, test_server]
runs-on: ubuntu-latest
if: >-
!cancelled() &&
!contains(github.event.head_commit.message, '[skip-ci]') &&
!contains(github.event.head_commit.message, '[skip-deployment]') &&
needs.build_server.result == 'success' &&
(needs.test_server.result == 'skipped' || needs.test_server.result == 'success')
steps:
- name: Execute deploy on remote (via ssh)
uses: appleboy/ssh-action@v1.0.0
with:
host: simplecloudnotifier.de
username: bfb-deploy-bot
port: 4477
key: "${{ secrets.SSH_KEY_BFBDEPLOYBOT }}"
script: cd /var/docker/deploy-scripts/simplecloudnotifier && ./deploy.sh master "${{ gitea.sha }}" || exit 1

1
.gitignore vendored
View File

@ -1 +0,0 @@
.aider*

View File

@ -1,22 +0,0 @@
SimpleCloudNotifier [![Get it in Google Play](data/README/badge_google.png)](https://play.google.com/store/apps/details?id=com.blackforestbytes.simplecloudnotifier)
===================
> SimpleCloudNotifier is an app to display messages that you can send to your phone with simple POST requests.
>
> After you start the app it generates a UserID and a UserSecret.
> Now you can send your message to https://simplecloudnotifier.blackforestbytes.com/send.php and a notification will be pushed to your phone.
> (see https://simplecloudnotifier.blackforestbytes.com/ for an example with curl)
>
>
> Use it to
> - send yourself automated messages from cron jobs
> - notify yourself when long-running scripts finish
> - send server error messages directly to your phone
> - integrate with other online services
>
> The possibilities are endless*
>
> \* Disclaimer: Developer does not actually guarantee endless possibilities
![](store/screenshot_1.png) ![](store/screenshot_2.png) ![](store/screenshot_3.png)

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AndroidProjectSystem">
<option name="providerId" value="com.android.tools.idea.GradleProjectSystem" />
</component>
</project>

32
android/.idea/assetWizardSettings.xml generated Normal file
View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="WizardSettings">
<option name="children">
<map>
<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_garbage" />
<entry key="sourceFile" value="C:\Users\Mike\Downloads\garbage.svg" />
</map>
</option>
</PersistentState>
</value>
</entry>
</map>
</option>
</PersistentState>
</value>
</entry>
</map>
</option>
</component>
</project>

View File

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

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetDropDown">
<value>
<entry key="app">
<State />
</entry>
</value>
</component>
</project>

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetSelector">
<selectionStates>
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
</SelectionState>
</selectionStates>
</component>
</project>

View File

@ -1,40 +0,0 @@
<?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,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectMigrations">
<option name="MigrateToGradleLocalJavaHome">
<set>
<option value="$PROJECT_DIR$" />
</set>
</option>
</component>
</project>

View File

@ -3,14 +3,9 @@
<component name="RunConfigurationProducerService"> <component name="RunConfigurationProducerService">
<option name="ignoredProducers"> <option name="ignoredProducers">
<set> <set>
<option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" /> <option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" /> <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
<option value="com.intellij.execution.junit.PatternConfigurationProducer" /> <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
<option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
<option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
<option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
</set> </set>
</option> </option>
</component> </component>

View File

@ -1,7 +1,7 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
android { android {
compileSdkVersion 30 compileSdkVersion 28
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 30 targetSdkVersion 28
versionCode vNumber versionCode vNumber
versionName vName versionName vName
} }
@ -30,88 +30,81 @@ android {
targetCompatibility 1.8 targetCompatibility 1.8
sourceCompatibility 1.8 sourceCompatibility 1.8
} }
namespace 'com.blackforestbytes.simplecloudnotifier'
} }
dependencies { dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar']) implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.2.0' implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4' implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.recyclerview:recyclerview:1.1.0' implementation 'androidx.recyclerview:recyclerview:1.0.0'
implementation 'androidx.recyclerview:recyclerview:1.1.0' implementation 'androidx.recyclerview:recyclerview:1.0.0'
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'
implementation 'com.google.android.material:material:1.2.1' implementation 'com.google.android.material:material:1.0.0'
implementation 'com.google.firebase:firebase-core:18.0.0' implementation 'com.google.firebase:firebase-core:16.0.4'
implementation 'com.google.firebase:firebase-messaging:21.0.0' implementation 'com.google.firebase:firebase-messaging:17.3.4'
implementation 'com.google.android.gms:play-services-ads:19.5.0' implementation 'com.google.android.gms:play-services-ads:17.1.0'
implementation 'com.android.billingclient:billing:3.0.1' implementation 'com.android.billingclient:billing:1.2'
implementation 'com.squareup.okhttp3:okhttp:4.9.0' implementation 'com.squareup.okhttp3:okhttp:3.10.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.10.7.1'
} }
apply plugin: 'com.google.gms.google-services' apply plugin: 'com.google.gms.google-services'
tasks.register("updateVersion") { task updateVersion << {
group = 'Custom' def lastTag = ['git', 'describe', "--abbrev=0", "--tags"].execute().text.trim()
doLast { def versionPropsFile = file('version.properties')
def lastTag = ['git', 'describe', "--abbrev=0", "--tags"].execute().text.trim() if (!versionPropsFile.canRead()) throw new FileNotFoundException("Could not read version.properties!")
Properties versionProps = new Properties()
new FileInputStream(versionPropsFile).withCloseable { fis -> versionProps.load(fis) }
def versionPropsFile = file('version.properties') def matcher = lastTag =~ /^v([0-9]+)\.([0-9]+)\.([0-9]+)$/
if (!versionPropsFile.canRead()) throw new FileNotFoundException("Could not read version.properties!")
Properties versionProps = new Properties()
new FileInputStream(versionPropsFile).withCloseable { fis -> versionProps.load(fis) }
def matcher = lastTag =~ /^v([0-9]+)\.([0-9]+)\.([0-9]+)$/ if (!matcher.matches()) throw new Exception("Last Tag ('" + lastTag + "') has invalid format :(")
if (!matcher.matches()) throw new Exception("Last Tag ('" + lastTag + "') has invalid format :(") def vName = (matcher[0][1] as Integer) + "." + (matcher[0][2] as Integer) + "." + (matcher[0][3] as Integer)
def vCode = versionProps['VERSION_CODE'] as Integer
def vName = (matcher[0][1] as Integer) + "." + (matcher[0][2] as Integer) + "." + (matcher[0][3] as Integer) if (new File(".do_publish_beta_release").exists()) new File(".do_publish_beta_release").delete()
def vCode = versionProps['VERSION_CODE'] as Integer if (new File(".do_publish_prod_release").exists()) new File(".do_publish_prod_release").delete()
if (new File(".do_publish_beta_release").exists()) new File(".do_publish_beta_release").delete() if (vName == versionProps['VERSION_NAME'].toString()) {
if (new File(".do_publish_prod_release").exists()) new File(".do_publish_prod_release").delete() 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 (vName == versionProps['VERSION_NAME'].toString()) { vCode++
println "This version was already built - skip deployment" new File(".do_publish_prod_release").createNewFile()
} else if (vName.endsWith(".0")) {
println ""
println "====================================================================="
println "====================================================================="
println "(!) This is a new PRODUCTION release - create deployment trigger file"
println "====================================================================="
println "====================================================================="
println ""
vCode++ versionProps['VERSION_NAME'] = vName.toString()
new File(".do_publish_prod_release").createNewFile() versionProps['VERSION_CODE'] = vCode.toString()
versionProps['VERSION_NAME'] = vName.toString() versionPropsFile.newWriter().withCloseable { w -> versionProps.store(w, null) }
versionProps['VERSION_CODE'] = vCode.toString() } else {
println ""
println "==============================================================="
println "(!) This is a new beta release - create deployment trigger file"
println "==============================================================="
println ""
versionPropsFile.newWriter().withCloseable { w -> versionProps.store(w, null) } vCode++
} else { new File(".do_publish_beta_release").createNewFile()
println ""
println "==============================================================="
println "(!) This is a new beta release - create deployment trigger file"
println "==============================================================="
println ""
vCode++ versionProps['VERSION_NAME'] = vName.toString()
new File(".do_publish_beta_release").createNewFile() versionProps['VERSION_CODE'] = vCode.toString()
versionProps['VERSION_NAME'] = vName.toString() versionPropsFile.newWriter().withCloseable { w -> versionProps.store(w, null) }
versionProps['VERSION_CODE'] = vCode.toString()
versionPropsFile.newWriter().withCloseable { w -> versionProps.store(w, null) }
}
} }
} }

View File

@ -1,67 +1,43 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest
xmlns:tools="http://schemas.android.com/tools"> xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.blackforestbytes.simplecloudnotifier">
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="com.android.vending.BILLING" /> <uses-permission android:name="com.android.vending.BILLING" />
<application <application
android:name=".SCNApp"
android:allowBackup="false" android:allowBackup="false"
android:name="SCNApp"
android:icon="@drawable/icon" android:icon="@drawable/icon"
android:label="@string/app_name" android:label="@string/app_name"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/AppTheme" android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning"> tools:ignore="GoogleAppIndexingWarning">
<activity android:name=".view.MainActivity"> <activity android:name=".view.MainActivity">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<provider <meta-data android:name="com.google.firebase.messaging.default_notification_icon" android:resource="@drawable/icon" />
android:name="androidx.core.content.FileProvider" <meta-data android:name="com.google.firebase.messaging.default_notification_color" android:resource="@color/colorAccent" />
android:authorities="com.blackforestbytes.simplecloudnotifier.fileprovider" <meta-data android:name="com.google.android.gms.ads.AD_MANAGER_APP" android:value="true"/>
android:grantUriPermissions="true" <meta-data android:name="com.google.android.gms.ads.APPLICATION_ID" android:value="ca-app-pub-3320562328966175~7579972005"/>
android:exported="false">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/filepaths" />
</provider>
<service android:name=".service.FBMService" android:exported="false">
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@drawable/icon" />
<meta-data
android:name="com.google.firebase.messaging.default_notification_color"
android:resource="@color/colorAccent" />
<meta-data
android:name="com.google.android.gms.ads.AD_MANAGER_APP"
android:value="true" />
<meta-data
android:name="com.google.android.gms.ads.APPLICATION_ID"
android:value="ca-app-pub-3320562328966175~7579972005" />
<service
android:name=".service.FBMService"
android:exported="false">
<intent-filter> <intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" /> <action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter> </intent-filter>
</service> </service>
<receiver <receiver android:name=".service.BroadcastReceiverService" android:exported="false" />
android:name=".service.BroadcastReceiverService"
android:exported="false" />
<activity
android:name=".view.debug.QueryLogActivity"
android:label="@string/title_activity_query_log"
android:theme="@style/AppTheme" />
<activity android:name=".view.debug.SingleQueryLogActivity" />
</application> </application>
</manifest> </manifest>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

View File

@ -5,7 +5,6 @@ import android.content.Context;
import android.widget.Toast; import android.widget.Toast;
import com.android.billingclient.api.BillingClient; import com.android.billingclient.api.BillingClient;
import com.blackforestbytes.simplecloudnotifier.model.QueryLog;
import com.blackforestbytes.simplecloudnotifier.view.AccountFragment; import com.blackforestbytes.simplecloudnotifier.view.AccountFragment;
import com.blackforestbytes.simplecloudnotifier.view.MainActivity; import com.blackforestbytes.simplecloudnotifier.view.MainActivity;
import com.blackforestbytes.simplecloudnotifier.view.TabAdapter; import com.blackforestbytes.simplecloudnotifier.view.TabAdapter;
@ -100,5 +99,14 @@ public class SCNApp extends Application implements LifecycleObserver
} }
} }
//TODO: Config for collapsed line count /*
//TODO: Sometimes ads but promode ==TODO==
[ ] - test notification channels
[ ] - startup time
[ ] - periodically get non-ack (option - even when not in-app)
[ ] - publish (+ HN post ?)
*/

View File

@ -24,11 +24,6 @@ public final class CollectionHelper
return output; return output;
} }
public static <T> void sort_inplace(List<T> input, Comparator<T> comparator)
{
Collections.sort(input, comparator);
}
public static <T, U extends Comparable<U>> List<T> sort(List<T> input, Func1to1<T, U> mapper) public static <T, U extends Comparable<U>> List<T> sort(List<T> input, Func1to1<T, U> mapper)
{ {
return sort(input, mapper, 1); return sort(input, mapper, 1);

View File

@ -1,19 +0,0 @@
package com.blackforestbytes.simplecloudnotifier.lib.datatypes;
public class Tuple5<T1, T2, T3, T4, T5>
{
public final T1 Item1;
public final T2 Item2;
public final T3 Item3;
public final T4 Item4;
public final T5 Item5;
public Tuple5(T1 i1, T2 i2, T3 i3, T4 i4, T5 i5)
{
Item1 = i1;
Item2 = i2;
Item3 = i3;
Item4 = i4;
Item5 = i5;
}
}

View File

@ -1,6 +0,0 @@
package com.blackforestbytes.simplecloudnotifier.lib.lambda;
@FunctionalInterface
public interface Func5to0<TInput1, TInput2, TInput3, TInput4, TInput5> {
void invoke(TInput1 value1, TInput2 value2, TInput3 value3, TInput4 value4, TInput5 value5);
}

View File

@ -9,8 +9,6 @@ import java.util.TimeZone;
public class CMessage public class CMessage
{ {
public boolean IsExpandedInAdapter = false;
public final long SCN_ID; public final long SCN_ID;
public final long Timestamp; public final long Timestamp;
public final String Title; public final String Title;

View File

@ -3,21 +3,17 @@ package com.blackforestbytes.simplecloudnotifier.model;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import com.blackforestbytes.simplecloudnotifier.lib.collections.CollectionHelper;
import com.blackforestbytes.simplecloudnotifier.lib.string.Str; import com.blackforestbytes.simplecloudnotifier.lib.string.Str;
import com.blackforestbytes.simplecloudnotifier.view.MessageAdapter; import com.blackforestbytes.simplecloudnotifier.view.MessageAdapter;
import com.blackforestbytes.simplecloudnotifier.SCNApp; import com.blackforestbytes.simplecloudnotifier.SCNApp;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
public class CMessageList public class CMessageList
{ {
private final Object msg_lock = new Object();
public ArrayList<CMessage> Messages; public ArrayList<CMessage> Messages;
public Set<String> AllAcks; public Set<String> AllAcks;
@ -36,31 +32,23 @@ public class CMessageList
private CMessageList() private CMessageList()
{ {
reloadPrefs(); Messages = new ArrayList<>();
} AllAcks = new HashSet<>();
public void reloadPrefs() SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("CMessageList", Context.MODE_PRIVATE);
{ int count = sharedPref.getInt("message_count", 0);
synchronized (msg_lock) for (int i=0; i < count; i++)
{ {
Messages = new ArrayList<>(); long time = sharedPref.getLong("message["+i+"].timestamp", 0);
AllAcks = new HashSet<>(); String title = sharedPref.getString("message["+i+"].title", "");
String content = sharedPref.getString("message["+i+"].content", "");
PriorityEnum prio = PriorityEnum.parseAPI(sharedPref.getInt("message["+i+"].priority", 1));
long scnid = sharedPref.getLong("message["+i+"].scnid", 0);
SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("CMessageList", Context.MODE_PRIVATE); Messages.add(new CMessage(scnid, time, title, content, prio));
int count = sharedPref.getInt("message_count", 0);
for (int i=0; i < count; i++)
{
long time = sharedPref.getLong("message["+i+"].timestamp", 0);
String title = sharedPref.getString("message["+i+"].title", "");
String content = sharedPref.getString("message["+i+"].content", "");
PriorityEnum prio = PriorityEnum.parseAPI(sharedPref.getInt("message["+i+"].priority", 1));
long scnid = sharedPref.getLong("message["+i+"].scnid", 0);
Messages.add(new CMessage(scnid, time, title, content, prio));
}
AllAcks = sharedPref.getStringSet("acks", new HashSet<>());
} }
AllAcks = sharedPref.getStringSet("acks", new HashSet<>());
} }
public CMessage add(final long scnid, final long time, final String title, final String content, final PriorityEnum pe) public CMessage add(final long scnid, final long time, final String title, final String content, final PriorityEnum pe)
@ -72,44 +60,29 @@ public class CMessageList
SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("CMessageList", Context.MODE_PRIVATE); SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("CMessageList", Context.MODE_PRIVATE);
int count = sharedPref.getInt("message_count", 0); int count = sharedPref.getInt("message_count", 0);
synchronized (msg_lock) SharedPreferences.Editor e = sharedPref.edit();
{
Messages.add(msg);
AllAcks.add(Long.toHexString(msg.SCN_ID));
while (Messages.size()>SCNSettings.inst().LocalCacheSize) Messages.remove(0); Messages.add(msg);
} AllAcks.add(Long.toHexString(msg.SCN_ID));
if (Messages.size()>1 && Messages.get(Messages.size()-2).Timestamp < msg.Timestamp) while (Messages.size()>SCNSettings.inst().LocalCacheSize) Messages.remove(0);
{
// quick save
SharedPreferences.Editor e = sharedPref.edit(); e.putInt( "message_count", count+1);
e.putLong( "message["+count+"].timestamp", time);
e.putString("message["+count+"].title", title);
e.putString("message["+count+"].content", content);
e.putInt( "message["+count+"].priority", pe.ID);
e.putLong( "message["+count+"].scnid", scnid);
e.putInt( "message_count", count+1); e.putStringSet("acks", AllAcks);
e.putLong( "message["+count+"].timestamp", time);
e.putString("message["+count+"].title", title);
e.putString("message["+count+"].content", content);
e.putInt( "message["+count+"].priority", pe.ID);
e.putLong( "message["+count+"].scnid", scnid);
e.putStringSet("acks", AllAcks);
e.apply();
}
else
{
// full save
fullSave(); // does sort in here
}
e.apply();
for (WeakReference<MessageAdapter> ref : _listener) for (WeakReference<MessageAdapter> ref : _listener)
{ {
MessageAdapter a = ref.get(); MessageAdapter a = ref.get();
if (a == null) continue; if (a == null) continue;
a.customNotifyDataSetChanged(); a.customNotifyItemInserted(count);
a.scrollToTop(); a.scrollToTop();
} }
CleanUpListener(); CleanUpListener();
@ -117,12 +90,9 @@ public class CMessageList
if (!run) if (!run)
{ {
synchronized (msg_lock) Messages.add(new CMessage(scnid, time, title, content, pe));
{ AllAcks.add(Long.toHexString(msg.SCN_ID));
Messages.add(new CMessage(scnid, time, title, content, pe)); fullSave();
AllAcks.add(Long.toHexString(msg.SCN_ID));
}
fullSave(); // does sort in here
} }
return msg; return msg;
@ -144,39 +114,31 @@ public class CMessageList
public void fullSave() public void fullSave()
{ {
synchronized (msg_lock) SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("CMessageList", Context.MODE_PRIVATE);
SharedPreferences.Editor e = sharedPref.edit();
e.clear();
e.putInt("message_count", Messages.size());
for (int i = 0; i < Messages.size(); i++)
{ {
CollectionHelper.sort_inplace(Messages, (a,b) -> Long.compare(a.Timestamp, b.Timestamp)); e.putLong( "message["+i+"].timestamp", Messages.get(i).Timestamp);
e.putString("message["+i+"].title", Messages.get(i).Title);
SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("CMessageList", Context.MODE_PRIVATE); e.putString("message["+i+"].content", Messages.get(i).Content);
SharedPreferences.Editor e = sharedPref.edit(); e.putInt( "message["+i+"].priority", Messages.get(i).Priority.ID);
e.putLong( "message["+i+"].scnid", Messages.get(i).SCN_ID);
e.clear();
e.putInt("message_count", Messages.size());
for (int i = 0; i < Messages.size(); i++)
{
e.putLong( "message["+i+"].timestamp", Messages.get(i).Timestamp);
e.putString("message["+i+"].title", Messages.get(i).Title);
e.putString("message["+i+"].content", Messages.get(i).Content);
e.putInt( "message["+i+"].priority", Messages.get(i).Priority.ID);
e.putLong( "message["+i+"].scnid", Messages.get(i).SCN_ID);
}
e.putStringSet("acks", AllAcks);
e.apply();
} }
e.putStringSet("acks", AllAcks);
e.apply();
} }
public CMessage tryGet(int pos) public CMessage tryGet(int pos)
{ {
synchronized (msg_lock) if (pos < 0 || pos >= Messages.size()) return null;
{ return Messages.get(pos);
if (pos < 0 || pos >= Messages.size()) return null;
return Messages.get(pos);
}
} }
public CMessage tryGetFromBack(int pos) public CMessage tryGetFromBack(int pos)
@ -186,10 +148,7 @@ public class CMessageList
public int size() public int size()
{ {
synchronized (msg_lock) return Messages.size();
{
return Messages.size();
}
} }
public void register(MessageAdapter adp) public void register(MessageAdapter adp)
@ -208,30 +167,18 @@ public class CMessageList
public boolean isAck(long id) public boolean isAck(long id)
{ {
synchronized (msg_lock) return AllAcks.contains(Long.toHexString(id));
{
return AllAcks.contains(Long.toHexString(id));
}
} }
public CMessage removeFromBack(int pos) public void remove(int index)
{ {
CMessage r; Messages.remove(index);
synchronized (msg_lock) fullSave();
{
int index = Messages.size() - pos - 1;
r = Messages.remove(index);
}
fullSave(); // does sort in here
return r;
} }
public void insert(int index, CMessage item) public void insert(int index, CMessage item)
{ {
synchronized (msg_lock) Messages.add(index, item);
{ fullSave();
Messages.add(index, item);
}
fullSave(); // does sort in here
} }
} }

View File

@ -1,58 +0,0 @@
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; // ????
}
}

View File

@ -1,69 +0,0 @@
package com.blackforestbytes.simplecloudnotifier.model;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Log;
import com.blackforestbytes.simplecloudnotifier.SCNApp;
import com.blackforestbytes.simplecloudnotifier.lib.collections.CollectionHelper;
import java.util.ArrayList;
import java.util.List;
public class QueryLog
{
private final static int MAX_HISTORY_SIZE = 192;
private static QueryLog _instance;
public static QueryLog inst() { if (_instance == null) synchronized (QueryLog.class) { if (_instance == null) _instance = new QueryLog(); } return _instance; }
private QueryLog(){ reloadPrefs(); }
private final List<SingleQuery> history = new ArrayList<>();
public synchronized void add(SingleQuery r)
{
history.add(r);
while (history.size() > MAX_HISTORY_SIZE) history.remove(0);
save();
}
public synchronized List<SingleQuery> get()
{
List<SingleQuery> r = new ArrayList<>(history);
CollectionHelper.sort_inplace(r, (o1, o2) -> (-1) * o1.Timestamp.compareTo(o2.Timestamp));
return r;
}
public synchronized void save()
{
SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("QueryLog", Context.MODE_PRIVATE);
SharedPreferences.Editor e = sharedPref.edit();
e.clear();
e.putInt("history_count", history.size());
for (int i = 0; i < history.size(); i++) history.get(i).save(e, "message["+(i+1000)+"]");
e.apply();
}
public synchronized void reloadPrefs()
{
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());
}
}
}

View File

@ -6,28 +6,23 @@ 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.installations.FirebaseInstallations; import com.google.firebase.iid.FirebaseInstanceId;
public class SCNSettings public class SCNSettings
{ {
private final static Object _lock = new Object(); private final static Object _lock = new Object();
private static volatile SCNSettings _inst = null; private static SCNSettings _inst = null;
public static SCNSettings inst() public static SCNSettings inst()
{ {
SCNSettings local = _inst; synchronized (_lock)
if (local == null)
{ {
synchronized (_lock) if (_inst != null) return _inst;
{ return _inst = new SCNSettings();
local = _inst;
if (local == null) _inst = local = new SCNSettings();
}
} }
return local;
} }
// ------------------------------------------------------------ // ------------------------------------------------------------
@ -52,8 +47,6 @@ public class SCNSettings
public boolean Enabled = true; public boolean Enabled = true;
public int LocalCacheSize = 500; public int LocalCacheSize = 500;
public boolean EnableDeleteSwipe = false;
public int PreviewLineCount = 6;
public final NotificationSettings PriorityLow = new NotificationSettings(PriorityEnum.LOW); public final NotificationSettings PriorityLow = new NotificationSettings(PriorityEnum.LOW);
public final NotificationSettings PriorityNorm = new NotificationSettings(PriorityEnum.NORMAL); public final NotificationSettings PriorityNorm = new NotificationSettings(PriorityEnum.NORMAL);
@ -62,11 +55,6 @@ 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);
@ -82,8 +70,6 @@ public class SCNSettings
Enabled = sharedPref.getBoolean("app_enabled", Enabled); Enabled = sharedPref.getBoolean("app_enabled", Enabled);
LocalCacheSize = sharedPref.getInt("local_cache_size", LocalCacheSize); LocalCacheSize = sharedPref.getInt("local_cache_size", LocalCacheSize);
EnableDeleteSwipe = sharedPref.getBoolean("do_del_swipe", EnableDeleteSwipe);
PreviewLineCount = sharedPref.getInt("preview_line_count", PreviewLineCount);
PriorityLow.EnableLED = sharedPref.getBoolean("priority_low:enabled_led", PriorityLow.EnableLED); PriorityLow.EnableLED = sharedPref.getBoolean("priority_low:enabled_led", PriorityLow.EnableLED);
PriorityLow.EnableSound = sharedPref.getBoolean("priority_low:enabled_sound", PriorityLow.EnableSound); PriorityLow.EnableSound = sharedPref.getBoolean("priority_low:enabled_sound", PriorityLow.EnableSound);
@ -127,14 +113,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);
e.putBoolean("do_del_swipe", EnableDeleteSwipe);
e.putInt( "preview_line_count", PreviewLineCount);
e.putBoolean("priority_low:enabled_led", PriorityLow.EnableLED); e.putBoolean("priority_low:enabled_led", PriorityLow.EnableLED);
e.putBoolean("priority_low:enabled_sound", PriorityLow.EnableSound); e.putBoolean("priority_low:enabled_sound", PriorityLow.EnableSound);
@ -174,21 +155,19 @@ public class SCNSettings
return user_id>=0 && user_key != null && !user_key.isEmpty(); return user_id>=0 && user_key != null && !user_key.isEmpty();
} }
public String createOnlineURL(boolean longurl) public String createOnlineURL()
{ {
String base = longurl ? ServerCommunication.PAGE_URL_LONG : ServerCommunication.PAGE_URL_SHORT; if (!isConnected()) return ServerCommunication.BASE_URL + "index.php";
return ServerCommunication.BASE_URL + "index.php?preset_user_id="+user_id+"&preset_user_key="+user_key;
if (!isConnected()) return base;
return base + "index.php?preset_user_id="+user_id+"&preset_user_key="+user_key;
} }
public void setServerToken(String token, View loader, boolean force) public void setServerToken(String token, View loader)
{ {
if (isConnected()) if (isConnected())
{ {
fcm_token_local = token; fcm_token_local = token;
save(); save();
if (!fcm_token_local.equals(fcm_token_server) || force) ServerCommunication.updateFCMToken(user_id, user_key, fcm_token_local, loader); if (!fcm_token_local.equals(fcm_token_server)) ServerCommunication.updateFCMToken(user_id, user_key, fcm_token_local, loader);
} }
else else
{ {
@ -200,12 +179,13 @@ public class SCNSettings
} }
// called at app start // called at app start
public void work(Activity a, boolean force) public void work(Activity a)
{ {
FirebaseInstallations.getInstance().getId().addOnSuccessListener(a, newToken -> FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(a, instanceIdResult ->
{ {
Log.d("FB::GetInstanceId", newToken); String newToken = instanceIdResult.getToken();
SCNSettings.inst().setServerToken(newToken, null, force); Log.e("FB::GetInstanceId", newToken);
SCNSettings.inst().setServerToken(newToken, null);
}).addOnCompleteListener(r -> }).addOnCompleteListener(r ->
{ {
if (isConnected()) ServerCommunication.info(user_id, user_key, null); if (isConnected()) ServerCommunication.info(user_id, user_key, null);
@ -231,15 +211,16 @@ public class SCNSettings
if (promode_server != promode_local) updateProState(loader); if (promode_server != promode_local) updateProState(loader);
if (!Str.equals(fcm_token_local, fcm_token_server)) work(a, false); if (!Str.equals(fcm_token_local, fcm_token_server)) work(a);
} }
else else
{ {
// get token then register // get token then register
FirebaseInstallations.getInstance().getId().addOnSuccessListener(a, newToken -> FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(a, instanceIdResult ->
{ {
Log.d("FB::GetInstanceId", newToken); String newToken = instanceIdResult.getToken();
SCNSettings.inst().setServerToken(newToken, loader, false); // does register in here Log.e("FB::GetInstanceId", newToken);
SCNSettings.inst().setServerToken(newToken, loader); // does register in here
}).addOnCompleteListener(r -> }).addOnCompleteListener(r ->
{ {
if (isConnected()) ServerCommunication.info(user_id, user_key, null); // info again for safety if (isConnected()) ServerCommunication.info(user_id, user_key, null); // info again for safety
@ -249,17 +230,14 @@ public class SCNSettings
public void updateProState(View loader) public void updateProState(View loader)
{ {
Tuple3<Boolean, Boolean, String> state = IABService.inst().getPurchaseCachedExtended(IABService.IAB_PRO_MODE); Purchase purch = IABService.inst().getPurchaseCached(IABService.IAB_PRO_MODE);
if (!state.Item2) return; // not initialized boolean promode_real = (purch != null);
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

@ -4,11 +4,9 @@ import android.util.Log;
import android.view.View; import android.view.View;
import com.blackforestbytes.simplecloudnotifier.SCNApp; import com.blackforestbytes.simplecloudnotifier.SCNApp;
import com.blackforestbytes.simplecloudnotifier.lib.lambda.Func5to0;
import com.blackforestbytes.simplecloudnotifier.lib.string.Str; import com.blackforestbytes.simplecloudnotifier.lib.string.Str;
import com.blackforestbytes.simplecloudnotifier.service.FBMService; import com.blackforestbytes.simplecloudnotifier.service.FBMService;
import org.joda.time.Instant;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
@ -26,9 +24,7 @@ import okhttp3.ResponseBody;
public class ServerCommunication public class ServerCommunication
{ {
public static final String PAGE_URL_LONG = "https://simplecloudnotifier.blackforestbytes.com/"; public static final String BASE_URL = /*SCNApp.LOCAL_DEBUG ? "http://localhost:1010/" : */"https://scn.blackforestbytes.com/api/";
public static final String PAGE_URL_SHORT = "https://scn.blackforestbytes.com/";
public static final String BASE_URL = "https://scn.blackforestbytes.com/api/";
private static final OkHttpClient client = new OkHttpClient(); private static final OkHttpClient client = new OkHttpClient();
@ -47,28 +43,27 @@ public class ServerCommunication
@Override @Override
public void onFailure(Call call, IOException e) public void onFailure(Call call, IOException e)
{ {
handleError("register", call, null, Str.Empty, true, e); Log.e("SC:register", e.toString());
SCNApp.showToast("Communication with server failed", 4000);
SCNApp.runOnUiThread(() -> { if (loader!=null)loader.setVisibility(View.GONE); }); SCNApp.runOnUiThread(() -> { if (loader!=null)loader.setVisibility(View.GONE); });
} }
@Override @Override
public void onResponse(Call call, Response response) public void onResponse(Call call, Response response)
{ {
String r = Str.Empty;
try (ResponseBody responseBody = response.body()) 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"); if (responseBody == null) throw new IOException("No response");
r = responseBody.string(); String r = responseBody.string();
Log.d("Server::Response", request.url().toString()+"\n"+r); Log.d("Server::Response", r);
JSONObject json = (JSONObject) new JSONTokener(r).nextValue(); JSONObject json = (JSONObject) new JSONTokener(r).nextValue();
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;
} }
@ -81,12 +76,11 @@ public class ServerCommunication
SCNSettings.inst().save(); SCNSettings.inst().save();
SCNApp.refreshAccountTab(); SCNApp.refreshAccountTab();
handleSuccess("register", call, response, r);
} }
catch (Exception e) catch (Exception e)
{ {
handleError("register", call, response, r, false, e); Log.e("SC:register", e.toString());
SCNApp.showToast("Communication with server failed", 4000);
} }
finally finally
{ {
@ -97,7 +91,8 @@ public class ServerCommunication
} }
catch (Exception e) catch (Exception e)
{ {
handleError("register", null, null, Str.Empty, false, e); Log.e("SC:register", e.toString());
SCNApp.showToast("Communication with server failed", 4000);
} }
} }
@ -106,7 +101,7 @@ public class ServerCommunication
try try
{ {
Request request = new Request.Builder() Request request = new Request.Builder()
.url(BASE_URL + "update.php?user_id="+id+"&user_key="+key+"&fcm_token="+token) .url(BASE_URL + "updateFCMToken.php?user_id="+id+"&user_key="+key+"&fcm_token="+token)
.build(); .build();
client.newCall(request).enqueue(new Callback() client.newCall(request).enqueue(new Callback()
@ -114,28 +109,27 @@ public class ServerCommunication
@Override @Override
public void onFailure(Call call, IOException e) public void onFailure(Call call, IOException e)
{ {
handleError("update<1>", call, null, Str.Empty, true, e); Log.e("SC:update_1", e.toString());
SCNApp.showToast("Communication with server failed", 4000);
SCNApp.runOnUiThread(() -> { if (loader!=null)loader.setVisibility(View.GONE); }); SCNApp.runOnUiThread(() -> { if (loader!=null)loader.setVisibility(View.GONE); });
} }
@Override @Override
public void onResponse(Call call, Response response) public void onResponse(Call call, Response response)
{ {
String r = Str.Empty;
try (ResponseBody responseBody = response.body()) 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"); if (responseBody == null) throw new IOException("No response");
r = responseBody.string(); String r = responseBody.string();
Log.d("Server::Response", request.url().toString()+"\n"+r); Log.d("Server::Response", r);
JSONObject json = (JSONObject) new JSONTokener(r).nextValue(); JSONObject json = (JSONObject) new JSONTokener(r).nextValue();
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;
} }
@ -148,12 +142,10 @@ public class ServerCommunication
SCNSettings.inst().save(); SCNSettings.inst().save();
SCNApp.refreshAccountTab(); SCNApp.refreshAccountTab();
handleSuccess("update<1>", call, response, r);
} }
catch (Exception e) catch (Exception e)
{ {
handleError("update<1>", call, response, r, false, e); Log.e("SC:update_1", e.toString());
SCNApp.showToast("Communication with server failed", 4000); SCNApp.showToast("Communication with server failed", 4000);
} }
finally finally
@ -165,7 +157,8 @@ public class ServerCommunication
} }
catch (Exception e) catch (Exception e)
{ {
handleError("update<1>", null, null, Str.Empty, false, e); Log.e("SC:update_1", e.toString());
SCNApp.showToast("Communication with server failed", 4000);
} }
} }
@ -174,35 +167,30 @@ public class ServerCommunication
try try
{ {
Request request = new Request.Builder() Request request = new Request.Builder()
.url(BASE_URL + "update.php?user_id=" + id + "&user_key=" + key) .url(BASE_URL + "updateFCMToken.php?user_id=" + id + "&user_key=" + key)
.build(); .build();
client.newCall(request).enqueue(new Callback() { client.newCall(request).enqueue(new Callback() {
@Override @Override
public void onFailure(Call call, IOException e) public void onFailure(Call call, IOException e) {
{ Log.e("SC:update_2", e.toString());
handleError("update<1>", call, null, Str.Empty, true, e);
SCNApp.showToast("Communication with server failed", 4000); SCNApp.showToast("Communication with server failed", 4000);
} }
@Override @Override
public void onResponse(Call call, Response response) public void onResponse(Call call, Response response) {
{ try (ResponseBody responseBody = response.body()) {
String r = Str.Empty;
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(); String r = responseBody.string();
Log.d("Server::Response", request.url().toString()+"\n"+r); Log.d("Server::Response", r);
JSONObject json = (JSONObject) new JSONTokener(r).nextValue(); JSONObject json = (JSONObject) new JSONTokener(r).nextValue();
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;
} }
@ -214,16 +202,10 @@ public class ServerCommunication
SCNSettings.inst().save(); SCNSettings.inst().save();
SCNApp.refreshAccountTab(); SCNApp.refreshAccountTab();
} catch (Exception e) {
handleSuccess("update<2>", call, response, r); Log.e("SC:update_2", e.toString());
}
catch (Exception e)
{
handleError("update<2>", call, response, r, false, e);
SCNApp.showToast("Communication with server failed", 4000); SCNApp.showToast("Communication with server failed", 4000);
} } finally {
finally
{
SCNApp.runOnUiThread(() -> { SCNApp.runOnUiThread(() -> {
if (loader != null) loader.setVisibility(View.GONE); if (loader != null) loader.setVisibility(View.GONE);
}); });
@ -233,7 +215,8 @@ public class ServerCommunication
} }
catch (Exception e) catch (Exception e)
{ {
handleError("update<2>", null, null, Str.Empty, false, e); Log.e("SC:update_2", e.toString());
SCNApp.showToast("Communication with server failed", 4000);
} }
} }
@ -248,31 +231,28 @@ public class ServerCommunication
client.newCall(request).enqueue(new Callback() { client.newCall(request).enqueue(new Callback() {
@Override @Override
public void onFailure(Call call, IOException e) { public void onFailure(Call call, IOException e) {
handleError("info", call, null, Str.Empty, true, e); Log.e("SC:info", e.toString());
SCNApp.showToast("Communication with server failed", 4000);
SCNApp.runOnUiThread(() -> { SCNApp.runOnUiThread(() -> {
if (loader != null) loader.setVisibility(View.GONE); if (loader != null) loader.setVisibility(View.GONE);
}); });
} }
@Override @Override
public void onResponse(Call call, Response response) public void onResponse(Call call, Response response) {
{ try (ResponseBody responseBody = response.body()) {
String r = Str.Empty;
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(); String r = responseBody.string();
Log.d("Server::Response", request.url().toString()+"\n"+r); Log.d("Server::Response", r);
JSONObject json = (JSONObject) new JSONTokener(r).nextValue(); JSONObject json = (JSONObject) new JSONTokener(r).nextValue();
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);
@ -305,23 +285,21 @@ public class ServerCommunication
if (json_int(json, "unack_count")>0) ServerCommunication.requery(id, key, loader); if (json_int(json, "unack_count")>0) ServerCommunication.requery(id, key, loader);
handleSuccess("info", call, response, r); } catch (Exception e) {
} Log.e("SC:info", e.toString());
catch (Exception e)
{
handleError("info", call, response, r, false, e);
SCNApp.showToast("Communication with server failed", 4000); SCNApp.showToast("Communication with server failed", 4000);
} } finally {
finally SCNApp.runOnUiThread(() -> {
{ if (loader != null) loader.setVisibility(View.GONE);
SCNApp.runOnUiThread(() -> { if (loader != null) loader.setVisibility(View.GONE); }); });
} }
} }
}); });
} }
catch (Exception e) catch (Exception e)
{ {
handleError("info", null, null, Str.Empty, false, e); Log.e("SC:info", e.toString());
SCNApp.showToast("Communication with server failed", 4000);
} }
} }
@ -336,31 +314,28 @@ public class ServerCommunication
client.newCall(request).enqueue(new Callback() { client.newCall(request).enqueue(new Callback() {
@Override @Override
public void onFailure(Call call, IOException e) { public void onFailure(Call call, IOException e) {
handleError("requery", call, null, Str.Empty, true, e); Log.e("SC:requery", e.toString());
SCNApp.showToast("Communication with server failed", 4000);
SCNApp.runOnUiThread(() -> { SCNApp.runOnUiThread(() -> {
if (loader != null) loader.setVisibility(View.GONE); if (loader != null) loader.setVisibility(View.GONE);
}); });
} }
@Override @Override
public void onResponse(Call call, Response response) public void onResponse(Call call, Response response) {
{ try (ResponseBody responseBody = response.body()) {
String r = Str.Empty;
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(); String r = responseBody.string();
Log.d("Server::Response", request.url().toString()+"\n"+r); Log.d("Server::Response", r);
JSONObject json = (JSONObject) new JSONTokener(r).nextValue(); JSONObject json = (JSONObject) new JSONTokener(r).nextValue();
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;
} }
@ -368,7 +343,7 @@ public class ServerCommunication
JSONArray arr = json.getJSONArray("data"); JSONArray arr = json.getJSONArray("data");
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
{ {
JSONObject o = arr.getJSONObject(i); JSONObject o = arr.getJSONObject(0);
long time = json_lng(o, "timestamp"); long time = json_lng(o, "timestamp");
String title = json_str(o, "title"); String title = json_str(o, "title");
@ -379,15 +354,10 @@ public class ServerCommunication
FBMService.recieveData(time, title, content, prio, scn_id, true); FBMService.recieveData(time, title, content, prio, scn_id, true);
} }
handleSuccess("requery", call, response, r); } catch (Exception e) {
} Log.e("SC:info", e.toString());
catch (Exception e)
{
handleError("requery", call, response, r, false, e);
SCNApp.showToast("Communication with server failed", 4000); SCNApp.showToast("Communication with server failed", 4000);
} } finally {
finally
{
SCNApp.runOnUiThread(() -> { SCNApp.runOnUiThread(() -> {
if (loader != null) loader.setVisibility(View.GONE); if (loader != null) loader.setVisibility(View.GONE);
}); });
@ -397,7 +367,8 @@ public class ServerCommunication
} }
catch (Exception e) catch (Exception e)
{ {
handleError("requery", null, null, Str.Empty, false, e); Log.e("SC:requery", e.toString());
SCNApp.showToast("Communication with server failed", 4000);
} }
} }
@ -411,31 +382,27 @@ public class ServerCommunication
.url(BASE_URL + "upgrade.php?user_id=" + id + "&user_key=" + key + "&pro=" + pro + "&pro_token=" + URLEncoder.encode(pro_token, "utf-8")) .url(BASE_URL + "upgrade.php?user_id=" + id + "&user_key=" + key + "&pro=" + pro + "&pro_token=" + URLEncoder.encode(pro_token, "utf-8"))
.build(); .build();
client.newCall(request).enqueue(new Callback() client.newCall(request).enqueue(new Callback() {
{
@Override @Override
public void onFailure(Call call, IOException e) public void onFailure(Call call, IOException e) {
{ Log.e("SC:upgrade", e.toString());
handleError("upgrade", call, null, Str.Empty, true, e); SCNApp.showToast("Communication with server failed", 4000);
} }
@Override @Override
public void onResponse(Call call, Response response) public void onResponse(Call call, Response response) {
{ try (ResponseBody responseBody = response.body()) {
String r = Str.Empty; if (!response.isSuccessful())
try (ResponseBody responseBody = response.body()) throw new IOException("Unexpected code " + response);
{
if (!response.isSuccessful()) 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(); String r = responseBody.string();
Log.d("Server::Response", request.url().toString()+"\n"+r); Log.d("Server::Response", r);
JSONObject json = (JSONObject) new JSONTokener(r).nextValue(); JSONObject json = (JSONObject) new JSONTokener(r).nextValue();
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;
} }
@ -446,15 +413,10 @@ public class ServerCommunication
SCNSettings.inst().save(); SCNSettings.inst().save();
SCNApp.refreshAccountTab(); SCNApp.refreshAccountTab();
} catch (Exception e) {
handleSuccess("upgrade", call, response, r); Log.e("SC:upgrade", e.toString());
} SCNApp.showToast("Communication with server failed", 4000);
catch (Exception e) } finally {
{
handleError("upgrade", call, response, r, false, e);
}
finally
{
SCNApp.runOnUiThread(() -> { if (loader != null) loader.setVisibility(View.GONE); }); SCNApp.runOnUiThread(() -> { if (loader != null) loader.setVisibility(View.GONE); });
} }
} }
@ -462,7 +424,8 @@ public class ServerCommunication
} }
catch (Exception e) catch (Exception e)
{ {
handleError("upgrade", null, null, Str.Empty, false, e); e.printStackTrace();
SCNApp.showToast("Communication with server failed", 4000);
} }
} }
@ -474,113 +437,37 @@ public class ServerCommunication
.url(BASE_URL + "ack.php?user_id=" + id + "&user_key=" + key + "&scn_msg_id=" + msg_scn_id) .url(BASE_URL + "ack.php?user_id=" + id + "&user_key=" + key + "&scn_msg_id=" + msg_scn_id)
.build(); .build();
client.newCall(request).enqueue(new Callback()
{
@Override
public void onFailure(Call call, IOException e)
{
handleError("ack", call, null, Str.Empty, true, e);
}
@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");
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);
handleNonSuccess("ack", call, response, r);
}
handleSuccess("ack", call, response, r);
}
catch (Exception e)
{
handleError("ack", call, response, r, false, e);
}
}
});
}
catch (Exception e)
{
handleError("ack", null, null, Str.Empty, false, e);
}
}
public static void expand(int id, String key, long scn_msg_id, View loader, Func5to0<String, String, PriorityEnum, Long, Long> okResult)
{
try
{
Request request = new Request.Builder()
.url(BASE_URL + "expand.php?user_id=" + id + "&user_key=" + key + "&scn_msg_id=" + scn_msg_id)
.build();
client.newCall(request).enqueue(new Callback() { client.newCall(request).enqueue(new Callback() {
@Override @Override
public void onFailure(Call call, IOException e) { public void onFailure(Call call, IOException e) {
handleError("expand", call, null, Str.Empty, true, e); Log.e("SC:ack", e.toString());
SCNApp.runOnUiThread(() -> { if (loader != null) loader.setVisibility(View.GONE); });
} }
@Override @Override
public void onResponse(Call call, Response response) public void onResponse(Call call, Response response)
{ {
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(); String r = responseBody.string();
Log.d("Server::Response", request.url().toString()+"\n"+r); Log.d("Server::Response", r);
JSONObject json = (JSONObject) new JSONTokener(r).nextValue(); JSONObject json = (JSONObject) new JSONTokener(r).nextValue();
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;
}
JSONObject o = json.getJSONObject("data"); } catch (Exception e) {
Log.e("SC:ack", e.toString());
long time = json_lng(o, "timestamp"); SCNApp.showToast("Communication with server failed", 4000);
String title = json_str(o, "title");
String content = json_str(o, "body");
PriorityEnum prio = PriorityEnum.parseAPI(json_int(o, "priority"));
long scn_id = json_lng(o, "scn_msg_id");
okResult.invoke(title, content, prio, time, scn_id);
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) catch (Exception e)
{ {
handleError("expand", null, null, Str.Empty, false, e); Log.e("SC:ack", e.toString());
} }
} }
@ -608,79 +495,4 @@ public class ServerCommunication
{ {
return o.getString(key); 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.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)
{
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.inst().add(q);
}
catch (Exception e2)
{
Log.e("SC:HandleError", e2.toString());
}
}
} }

View File

@ -1,82 +0,0 @@
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)
);
}
}

View File

@ -4,21 +4,14 @@ 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.string.Str;
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
@ -45,20 +38,8 @@ public class FBMService extends FirebaseMessagingService
String content = remoteMessage.getData().get("body"); String content = remoteMessage.getData().get("body");
PriorityEnum prio = PriorityEnum.parseAPI(remoteMessage.getData().get("priority")); PriorityEnum prio = PriorityEnum.parseAPI(remoteMessage.getData().get("priority"));
long scn_id = Long.parseLong(remoteMessage.getData().get("scn_msg_id")); long scn_id = Long.parseLong(remoteMessage.getData().get("scn_msg_id"));
boolean trimmed = Boolean.parseBoolean(remoteMessage.getData().get("trimmed"));
recieveData(time, title, content, prio, scn_id, false);
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)
{
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));
}
else
{
recieveData(time, title, content, prio, scn_id, false);
}
} }
catch (Exception e) catch (Exception e)
{ {

View File

@ -2,37 +2,23 @@ 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;
@ -59,72 +45,18 @@ 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()
@ -135,16 +67,14 @@ 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.BillingResponseCode.OK) if (purchasesResult.getResponseCode() == BillingClient.BillingResponse.OK)
{ {
for (Purchase p : Objects.requireNonNull(purchasesResult.getPurchasesList())) for (Purchase p : purchasesResult.getPurchasesList())
{ {
handlePurchase(p, false); handlePurchase(p);
} }
_isInitialized = true; boolean newProMode = getPurchaseCached(IAB_PRO_MODE) != null;
boolean newProMode = getPurchaseCachedSimple(IAB_PRO_MODE);
if (newProMode != SCNSettings.inst().promode_local) if (newProMode != SCNSettings.inst().promode_local)
{ {
refreshProModeListener(); refreshProModeListener();
@ -159,39 +89,20 @@ 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 = () -> { executeServiceRequest(() ->
// Query the purchase async {
SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder(); BillingFlowParams flowParams = BillingFlowParams
params.setSkusList(Collections.singletonList(id)).setType(BillingClient.SkuType.INAPP); .newBuilder()
client.querySkuDetailsAsync(params.build(), (billingResult, skuDetailsList) -> .setSku(id)
{ .setType(BillingClient.SkuType.INAPP) // SkuType.SUB for subscription
if (billingResult.getResponseCode() != BillingClient.BillingResponseCode.OK || skuDetailsList == null || skuDetailsList.size() != 1) .build();
{ client.launchBillingFlow(a, flowParams);
SCNApp.showToast("Could not find product", Toast.LENGTH_SHORT); }, true);
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) private void executeServiceRequest(Func0to0 runnable, final boolean userRequest) {
{
if (isServiceConnected) if (isServiceConnected)
{ {
runnable.invoke(); runnable.invoke();
@ -213,37 +124,34 @@ public class IABService implements PurchasesUpdatedListener
} }
@Override @Override
public void onPurchasesUpdated(@NonNull BillingResult billingResult, @Nullable List<Purchase> purchases) public void onPurchasesUpdated(int responseCode, @Nullable List<Purchase> purchases)
{ {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && purchases != null) if (responseCode == BillingClient.BillingResponse.OK && purchases != null)
{ {
for (Purchase purchase : purchases) for (Purchase purchase : purchases)
{ {
handlePurchase(purchase, true); handlePurchase(purchase);
} }
} }
else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED && purchases != null) else if (responseCode == BillingClient.BillingResponse.ITEM_ALREADY_OWNED && purchases != null)
{ {
for (Purchase purchase : purchases) for (Purchase purchase : purchases)
{ {
handlePurchase(purchase, true); handlePurchase(purchase);
} }
} }
} }
private void handlePurchase(Purchase purchase, boolean triggerUpdate) private void handlePurchase(Purchase purchase)
{ {
Log.d(TAG, "Got a verified purchase: " + purchase); Log.d(TAG, "Got a verified purchase: " + purchase);
purchases.add(purchase); purchases.add(purchase);
if (triggerUpdate) refreshProModeListener(); 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();
@ -255,9 +163,9 @@ public class IABService implements PurchasesUpdatedListener
client.startConnection(new BillingClientStateListener() client.startConnection(new BillingClientStateListener()
{ {
@Override @Override
public void onBillingSetupFinished(@NonNull BillingResult billingResult) public void onBillingSetupFinished(@BillingClient.BillingResponse int billingResponseCode)
{ {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) if (billingResponseCode == BillingClient.BillingResponse.OK)
{ {
isServiceConnected = true; isServiceConnected = true;
if (executeOnSuccess != null) executeOnSuccess.invoke(); if (executeOnSuccess != null) executeOnSuccess.invoke();
@ -275,31 +183,13 @@ public class IABService implements PurchasesUpdatedListener
}); });
} }
public boolean getPurchaseCachedSimple(String id) public Purchase getPurchaseCached(String id)
{ {
return getPurchaseCachedExtended(id).Item1;
}
@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) for (Purchase p : purchases)
{ {
if (Str.equals(p.getSku(), id)) if (Str.equals(p.getSku(), id)) return p;
{
updateCache(id, true);
return new Tuple3<>(true, true, p.getPurchaseToken());
}
} }
updateCache(id, false); return null;
return new Tuple3<>(false, true, Str.Empty);
} }
} }

View File

@ -6,7 +6,6 @@ import android.app.NotificationManager;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.graphics.BitmapFactory;
import android.graphics.Color; import android.graphics.Color;
import android.media.AudioManager; import android.media.AudioManager;
import android.net.Uri; import android.net.Uri;
@ -65,8 +64,7 @@ public class NotificationService
channel0.setDescription("Push notifications from the server with low priority.\nGo to the in-app settings to configure ringtone, volume and vibrations"); channel0.setDescription("Push notifications from the server with low priority.\nGo to the in-app settings to configure ringtone, volume and vibrations");
channel0.setSound(null, null); channel0.setSound(null, null);
channel0.setVibrationPattern(null); channel0.setVibrationPattern(null);
channel0.setLightColor(Color.CYAN); channel0.setLightColor(Color.BLUE);
channel0.enableLights(true);
notifman.createNotificationChannel(channel0); notifman.createNotificationChannel(channel0);
} }
} }
@ -78,8 +76,7 @@ public class NotificationService
channel1.setDescription("Push notifications from the server with low priority.\nGo to the in-app settings to configure ringtone, volume and vibrations"); channel1.setDescription("Push notifications from the server with low priority.\nGo to the in-app settings to configure ringtone, volume and vibrations");
channel1.setSound(null, null); channel1.setSound(null, null);
channel1.setVibrationPattern(null); channel1.setVibrationPattern(null);
channel1.setLightColor(Color.CYAN); channel1.setLightColor(Color.BLUE);
channel1.enableLights(true);
notifman.createNotificationChannel(channel1); notifman.createNotificationChannel(channel1);
} }
} }
@ -87,13 +84,11 @@ public class NotificationService
NotificationChannel channel2 = notifman.getNotificationChannel(CHANNEL_P2_ID); NotificationChannel channel2 = notifman.getNotificationChannel(CHANNEL_P2_ID);
if (channel2 == null) if (channel2 == null)
{ {
channel2 = new NotificationChannel(CHANNEL_P2_ID, "Push notifications (high priority)", NotificationManager.IMPORTANCE_DEFAULT); channel2 = new NotificationChannel(CHANNEL_P1_ID, "Push notifications (high priority)", NotificationManager.IMPORTANCE_DEFAULT);
channel2.setDescription("Push notifications from the server with low priority.\nGo to the in-app settings to configure ringtone, volume and vibrations"); channel2.setDescription("Push notifications from the server with low priority.\nGo to the in-app settings to configure ringtone, volume and vibrations");
channel2.setSound(null, null); channel2.setSound(null, null);
channel2.setVibrationPattern(null); channel2.setVibrationPattern(null);
channel2.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE); channel2.setLightColor(Color.BLUE);
channel2.setLightColor(Color.CYAN);
channel2.enableLights(true);
notifman.createNotificationChannel(channel2); notifman.createNotificationChannel(channel2);
} }
} }
@ -119,9 +114,9 @@ public class NotificationService
{ {
Vibrator v = (Vibrator) SCNApp.getContext().getSystemService(Context.VIBRATOR_SERVICE); Vibrator v = (Vibrator) SCNApp.getContext().getSystemService(Context.VIBRATOR_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
v.vibrate(VibrationEffect.createOneShot(500, VibrationEffect.DEFAULT_AMPLITUDE)); v.vibrate(VibrationEffect.createOneShot(1500, VibrationEffect.DEFAULT_AMPLITUDE));
} else { } else {
v.vibrate(500); v.vibrate(1500);
} }
} }
} }
@ -170,15 +165,12 @@ public class NotificationService
private void showBackground_old(CMessage msg, Context ctxt, NotificationSettings ns, PriorityEnum prio) private void showBackground_old(CMessage msg, Context ctxt, NotificationSettings ns, PriorityEnum prio)
{ {
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(ctxt, getChannel(prio)); NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(ctxt, getChannel(prio));
mBuilder.setSmallIcon(R.drawable.ic_notification_white); mBuilder.setSmallIcon(R.drawable.ic_bfb);
mBuilder.setLargeIcon(BitmapFactory.decodeResource(ctxt.getResources(), R.mipmap.ic_notification_full));
mBuilder.setContentTitle(msg.Title); mBuilder.setContentTitle(msg.Title);
mBuilder.setContentText(msg.Content); mBuilder.setContentText(msg.Content);
mBuilder.setShowWhen(true); mBuilder.setShowWhen(true);
mBuilder.setWhen(msg.Timestamp * 1000); mBuilder.setWhen(msg.Timestamp * 1000);
mBuilder.setAutoCancel(true); mBuilder.setAutoCancel(true);
mBuilder.setCategory(Notification.CATEGORY_MESSAGE);
mBuilder.setGroup("com.blackforestbytes.simplecloudnotifier.notifications.group."+prio.toString());
if (msg.Priority == PriorityEnum.LOW) mBuilder.setPriority(NotificationCompat.PRIORITY_LOW); if (msg.Priority == PriorityEnum.LOW) mBuilder.setPriority(NotificationCompat.PRIORITY_LOW);
if (msg.Priority == PriorityEnum.NORMAL) mBuilder.setPriority(NotificationCompat.PRIORITY_DEFAULT); if (msg.Priority == PriorityEnum.NORMAL) mBuilder.setPriority(NotificationCompat.PRIORITY_DEFAULT);
@ -206,22 +198,19 @@ public class NotificationService
Notification n = mBuilder.build(); Notification n = mBuilder.build();
if (mNotificationManager != null) mNotificationManager.notify((int)msg.SCN_ID, n); if (mNotificationManager != null) mNotificationManager.notify(0, n);
} }
@RequiresApi(api = Build.VERSION_CODES.O) @RequiresApi(api = Build.VERSION_CODES.O)
private void showBackground_new(CMessage msg, Context ctxt, NotificationSettings ns, PriorityEnum prio) private void showBackground_new(CMessage msg, Context ctxt, NotificationSettings ns, PriorityEnum prio)
{ {
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(ctxt, getChannel(prio)); NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(ctxt, getChannel(prio));
mBuilder.setSmallIcon(R.drawable.ic_notification_white); mBuilder.setSmallIcon(R.drawable.ic_bfb);
mBuilder.setLargeIcon(BitmapFactory.decodeResource(ctxt.getResources(), R.mipmap.ic_notification_full));
mBuilder.setContentTitle(msg.Title); mBuilder.setContentTitle(msg.Title);
mBuilder.setContentText(msg.Content); mBuilder.setContentText(msg.Content);
mBuilder.setShowWhen(true); mBuilder.setShowWhen(true);
mBuilder.setWhen(msg.Timestamp * 1000); mBuilder.setWhen(msg.Timestamp * 1000);
mBuilder.setAutoCancel(true); mBuilder.setAutoCancel(true);
mBuilder.setCategory(Notification.CATEGORY_MESSAGE);
mBuilder.setGroup("com.blackforestbytes.simplecloudnotifier.notifications.group."+prio.toString());
if (ns.EnableLED) mBuilder.setLights(ns.LEDColor, 500, 500); if (ns.EnableLED) mBuilder.setLights(ns.LEDColor, 500, 500);
@ -229,10 +218,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 intent = new Intent(ctxt, MainActivity.class); Intent intnt_click = new Intent(SCNApp.getContext(), BroadcastReceiverService.class);
PendingIntent pi = PendingIntent.getActivity(ctxt, 0, intent, 0); intnt_click.putExtra(BroadcastReceiverService.ID_KEY, BroadcastReceiverService.NOTIF_SHOW_MAIN);
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;
@ -253,12 +242,12 @@ public class NotificationService
Notification n = mBuilder.build(); Notification n = mBuilder.build();
n.flags |= Notification.FLAG_AUTO_CANCEL; n.flags |= Notification.FLAG_AUTO_CANCEL;
mNotificationManager.notify((int)msg.SCN_ID, n); mNotificationManager.notify(0, n);
if (ns.EnableVibration) if (ns.EnableVibration)
{ {
Vibrator v = (Vibrator) SCNApp.getContext().getSystemService(Context.VIBRATOR_SERVICE); Vibrator v = (Vibrator) SCNApp.getContext().getSystemService(Context.VIBRATOR_SERVICE);
v.vibrate(VibrationEffect.createOneShot(500, VibrationEffect.DEFAULT_AMPLITUDE)); v.vibrate(VibrationEffect.createOneShot(1500, VibrationEffect.DEFAULT_AMPLITUDE));
} }
//if (ns.EnableLED) { } // no LED in Android-O -- configure via Channel //if (ns.EnableLED) { } // no LED in Android-O -- configure via Channel

View File

@ -1,56 +0,0 @@
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());
}
}

View File

@ -3,7 +3,6 @@ package com.blackforestbytes.simplecloudnotifier.util;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.view.View; import android.view.View;
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
import com.blackforestbytes.simplecloudnotifier.view.MessageAdapter; import com.blackforestbytes.simplecloudnotifier.view.MessageAdapter;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@ -14,21 +13,10 @@ public class MessageAdapterTouchHelper extends ItemTouchHelper.SimpleCallback
{ {
private MessageAdapterTouchHelperListener listener; private MessageAdapterTouchHelperListener listener;
private int dir = 0;
public MessageAdapterTouchHelper(int dragDirs, int swipeDirs, MessageAdapterTouchHelperListener listener) public MessageAdapterTouchHelper(int dragDirs, int swipeDirs, MessageAdapterTouchHelperListener listener)
{ {
super(dragDirs, swipeDirs); super(dragDirs, swipeDirs);
this.dir = swipeDirs;
this.listener = listener; this.listener = listener;
updateEnabled();
}
public void updateEnabled()
{
int sdir = SCNSettings.inst().EnableDeleteSwipe ? ItemTouchHelper.LEFT : 0;
if (dir == sdir) return;
setDefaultSwipeDirs(dir = sdir);
} }
@Override @Override

View File

@ -1,25 +0,0 @@
package com.blackforestbytes.simplecloudnotifier.util;
import android.text.Editable;
import android.text.TextWatcher;
public abstract class TextChangedListener<T> implements TextWatcher {
private T target;
public TextChangedListener(T target) {
this.target = target;
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {}
@Override
public void afterTextChanged(Editable s) {
this.onTextChanged(target, s);
}
public abstract void onTextChanged(T target, Editable s);
}

View File

@ -6,14 +6,12 @@ import android.content.ClipData;
import android.content.ClipboardManager; import android.content.ClipboardManager;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.graphics.Color;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ImageButton; import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import com.blackforestbytes.simplecloudnotifier.R; import com.blackforestbytes.simplecloudnotifier.R;
@ -93,7 +91,7 @@ public class AccountFragment extends Fragment
builder.setPositiveButton("YES", (dialog, which) -> { builder.setPositiveButton("YES", (dialog, which) -> {
CMessageList.inst().clear(); CMessageList.inst().clear();
SCNApp.showToast("Messages cleared", 1000); SCNApp.showToast("Notifications cleared", 1000);
dialog.dismiss(); dialog.dismiss();
}); });
@ -105,7 +103,7 @@ public class AccountFragment extends Fragment
v.findViewById(R.id.btnQR).setOnClickListener(cv -> v.findViewById(R.id.btnQR).setOnClickListener(cv ->
{ {
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(SCNSettings.inst().createOnlineURL(true))); Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(SCNSettings.inst().createOnlineURL()));
startActivity(browserIntent); startActivity(browserIntent);
}); });
@ -128,11 +126,10 @@ public class AccountFragment extends Fragment
public void updateUI(View v) public void updateUI(View v)
{ {
if (v == null) return; if (v == null) return;
TextView tvUserID = v.findViewById(R.id.tvUserID); TextView tvUserID = v.findViewById(R.id.tvUserID);
TextView tvUserKey = v.findViewById(R.id.tvUserKey); TextView tvUserKey = v.findViewById(R.id.tvUserKey);
TextView tvQuota = v.findViewById(R.id.tvQuota); TextView tvQuota = v.findViewById(R.id.tvQuota);
ImageView ivQuota = v.findViewById(R.id.ic_img_quota); ImageButton btnQR = v.findViewById(R.id.btnQR);
ImageButton btnQR = v.findViewById(R.id.btnQR);
SCNSettings s = SCNSettings.inst(); SCNSettings s = SCNSettings.inst();
@ -141,8 +138,7 @@ public class AccountFragment extends Fragment
tvUserID.setText(String.valueOf(s.user_id)); tvUserID.setText(String.valueOf(s.user_id));
tvUserKey.setText(s.user_key); tvUserKey.setText(s.user_key);
tvQuota.setText(String.format("%d / %d", s.quota_curr, s.quota_max)); tvQuota.setText(String.format("%d / %d", s.quota_curr, s.quota_max));
btnQR.setImageBitmap(QRCode.from(s.createOnlineURL(false)).to(ImageType.PNG).withSize(512, 512).bitmap()); btnQR.setImageBitmap(QRCode.from(s.createOnlineURL()).to(ImageType.PNG).withSize(512, 512).bitmap());
ivQuota.setColorFilter(s.quota_curr>=s.quota_max ? Color.rgb(200, 0, 0) : Color.rgb(128, 128, 128));
} }
else else
{ {
@ -150,7 +146,6 @@ public class AccountFragment extends Fragment
tvUserKey.setText(R.string.str_not_connected); tvUserKey.setText(R.string.str_not_connected);
tvQuota.setText(R.string.str_not_connected); tvQuota.setText(R.string.str_not_connected);
btnQR.setImageResource(R.drawable.qr_default); btnQR.setImageResource(R.drawable.qr_default);
ivQuota.setColorFilter(0x80_80_80);
} }
} }
} }

View File

@ -1,23 +1,14 @@
package com.blackforestbytes.simplecloudnotifier.view; package com.blackforestbytes.simplecloudnotifier.view;
import android.content.Context;
import android.content.Intent;
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.widget.RelativeLayout; import android.widget.RelativeLayout;
import android.widget.Toast;
import com.blackforestbytes.simplecloudnotifier.R; import com.blackforestbytes.simplecloudnotifier.R;
import com.blackforestbytes.simplecloudnotifier.SCNApp; import com.blackforestbytes.simplecloudnotifier.SCNApp;
import com.blackforestbytes.simplecloudnotifier.model.CMessageList; import com.blackforestbytes.simplecloudnotifier.model.CMessageList;
import com.blackforestbytes.simplecloudnotifier.model.QueryLog;
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings; import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
import com.blackforestbytes.simplecloudnotifier.service.IABService; import com.blackforestbytes.simplecloudnotifier.service.IABService;
import com.blackforestbytes.simplecloudnotifier.service.NotificationService; import com.blackforestbytes.simplecloudnotifier.service.NotificationService;
import com.blackforestbytes.simplecloudnotifier.view.debug.QueryLogActivity;
import com.google.android.material.tabs.TabLayout; import com.google.android.material.tabs.TabLayout;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
@ -25,12 +16,6 @@ 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;
@ -39,8 +24,6 @@ public class MainActivity extends AppCompatActivity
@Override @Override
protected void onCreate(Bundle savedInstanceState) protected void onCreate(Bundle savedInstanceState)
{ {
QueryLog.inst();
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); setContentView(R.layout.activity_main);
@ -50,7 +33,6 @@ public class MainActivity extends AppCompatActivity
layoutRoot = findViewById(R.id.layoutRoot); layoutRoot = findViewById(R.id.layoutRoot);
Toolbar toolbar = findViewById(R.id.toolbar); Toolbar toolbar = findViewById(R.id.toolbar);
toolbar.setOnClickListener(this::onToolbackClicked);
setSupportActionBar(toolbar); setSupportActionBar(toolbar);
ViewPager viewPager = findViewById(R.id.pager); ViewPager viewPager = findViewById(R.id.pager);
@ -79,7 +61,7 @@ public class MainActivity extends AppCompatActivity
SCNApp.register(this); SCNApp.register(this);
IABService.startup(this); IABService.startup(this);
SCNSettings.inst().work(this, true); SCNSettings.inst().work(this);
} }
@Override @Override
@ -99,126 +81,4 @@ public class MainActivity extends AppCompatActivity
CMessageList.inst().fullSave(); CMessageList.inst().fullSave();
IABService.inst().destroy(); 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));
}
@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, true);
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

@ -1,7 +1,5 @@
package com.blackforestbytes.simplecloudnotifier.view; package com.blackforestbytes.simplecloudnotifier.view;
import android.content.Intent;
import android.graphics.Color;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -10,11 +8,8 @@ import android.widget.RelativeLayout;
import android.widget.TextView; import android.widget.TextView;
import com.blackforestbytes.simplecloudnotifier.R; import com.blackforestbytes.simplecloudnotifier.R;
import com.blackforestbytes.simplecloudnotifier.SCNApp;
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.SCNSettings;
import com.google.android.material.button.MaterialButton;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.Collections; import java.util.Collections;
@ -57,11 +52,12 @@ public class MessageAdapter extends RecyclerView.Adapter
{ {
CMessage msg = CMessageList.inst().tryGetFromBack(position); CMessage msg = CMessageList.inst().tryGetFromBack(position);
MessagePresenter view = (MessagePresenter) holder; MessagePresenter view = (MessagePresenter) holder;
view.setMessage(msg, position); view.setMessage(msg);
viewHolders.put(view, true); viewHolders.put(view, true);
} }
@Override @Override
public void onViewRecycled(@NonNull RecyclerView.ViewHolder holder) public void onViewRecycled(@NonNull RecyclerView.ViewHolder holder)
{ {
@ -91,17 +87,16 @@ public class MessageAdapter extends RecyclerView.Adapter
manLayout.smoothScrollToPosition(viewRecycler, null, 0); manLayout.smoothScrollToPosition(viewRecycler, null, 0);
} }
public CMessage removeItem(int position) public void removeItem(int position)
{ {
CMessage i = CMessageList.inst().removeFromBack(position); CMessageList.inst().remove(position);
notifyDataSetChanged(); notifyItemRemoved(position);
return i;
} }
public void restoreItem(CMessage item, int position) public void restoreItem(CMessage item, int position)
{ {
CMessageList.inst().insert(position, item); CMessageList.inst().insert(position, item);
notifyDataSetChanged(); notifyItemInserted(position);
} }
public class MessagePresenter extends RecyclerView.ViewHolder implements View.OnClickListener public class MessagePresenter extends RecyclerView.ViewHolder implements View.OnClickListener
@ -114,11 +109,7 @@ public class MessageAdapter extends RecyclerView.Adapter
public RelativeLayout viewForeground; public RelativeLayout viewForeground;
public RelativeLayout viewBackground; public RelativeLayout viewBackground;
public MaterialButton btnShare;
public MaterialButton btnDelete;
private CMessage data; private CMessage data;
private int datapos;
MessagePresenter(View itemView) MessagePresenter(View itemView)
{ {
@ -129,8 +120,6 @@ public class MessageAdapter extends RecyclerView.Adapter
ivPriority = itemView.findViewById(R.id.ivPriority); ivPriority = itemView.findViewById(R.id.ivPriority);
viewForeground = itemView.findViewById(R.id.layoutFront); viewForeground = itemView.findViewById(R.id.layoutFront);
viewBackground = itemView.findViewById(R.id.layoutBack); viewBackground = itemView.findViewById(R.id.layoutBack);
btnShare = itemView.findViewById(R.id.btnShare);
btnDelete = itemView.findViewById(R.id.btnDelete);
itemView.setOnClickListener(this); itemView.setOnClickListener(this);
tvTimestamp.setOnClickListener(this); tvTimestamp.setOnClickListener(this);
@ -138,22 +127,9 @@ public class MessageAdapter extends RecyclerView.Adapter
tvMessage.setOnClickListener(this); tvMessage.setOnClickListener(this);
ivPriority.setOnClickListener(this); ivPriority.setOnClickListener(this);
viewForeground.setOnClickListener(this); viewForeground.setOnClickListener(this);
btnShare.setOnClickListener(v ->
{
if (data == null) return;
Intent sharingIntent = new Intent(android.content.Intent.ACTION_SEND);
sharingIntent.setType("text/plain");
sharingIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, data.Title);
sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, data.Content);
SCNApp.getMainActivity().startActivity(Intent.createChooser(sharingIntent, "Share message"));
});
btnDelete.setOnClickListener(v -> { if (data != null) SCNApp.getMainActivity().adpTabs.tab1.deleteMessage(datapos); });
} }
void setMessage(CMessage msg, int pos) void setMessage(CMessage msg)
{ {
tvTimestamp.setText(msg.formatTimestamp()); tvTimestamp.setText(msg.formatTimestamp());
tvTitle.setText(msg.Title); tvTitle.setText(msg.Title);
@ -164,63 +140,32 @@ public class MessageAdapter extends RecyclerView.Adapter
case LOW: case LOW:
ivPriority.setVisibility(View.VISIBLE); ivPriority.setVisibility(View.VISIBLE);
ivPriority.setImageResource(R.drawable.priority_low); ivPriority.setImageResource(R.drawable.priority_low);
ivPriority.setColorFilter(Color.rgb(176, 176, 176));
break; break;
case NORMAL: case NORMAL:
ivPriority.setVisibility(View.GONE); ivPriority.setVisibility(View.GONE);
ivPriority.setColorFilter(Color.rgb(176, 176, 176));
break; break;
case HIGH: case HIGH:
ivPriority.setVisibility(View.VISIBLE); ivPriority.setVisibility(View.VISIBLE);
ivPriority.setImageResource(R.drawable.priority_high); ivPriority.setImageResource(R.drawable.priority_high);
ivPriority.setColorFilter(Color.rgb(200, 0, 0));
break; break;
} }
data = msg; data = msg;
datapos = pos;
if (msg.IsExpandedInAdapter) expand(true); else collapse(true);
}
private void expand(boolean force)
{
if (data != null && data.IsExpandedInAdapter && !force) return;
if (data != null) data.IsExpandedInAdapter = true;
if (tvMessage != null) tvMessage.setMaxLines(9999);
if (btnDelete != null) btnDelete.setVisibility(View.VISIBLE);
if (btnShare != null) btnShare.setVisibility(View.VISIBLE);
}
private int norm(int i) { return (i<=0)?0:((i>9999)?9999:i); }
private void collapse(boolean force)
{
if (data != null && !data.IsExpandedInAdapter && !force) return;
if (data != null) data.IsExpandedInAdapter = false;
if (tvMessage != null) tvMessage.setMaxLines(norm(SCNSettings.inst().PreviewLineCount));
if (btnDelete != null) btnDelete.setVisibility(View.GONE);
if (btnShare != null) btnShare.setVisibility(View.GONE);
} }
@Override @Override
public void onClick(View v) public void onClick(View v)
{ {
if (data.IsExpandedInAdapter)
{
collapse(false);
return;
}
for (MessagePresenter holder : MessageAdapter.this.viewHolders.keySet()) for (MessagePresenter holder : MessageAdapter.this.viewHolders.keySet())
{ {
if (holder == null) continue; if (holder == null) continue;
if (holder == this) continue; if (holder == this) continue;
holder.collapse(false); if (holder.tvMessage == null) continue;
if (holder.tvMessage.getMaxLines() == 6) continue;
holder.tvMessage.setMaxLines(6);
} }
expand(false); tvMessage.setMaxLines(9999);
} }
} }
} }

View File

@ -9,6 +9,7 @@ import android.view.ViewGroup;
import com.blackforestbytes.simplecloudnotifier.R; import com.blackforestbytes.simplecloudnotifier.R;
import com.blackforestbytes.simplecloudnotifier.SCNApp; import com.blackforestbytes.simplecloudnotifier.SCNApp;
import com.blackforestbytes.simplecloudnotifier.model.CMessage; import com.blackforestbytes.simplecloudnotifier.model.CMessage;
import com.blackforestbytes.simplecloudnotifier.model.CMessageList;
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings; import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
import com.blackforestbytes.simplecloudnotifier.service.IABService; import com.blackforestbytes.simplecloudnotifier.service.IABService;
import com.blackforestbytes.simplecloudnotifier.util.MessageAdapterTouchHelper; import com.blackforestbytes.simplecloudnotifier.util.MessageAdapterTouchHelper;
@ -27,8 +28,6 @@ public class NotificationsFragment extends Fragment implements MessageAdapterTou
private PublisherAdView adView; private PublisherAdView adView;
private MessageAdapter adpMessages; private MessageAdapter adpMessages;
public MessageAdapterTouchHelper touchHelper;
public NotificationsFragment() public NotificationsFragment()
{ {
// Required empty public constructor // Required empty public constructor
@ -44,7 +43,7 @@ public class NotificationsFragment extends Fragment implements MessageAdapterTou
rvMessages.setLayoutManager(lman); rvMessages.setLayoutManager(lman);
rvMessages.setAdapter(adpMessages = new MessageAdapter(v.findViewById(R.id.tvNoElements), lman, rvMessages)); rvMessages.setAdapter(adpMessages = new MessageAdapter(v.findViewById(R.id.tvNoElements), lman, rvMessages));
ItemTouchHelper.SimpleCallback itemTouchHelperCallback = touchHelper = new MessageAdapterTouchHelper(0, ItemTouchHelper.LEFT, this); ItemTouchHelper.SimpleCallback itemTouchHelperCallback = new MessageAdapterTouchHelper(0, ItemTouchHelper.LEFT, this);
new ItemTouchHelper(itemTouchHelperCallback).attachToRecyclerView(rvMessages); new ItemTouchHelper(itemTouchHelperCallback).attachToRecyclerView(rvMessages);
adView = v.findViewById(R.id.adBanner); adView = v.findViewById(R.id.adBanner);
@ -58,7 +57,7 @@ public class NotificationsFragment extends Fragment implements MessageAdapterTou
public void updateProState() public void updateProState()
{ {
if (adView != null) adView.setVisibility(IABService.inst().getPurchaseCachedSimple(IABService.IAB_PRO_MODE) ? View.GONE : View.VISIBLE); if (adView != null) adView.setVisibility(IABService.inst().getPurchaseCached(IABService.IAB_PRO_MODE) != null ? View.GONE : View.VISIBLE);
} }
@Override @Override
@ -66,25 +65,17 @@ public class NotificationsFragment extends Fragment implements MessageAdapterTou
{ {
if (viewHolder instanceof MessageAdapter.MessagePresenter) if (viewHolder instanceof MessageAdapter.MessagePresenter)
{ {
deleteMessage(viewHolder.getAdapterPosition()); final CMessage deletedItem = CMessageList.inst().tryGet(viewHolder.getAdapterPosition());
final int deletedIndex = viewHolder.getAdapterPosition();
String name = deletedItem.Title;
adpMessages.removeItem(viewHolder.getAdapterPosition());
Snackbar snackbar = Snackbar.make(SCNApp.getMainActivity().layoutRoot, name + " removed", Snackbar.LENGTH_LONG);
snackbar.setAction("UNDO", view -> adpMessages.restoreItem(deletedItem, deletedIndex));
snackbar.setActionTextColor(Color.YELLOW);
snackbar.show();
} }
} }
public void deleteMessage(int pos)
{
final int deletedIndex = pos;
final CMessage deletedItem = adpMessages.removeItem(pos);
String name = deletedItem.Title;
Snackbar snackbar = Snackbar.make(SCNApp.getMainActivity().layoutRoot, name + " removed", Snackbar.LENGTH_LONG);
snackbar.setAction("UNDO", view -> adpMessages.restoreItem(deletedItem, deletedIndex));
snackbar.setActionTextColor(Color.YELLOW);
snackbar.show();
}
public void updateDeleteSwipeEnabled()
{
if (touchHelper != null) touchHelper.updateEnabled();
}
} }

View File

@ -1,15 +1,15 @@
package com.blackforestbytes.simplecloudnotifier.view; package com.blackforestbytes.simplecloudnotifier.view;
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.util.Log; import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
@ -17,34 +17,27 @@ import android.view.ViewGroup;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.Button; import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.SeekBar; import android.widget.SeekBar;
import android.widget.Spinner; import android.widget.Spinner;
import android.widget.Switch; import android.widget.Switch;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.core.content.FileProvider;
import androidx.fragment.app.Fragment;
import com.android.billingclient.api.Purchase;
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;
import com.blackforestbytes.simplecloudnotifier.service.IABService; import com.blackforestbytes.simplecloudnotifier.service.IABService;
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;
@ -56,8 +49,6 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
private Button prefUpgradeAccount; private Button prefUpgradeAccount;
private TextView prefUpgradeAccount_msg; private TextView prefUpgradeAccount_msg;
private TextView prefUpgradeAccount_info; private TextView prefUpgradeAccount_info;
private Switch prefEnableDeleteSwipe;
private EditText prefPreviewLineCount;
private Switch prefMsgLowEnableSound; private Switch prefMsgLowEnableSound;
private TextView prefMsgLowRingtone_value; private TextView prefMsgLowRingtone_value;
@ -95,9 +86,6 @@ 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];
@ -126,8 +114,6 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
prefUpgradeAccount = v.findViewById(R.id.prefUpgradeAccount); prefUpgradeAccount = v.findViewById(R.id.prefUpgradeAccount);
prefUpgradeAccount_msg = v.findViewById(R.id.prefUpgradeAccount2); prefUpgradeAccount_msg = v.findViewById(R.id.prefUpgradeAccount2);
prefUpgradeAccount_info = v.findViewById(R.id.prefUpgradeAccount_info); prefUpgradeAccount_info = v.findViewById(R.id.prefUpgradeAccount_info);
prefEnableDeleteSwipe = v.findViewById(R.id.prefEnableDeleteSwipe);
prefPreviewLineCount = v.findViewById(R.id.prefPreviewLineCount);
prefMsgLowEnableSound = v.findViewById(R.id.prefMsgLowEnableSound); prefMsgLowEnableSound = v.findViewById(R.id.prefMsgLowEnableSound);
prefMsgLowRingtone_value = v.findViewById(R.id.prefMsgLowRingtone_value); prefMsgLowRingtone_value = v.findViewById(R.id.prefMsgLowRingtone_value);
@ -161,19 +147,11 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
prefMsgHighLedColor_value = v.findViewById(R.id.prefMsgHighLedColor_value); prefMsgHighLedColor_value = v.findViewById(R.id.prefMsgHighLedColor_value);
prefMsgHighLedColor_container = v.findViewById(R.id.prefMsgHighLedColor_container); prefMsgHighLedColor_container = v.findViewById(R.id.prefMsgHighLedColor_container);
prefMsgHighEnableVibrations = v.findViewById(R.id.prefMsgHighEnableVibrations); prefMsgHighEnableVibrations = v.findViewById(R.id.prefMsgHighEnableVibrations);
prefMsgHighForceVolume = v.findViewById(R.id.prefMsgHighForceVolume); prefMsgHighForceVolume = v.findViewById(R.id.prefMsgHighForceVolume);
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);
plcsa.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
prefLocalCacheSize.setAdapter(plcsa);
} }
@SuppressLint("SetTextI18n")
private void updateUI() private void updateUI()
{ {
SCNSettings s = SCNSettings.inst(); SCNSettings s = SCNSettings.inst();
@ -181,14 +159,15 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
if (c == null) return; if (c == null) return;
if (prefAppEnabled.isChecked() != s.Enabled) prefAppEnabled.setChecked(s.Enabled); if (prefAppEnabled.isChecked() != s.Enabled) prefAppEnabled.setChecked(s.Enabled);
if (prefEnableDeleteSwipe.isChecked() != s.EnableDeleteSwipe) prefEnableDeleteSwipe.setChecked(s.EnableDeleteSwipe);
if (!prefPreviewLineCount.getText().toString().equals(Integer.toString(s.PreviewLineCount))) prefPreviewLineCount.setText(Integer.toString(s.PreviewLineCount));
prefUpgradeAccount.setVisibility( SCNSettings.inst().promode_local ? View.GONE : View.VISIBLE); prefUpgradeAccount.setVisibility( SCNSettings.inst().promode_local ? View.GONE : View.VISIBLE);
prefUpgradeAccount_info.setVisibility(SCNSettings.inst().promode_local ? View.GONE : View.VISIBLE); prefUpgradeAccount_info.setVisibility(SCNSettings.inst().promode_local ? View.GONE : View.VISIBLE);
prefUpgradeAccount_msg.setVisibility( SCNSettings.inst().promode_local ? View.VISIBLE : View.GONE ); prefUpgradeAccount_msg.setVisibility( SCNSettings.inst().promode_local ? View.VISIBLE : View.GONE );
if (prefLocalCacheSize.getSelectedItemPosition() != getCacheSizeIndex(s.LocalCacheSize)) prefLocalCacheSize.setSelection(getCacheSizeIndex(s.LocalCacheSize)); ArrayAdapter<Integer> plcsa = new ArrayAdapter<>(c, android.R.layout.simple_spinner_item, SCNSettings.CHOOSABLE_CACHE_SIZES);
plcsa.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
prefLocalCacheSize.setAdapter(plcsa);
prefLocalCacheSize.setSelection(getCacheSizeIndex(s.LocalCacheSize));
if (prefMsgLowEnableSound.isChecked() != s.PriorityLow.EnableSound) prefMsgLowEnableSound.setChecked(s.PriorityLow.EnableSound); if (prefMsgLowEnableSound.isChecked() != s.PriorityLow.EnableSound) prefMsgLowEnableSound.setChecked(s.PriorityLow.EnableSound);
if (!prefMsgLowRingtone_value.getText().equals(s.PriorityLow.SoundName)) prefMsgLowRingtone_value.setText(s.PriorityLow.SoundName); if (!prefMsgLowRingtone_value.getText().equals(s.PriorityLow.SoundName)) prefMsgLowRingtone_value.setText(s.PriorityLow.SoundName);
@ -234,14 +213,7 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
{ {
SCNSettings s = SCNSettings.inst(); SCNSettings s = SCNSettings.inst();
prefAppEnabled.setOnCheckedChangeListener((a,b) -> { boolean prev=s.Enabled; s.Enabled=b; saveAndUpdate(); updateEnabled(prev, b); }); prefAppEnabled.setOnCheckedChangeListener((a,b) -> { s.Enabled=b; saveAndUpdate(); });
prefEnableDeleteSwipe.setOnCheckedChangeListener((a,b) -> { s.EnableDeleteSwipe=b; saveAndUpdate(); });
prefPreviewLineCount.addTextChangedListener(new TextChangedListener<EditText>(prefPreviewLineCount) {
@Override
public void onTextChanged(EditText target, Editable ed) {
if (!ed.toString().isEmpty()) try { s.PreviewLineCount=Integer.parseInt(ed.toString()); saveAndUpdate(); } catch (Exception e) { /* */ }
}
});
prefLocalCacheSize.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() prefLocalCacheSize.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener()
{ {
@ -254,9 +226,6 @@ 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(); });
@ -288,67 +257,6 @@ 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)
{
if (!prev && now)
{
SCNApp.showToast("SimpleCloudNotifier is now enabled", Toast.LENGTH_SHORT);
}
else if (prev && !now)
{
SCNApp.showToast("SimpleCloudNotifier is now disabled\nYou won't recieve new messages.", Toast.LENGTH_LONG);
}
}
private void updateVolume(int idx, int volume) private void updateVolume(int idx, int volume)
{ {
if (mPlayers[idx] != null && mPlayers[idx].isPlaying()) if (mPlayers[idx] != null && mPlayers[idx].isPlaying())
@ -416,7 +324,6 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
{ {
SCNSettings.inst().save(); SCNSettings.inst().save();
updateUI(); updateUI();
SCNApp.getMainActivity().adpTabs.tab1.updateDeleteSwipeEnabled();
} }
private void onUpgradeAccount() private void onUpgradeAccount()
@ -426,11 +333,11 @@ public class SettingsFragment extends Fragment implements MusicPickerListener
public void updateProState() public void updateProState()
{ {
boolean pmode = IABService.inst().getPurchaseCachedSimple(IABService.IAB_PRO_MODE); Purchase p = IABService.inst().getPurchaseCached(IABService.IAB_PRO_MODE);
if (prefUpgradeAccount != null) prefUpgradeAccount.setVisibility( pmode ? View.GONE : View.VISIBLE); if (prefUpgradeAccount != null) prefUpgradeAccount.setVisibility( p != null ? View.GONE : View.VISIBLE);
if (prefUpgradeAccount_info != null) prefUpgradeAccount_info.setVisibility(pmode ? View.GONE : View.VISIBLE); if (prefUpgradeAccount_info != null) prefUpgradeAccount_info.setVisibility(p != null ? View.GONE : View.VISIBLE);
if (prefUpgradeAccount_msg != null) prefUpgradeAccount_msg.setVisibility( pmode ? View.VISIBLE : View.GONE ); if (prefUpgradeAccount_msg != null) prefUpgradeAccount_msg.setVisibility( p != null ? View.VISIBLE : View.GONE );
} }
private int getCacheSizeIndex(int value) private int getCacheSizeIndex(int value)

View File

@ -35,7 +35,7 @@ public class TabAdapter extends FragmentStatePagerAdapter {
{ {
switch (position) switch (position)
{ {
case 0: return "Messages"; case 0: return "Notifications";
case 1: return "Account"; case 1: return "Account";
case 2: return "Settings"; case 2: return "Settings";
default: return null; default: return null;

View File

@ -1,44 +0,0 @@
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.inst().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);
}
});
}
}

View File

@ -1,66 +0,0 @@
package com.blackforestbytes.simplecloudnotifier.view.debug;
import android.content.Context;
import android.graphics.Color;
import androidx.annotation.NonNull;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import com.blackforestbytes.simplecloudnotifier.R;
import com.blackforestbytes.simplecloudnotifier.model.SingleQuery;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
public class QueryLogAdapter extends ArrayAdapter<SingleQuery>
{
public static DateTimeFormatter UI_FULLTIME_FORMATTER = DateTimeFormat.forPattern("HH:mm:ss");
public QueryLogAdapter(@NonNull Context context, @NonNull SingleQuery[] objects)
{
super(context, R.layout.adapter_querylog, objects);
}
@NonNull
@Override
public View getView(int position, View convertView, @NonNull ViewGroup parent)
{
View v = convertView;
if (v == null) {
LayoutInflater vi;
vi = LayoutInflater.from(getContext());
v = vi.inflate(R.layout.adapter_querylog, parent, false);
}
SingleQuery p = getItem(position);
if (p != null)
{
TextView tt1 = v.findViewById(R.id.list_item_debuglogrow_time);
if (tt1 != null) tt1.setText(p.Timestamp.toString(UI_FULLTIME_FORMATTER));
if (tt1 != null) tt1.setTextColor(Color.BLACK);
TextView tt2 = v.findViewById(R.id.list_item_debuglogrow_level);
if (tt2 != null) tt2.setText(p.Level.toUIString());
if (tt2 != null) tt2.setTextColor(Color.BLACK);
TextView tt3 = v.findViewById(R.id.list_item_debuglogrow_info);
if (tt3 != null) tt3.setText("");
if (tt3 != null) tt3.setTextColor(Color.BLUE);
TextView tt4 = v.findViewById(R.id.list_item_debuglogrow_id);
if (tt4 != null) tt4.setText(p.Name);
if (tt4 != null) tt4.setTextColor(p.Level.getColor());
TextView tt5 = v.findViewById(R.id.list_item_debuglogrow_message);
if (tt5 != null) tt5.setText(p.ExceptionString.length()> 40 ? p.ExceptionString.substring(0, 40-3)+"..." : p.ExceptionString);
if (tt5 != null) tt5.setTextColor(p.Level.getColor());
}
return v;
}
}

View File

@ -1,38 +0,0 @@
package com.blackforestbytes.simplecloudnotifier.view.debug;
import androidx.appcompat.app.AppCompatActivity;
import android.annotation.SuppressLint;
import android.os.Bundle;
import android.widget.TextView;
import com.blackforestbytes.simplecloudnotifier.R;
import com.blackforestbytes.simplecloudnotifier.lib.string.CompactJsonFormatter;
import com.blackforestbytes.simplecloudnotifier.model.SingleQuery;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import java.util.Objects;
public class SingleQueryLogActivity extends AppCompatActivity
{
@Override
@SuppressLint("SetTextI18n")
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_singlequerylog);
SingleQuery q = SingleQuery.load(getIntent().getBundleExtra("query"), "data");
this.<TextView>findViewById(R.id.tvQL_Timestamp).setText(q.Timestamp.toString(DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")));
this.<TextView>findViewById(R.id.tvQL_Level).setText(q.Level.toUIString());
this.<TextView>findViewById(R.id.tvQL_Level).setTextColor(q.Level.getColor());
this.<TextView>findViewById(R.id.tvQL_Name).setText(q.Name);
this.<TextView>findViewById(R.id.tvQL_URL).setText(q.URL.replace("?", "\r\n?").replace("&", "\r\n&"));
this.<TextView>findViewById(R.id.tvQL_Response).setText(CompactJsonFormatter.formatJSON(q.Response, 999));
this.<TextView>findViewById(R.id.tvQL_ResponseCode).setText(Integer.toString(q.ResponseCode));
this.<TextView>findViewById(R.id.tvQL_ExceptionString).setText(q.ExceptionString);
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 472 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 320 B

View File

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" >
<solid android:color="@android:color/white" />
<stroke android:width="1dip" android:color="#888888"/>
</shape>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 551 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 949 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1,15 @@
<vector android:height="24dp" android:viewportHeight="1000"
android:viewportWidth="1000" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000"
android:pathData="M500,500m-500,0a500,500 0,1 1,1000 0a500,500 0,1 1,-1000 0"
android:strokeColor="#000000" android:strokeWidth="1"/>
<path android:fillColor="#ffffff" android:pathData="M300,694L700,694L500,136L300,694"/>
<path android:fillColor="#ffffff" android:pathData="M473,559L527,559L527,774L473,774"/>
<path android:fillColor="#000000" android:pathData="M376,640L624,640L500,295L376,640"/>
<path android:fillColor="#ffffff" android:pathData="M100,730L500,730L300,172L100,730"/>
<path android:fillColor="#000000" android:pathData="M176,676L424,676L300,331L176,676"/>
<path android:fillColor="#ffffff" android:pathData="M273,595L327,595L327,810L273,810"/>
<path android:fillColor="#ffffff" android:pathData="M500,730L900,730L700,172L500,730"/>
<path android:fillColor="#000000" android:pathData="M576,676L824,676L700,331L576,676"/>
<path android:fillColor="#ffffff" android:pathData="M673,595L727,595L727,810L673,810"/>
</vector>

View File

@ -1,10 +0,0 @@
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:width="12sp"
android:height="12sp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z"/>
</vector>

View File

@ -1,16 +0,0 @@
<vector
android:height="12sp"
android:width="12sp"
android:viewportHeight="53"
android:viewportWidth="53"
xmlns:android="http://schemas.android.com/apk/res/android">
<path
android:fillColor="#FFFFFF"
android:pathData="M42.943,6H33.5V3c0,-1.654 -1.346,-3 -3,-3h-8c-1.654,0 -3,1.346 -3,3v3h-9.443C8.096,6 6.5,7.596 6.5,9.557V14h2h36h2V9.557C46.5,7.596 44.904,6 42.943,6zM31.5,6h-10V3c0,-0.552 0.449,-1 1,-1h8c0.551,0 1,0.448 1,1V6z"/>
<path
android:fillColor="#FFFFFF"
android:pathData="M8.5,49.271C8.5,51.327 10.173,53 12.229,53h28.541c2.057,0 3.729,-1.673 3.729,-3.729V16h-36V49.271z"/>
</vector>

View File

@ -1,31 +1,31 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout android:id="@+id/layoutRoot" <RelativeLayout
android:id="@+id/layoutRoot"
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"> tools:showIn="@layout/activity_main">
<androidx.appcompat.widget.Toolbar <androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar" android:id="@+id/toolbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentTop="true" android:layout_alignParentTop="true"
app:titleTextColor="@color/colorOnPrimary"
android:background="?attr/colorPrimary" android:background="?attr/colorPrimary"
android:elevation="6dp" android:elevation="6dp"
android:minHeight="?attr/actionBarSize" /> android:minHeight="?attr/actionBarSize"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
<com.google.android.material.tabs.TabLayout <com.google.android.material.tabs.TabLayout
android:id="@+id/tab_layout" android:id="@+id/tab_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@+id/toolbar" android:layout_below="@+id/toolbar"
app:titleTextColor="@color/colorOnPrimary"
app:tabTextColor="@color/colorOnPrimary"
app:tabSelectedTextColor="@color/colorSecondary"
android:background="?attr/colorPrimary" android:background="?attr/colorPrimary"
android:elevation="6dp" android:elevation="6dp"
android:minHeight="?attr/actionBarSize" /> android:minHeight="?attr/actionBarSize"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"/>
<androidx.viewpager.widget.ViewPager <androidx.viewpager.widget.ViewPager
android:id="@+id/pager" android:id="@+id/pager"

View File

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
android:id="@+id/layoutRoot"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/lvQueryList"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>

View File

@ -1,240 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:padding="4sp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:textAlignment="center"
android:textStyle="bold"
android:layout_margin="2dip"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:text="Server Query" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_margin="2dip"
android:layout_height="wrap_content"
android:layout_width="100dp"
android:text="Timestamp" />
<com.blackforestbytes.simplecloudnotifier.util.MaxHeightScrollView
app:maxHeightOverride="100"
android:background="@drawable/simple_black_border"
android:layout_margin="2dip"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fillViewport="true">
<TextView
android:id="@+id/tvQL_Timestamp"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:padding="1dip"
android:textIsSelectable="true"
android:text="" />
</com.blackforestbytes.simplecloudnotifier.util.MaxHeightScrollView>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_margin="2dip"
android:layout_height="wrap_content"
android:layout_width="100dp"
android:text="Level" />
<com.blackforestbytes.simplecloudnotifier.util.MaxHeightScrollView
app:maxHeightOverride="100"
android:background="@drawable/simple_black_border"
android:layout_margin="2dip"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fillViewport="true">
<TextView
android:id="@+id/tvQL_Level"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:padding="1dip"
android:textIsSelectable="true"
android:text="" />
</com.blackforestbytes.simplecloudnotifier.util.MaxHeightScrollView>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_margin="2dip"
android:layout_height="wrap_content"
android:layout_width="100dp"
android:text="Name" />
<com.blackforestbytes.simplecloudnotifier.util.MaxHeightScrollView
app:maxHeightOverride="100"
android:layout_margin="2dip"
android:background="@drawable/simple_black_border"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fillViewport="true">
<TextView
android:id="@+id/tvQL_Name"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:padding="1dip"
android:textIsSelectable="true"
android:text="" />
</com.blackforestbytes.simplecloudnotifier.util.MaxHeightScrollView>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_margin="2dip"
android:layout_height="wrap_content"
android:layout_width="100dp"
android:text="URL" />
<com.blackforestbytes.simplecloudnotifier.util.MaxHeightScrollView
app:maxHeightOverride="100"
android:layout_margin="2dip"
android:background="@drawable/simple_black_border"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fillViewport="true">
<TextView
android:id="@+id/tvQL_URL"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:padding="1dip"
android:textIsSelectable="true"
android:text=""/>
</com.blackforestbytes.simplecloudnotifier.util.MaxHeightScrollView>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_margin="2dip"
android:layout_height="wrap_content"
android:layout_width="100dp"
android:text="ResponeCode" />
<com.blackforestbytes.simplecloudnotifier.util.MaxHeightScrollView
app:maxHeightOverride="64"
android:layout_margin="2dip"
android:background="@drawable/simple_black_border"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fillViewport="true">
<TextView
android:id="@+id/tvQL_ResponseCode"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:padding="1dip"
android:textIsSelectable="true"
android:text="" />
</com.blackforestbytes.simplecloudnotifier.util.MaxHeightScrollView>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_margin="2dip"
android:layout_height="wrap_content"
android:layout_width="100dp"
android:text="Response" />
<com.blackforestbytes.simplecloudnotifier.util.MaxHeightScrollView
app:maxHeightOverride="100"
android:layout_margin="2dip"
android:background="@drawable/simple_black_border"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fillViewport="true">
<TextView
android:id="@+id/tvQL_Response"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:padding="1dip"
android:textIsSelectable="true"
android:text="" />
</com.blackforestbytes.simplecloudnotifier.util.MaxHeightScrollView>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_margin="2dip"
android:layout_height="wrap_content"
android:layout_width="100dp"
android:text="Exception" />
<com.blackforestbytes.simplecloudnotifier.util.MaxHeightScrollView
app:maxHeightOverride="100"
android:layout_margin="2dip"
android:background="@drawable/simple_black_border"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fillViewport="true">
<TextView
android:id="@+id/tvQL_ExceptionString"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:padding="1dip"
android:textIsSelectable="true"
android:text="" />
</com.blackforestbytes.simplecloudnotifier.util.MaxHeightScrollView>
</LinearLayout>
</LinearLayout>
</FrameLayout>

View File

@ -1,50 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/list_item_imagerow"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<LinearLayout
android:orientation="vertical"
android:layout_width="96dp"
android:layout_height="match_parent">
<TextView
android:id="@+id/list_item_debuglogrow_time"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/list_item_debuglogrow_level"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/list_item_debuglogrow_info"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/list_item_debuglogrow_id"
android:textStyle="bold"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/list_item_debuglogrow_message"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>

View File

@ -198,27 +198,19 @@
app:layout_constraintBottom_toTopOf="@+id/btnAccountReset" app:layout_constraintBottom_toTopOf="@+id/btnAccountReset"
app:layout_constraintTop_toBottomOf="@+id/ic_img_quota" /> app:layout_constraintTop_toBottomOf="@+id/ic_img_quota" />
<com.google.android.material.button.MaterialButton <Button
android:id="@+id/btnAccountReset" android:id="@+id/btnAccountReset"
app:cornerRadius="0dp"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginStart="4dp"
android:layout_marginEnd="4dp"
android:backgroundTint="#fa315b"
android:text="@string/str_reset_account" android:text="@string/str_reset_account"
app:layout_constraintBottom_toTopOf="@+id/btnClearLocalStorage" /> app:layout_constraintBottom_toTopOf="@+id/btnClearLocalStorage" />
<com.google.android.material.button.MaterialButton <Button
android:id="@+id/btnClearLocalStorage" android:id="@+id/btnClearLocalStorage"
app:cornerRadius="0dp"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Clear Messages" android:text="Clear Messages"
android:layout_marginStart="4dp"
android:layout_marginEnd="4dp"
android:backgroundTint="#607D8B"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"

View File

@ -79,62 +79,6 @@
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginBottom="2dp"
android:layout_marginTop="2dp"
android:background="#c0c0c0"/>
<Switch
android:layout_marginStart="4dp"
android:layout_marginEnd="4dp"
android:id="@+id/prefEnableDeleteSwipe"
android:text="@string/str_deleteswipe"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="48dp" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginBottom="2dp"
android:layout_marginTop="2dp"
android:background="#c0c0c0"/>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_marginStart="4dp"
android:layout_marginEnd="4dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="48dp">
<TextView
android:id="@+id/tvPreviewLineCount"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/str_previewlinecount"
android:textColor="#000"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/prefPreviewLineCount"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:minWidth="64dp"
android:id="@+id/prefPreviewLineCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="number"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:importantForAutofill="no"
tools:ignore="LabelFor,UnusedAttribute" />
</androidx.constraintlayout.widget.ConstraintLayout>
<View <View
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="1dp" android:layout_height="1dp"
@ -151,10 +95,8 @@
android:gravity="center|center" android:gravity="center|center"
android:minHeight="48dp"> android:minHeight="48dp">
<com.google.android.material.button.MaterialButton <Button
android:id="@+id/prefUpgradeAccount" android:id="@+id/prefUpgradeAccount"
app:cornerRadius="0dp"
android:backgroundTint="#4CAF50"
android:text="@string/str_upgrade_account" android:text="@string/str_upgrade_account"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />
@ -805,24 +747,6 @@
</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

@ -108,43 +108,6 @@
android:paddingTop="3dp" android:paddingTop="3dp"
android:contentDescription="@string/desc_priority_icon" /> android:contentDescription="@string/desc_priority_icon" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btnShare"
style="@style/Widget.MaterialComponents.Button.Icon"
app:cornerRadius="0dp"
app:icon="@drawable/ic_share_small"
android:layout_width="wrap_content"
android:layout_height="25dp"
android:layout_marginEnd="8sp"
android:insetTop="0dp"
android:insetBottom="0dp"
android:insetLeft="0dp"
android:insetRight="0dp"
android:textSize="12sp"
card_view:layout_constraintTop_toBottomOf="@id/tvMessage"
app:layout_constraintRight_toLeftOf="@id/btnDelete"
app:layout_constraintBottom_toBottomOf="parent"
android:backgroundTint="#03A9F4"
android:text="Share" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btnDelete"
style="@style/Widget.MaterialComponents.Button.Icon"
app:cornerRadius="0dp"
app:icon="@drawable/ic_trash_small"
android:layout_width="wrap_content"
android:layout_height="25dp"
android:insetTop="0dp"
android:insetBottom="0dp"
android:insetLeft="0dp"
android:insetRight="0dp"
android:textSize="12sp"
card_view:layout_constraintTop_toBottomOf="@id/tvMessage"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:backgroundTint="#F44336"
android:text="Delete"/>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView> </androidx.cardview.widget.CardView>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MaxHeightScrollView">
<attr name="maxHeightOverride" format="integer" />
</declare-styleable>
</resources>

View File

@ -6,9 +6,6 @@
<color name="colorHeader">#3F51B5</color> <color name="colorHeader">#3F51B5</color>
<color name="colorHeaderForeground">#FFFFFF</color> <color name="colorHeaderForeground">#FFFFFF</color>
<color name="colorOnPrimary">#ecf0f1</color>
<color name="colorSecondary">#FF5722</color>
<color name="colorBlack">#000</color> <color name="colorBlack">#000</color>
<color name="bg_row_background">#fa315b</color> <color name="bg_row_background">#fa315b</color>

View File

@ -6,5 +6,4 @@
<dimen name="padd_10">10dp</dimen> <dimen name="padd_10">10dp</dimen>
<dimen name="ic_delete">30dp</dimen> <dimen name="ic_delete">30dp</dimen>
<dimen name="thumbnail">90dp</dimen> <dimen name="thumbnail">90dp</dimen>
<dimen name="fab_margin">16dp</dimen>
</resources> </resources>

View File

@ -11,13 +11,13 @@
<string name="ic_img_fuel_desc">Icon Fuel</string> <string name="ic_img_fuel_desc">Icon Fuel</string>
<string name="str_qr_code">QR Code</string> <string name="str_qr_code">QR Code</string>
<string name="str_reset_account">Reset Account</string> <string name="str_reset_account">Reset Account</string>
<string name="no_notifications">No messages</string> <string name="no_notifications">No notifications</string>
<string name="str_not_connected">not connected</string> <string name="str_not_connected">not connected</string>
<string name="str_reload">reload</string> <string name="str_reload">reload</string>
<string name="desc_priority_icon">Priority icon</string> <string name="desc_priority_icon">Priority icon</string>
<string name="str_common_settings">Common Settings</string> <string name="str_common_settings">Common Settings</string>
<string name="str_enabled">Enabled</string> <string name="str_enabled">Enabled</string>
<string name="str_localcachesize">Remember the last x messages locally</string> <string name="str_localcachesize">Remember the last x notifications locally</string>
<string name="str_header_prio0">Notifications (priority 0 - Low)</string> <string name="str_header_prio0">Notifications (priority 0 - Low)</string>
<string name="str_header_prio1">Notifications (priority 1 - Normal)</string> <string name="str_header_prio1">Notifications (priority 1 - Normal)</string>
<string name="str_header_prio2">Notifications (priority 2 - High)</string> <string name="str_header_prio2">Notifications (priority 2 - High)</string>
@ -29,14 +29,9 @@
<string name="str_ledcolor">Notification light color</string> <string name="str_ledcolor">Notification light color</string>
<string name="str_enable_vibration">Enable notification vibration</string> <string name="str_enable_vibration">Enable notification vibration</string>
<string name="str_upgrade_account">Upgrade account</string> <string name="str_upgrade_account">Upgrade account</string>
<string name="str_deleteswipe">Delete messages by swiping left</string>
<string name="str_previewlinecount">Number of visibile lines in collapsed messages</string>
<string name="str_promode">Thank you for supporting the app and using the pro mode</string> <string name="str_promode">Thank you for supporting the app and using the pro mode</string>
<string name="str_promode_info">Increase your daily quota, remove the ad banner and support the developer (that\'s me)</string> <string name="str_promode_info">Increase your daily quota, remove the ad banner and support the developer (that\'s me)</string>
<string name="volume_icon">Volume icon</string> <string name="volume_icon">Volume icon</string>
<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="import_settings">Import settings</string>
<string name="export_settings">Export settings</string>
</resources> </resources>

View File

@ -1,20 +1,13 @@
<resources> <resources>
<!-- Base application theme. --> <!-- Base application theme. -->
<style name="AppTheme" parent="Theme.MaterialComponents.Light.NoActionBar"> <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- your app branding color for the app bar --> <!-- your app branding color for the app bar -->
<item name="colorPrimary">#3F51B5</item> <item name="colorPrimary">#3F51B5</item>
<!-- darker variant for the status bar and contextual app bars --> <!-- darker variant for the status bar and contextual app bars -->
<item name="colorPrimaryDark">#303F9F</item> <item name="colorPrimaryDark">#303F9F</item>
<!-- theme UI controls like checkboxes and text fields --> <!-- theme UI controls like checkboxes and text fields -->
<item name="colorAccent">#FF5722</item> <item name="colorAccent">#FF4081</item>
<item name="colorSecondary">#FF5722</item>
</style> </style>
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.MaterialComponents.Dark.ActionBar" />
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.MaterialComponents.Light" />
</resources> </resources>

View File

@ -1,7 +0,0 @@
<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 @@
#Thu Mar 05 15:29:10 UTC 2020 #Sat Nov 17 03:25:30 CET 2018
VERSION_NAME=1.8.0 VERSION_NAME=0.0.8
VERSION_CODE=23 VERSION_CODE=8

View File

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

View File

@ -14,6 +14,3 @@ org.gradle.jvmargs=-Xmx1536m
android.useAndroidX=true android.useAndroidX=true
android.enableJetifier=true android.enableJetifier=true
android.defaults.buildfeatures.buildconfig=true
android.nonTransitiveRClass=false
android.nonFinalResIds=false

View File

@ -1,6 +1,6 @@
#Tue Nov 03 14:10:19 CET 2020 #Wed Sep 26 22:10:14 CEST 2018
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-8.11.1-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip

View File

@ -1,58 +0,0 @@
# Created by https://www.toptal.com/developers/gitignore/api/java,gradle
# Edit at https://www.toptal.com/developers/gitignore?templates=java,gradle
### Java ###
# Compiled class file
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
replay_pid*
### Gradle ###
.gradle
**/build/
!src/**/build/
# Ignore Gradle GUI config
gradle-app.setting
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
!gradle-wrapper.jar
# Avoid ignore Gradle wrappper properties
!gradle-wrapper.properties
# Cache of project
.gradletasknamecache
# Eclipse Gradle plugin generated files
# Eclipse Core
.project
# JDT-specific (Eclipse Java Development Tools)
.classpath
### Gradle Patch ###
# Java heap dump
*.hprof
# End of https://www.toptal.com/developers/gitignore/api/java,gradle

View File

@ -1,8 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="18" />
</component>
</project>

View File

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
</set>
</option>
</GradleProjectSettings>
</option>
</component>
</project>

View File

@ -1,11 +0,0 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="JavadocReference" enabled="true" level="WARNING" enabled_by_default="true" editorAttributes="WARNING_ATTRIBUTES" />
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
<option name="processCode" value="true" />
<option name="processLiterals" value="true" />
<option name="processComments" value="true" />
</inspection_tool>
</profile>
</component>

View File

@ -1,25 +0,0 @@
<?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="MavenRepo" />
<option name="name" value="MavenRepo" />
<option name="url" value="https://repo.maven.apache.org/maven2/" />
</remote-repository>
<remote-repository>
<option name="id" value="maven" />
<option name="name" value="maven" />
<option name="url" value="https://jitpack.io" />
</remote-repository>
</component>
</project>

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="FrameworkDetectionExcludesConfiguration">
<file type="web" url="file://$PROJECT_DIR$" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_18" default="true" project-jdk-name="openjdk-18" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
</component>
</project>

View File

@ -1,47 +0,0 @@
buildscript {
repositories {
gradlePluginPortal()
}
dependencies {
classpath 'gradle.plugin.com.github.johnrengelman:shadow:7.1.2'
}
}
plugins {
id 'java'
id("com.github.johnrengelman.shadow") version "7.1.2"
id 'application'
}
group 'com.blackforestbytes'
version '1.0-SNAPSHOT'
repositories {
mavenCentral()
maven { url "https://jitpack.io" }
}
application {
mainClass = 'com.blackforestbytes.Main'
}
jar {
manifest {
attributes 'Main-Class': application.mainClass
}
}
tasks.jar {
manifest.attributes["Main-Class"] = application.mainClass
}
dependencies {
implementation 'com.github.RalleYTN:SimpleJSON:2.1.1'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
}
test {
useJUnitPlatform()
}

View File

@ -1,5 +0,0 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@ -1,240 +0,0 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

View File

@ -1,91 +0,0 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@ -1,2 +0,0 @@
rootProject.name = 'androidExportReader'

View File

@ -1,104 +0,0 @@
package com.blackforestbytes;
import de.ralleytn.simple.json.JSONArray;
import de.ralleytn.simple.json.JSONFormatter;
import de.ralleytn.simple.json.JSONObject;
import java.io.ObjectInputStream;
import java.net.URI;
import java.nio.file.FileSystems;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
public class Main {
@SuppressWarnings("unchecked")
public static void main(String[] args) {
if (args.length != 1) {
System.err.println("call with ./androidExportConvert scn_export.dat");
return;
}
try {
var path = FileSystems.getDefault().getPath(args[0]).normalize().toAbsolutePath().toUri().toURL();
ObjectInputStream stream = new ObjectInputStream(path.openStream());
Map<String, ?> d1 = new HashMap<>((Map<String, ?>)stream.readObject());
Map<String, ?> d2 = new HashMap<>((Map<String, ?>)stream.readObject());
Map<String, ?> d3 = new HashMap<>((Map<String, ?>)stream.readObject());
Map<String, ?> d4 = new HashMap<>((Map<String, ?>)stream.readObject());
stream.close();
JSONObject root = new JSONObject();
var subConfig = new JSONObject();
var subIAB = new JSONArray();
var subCMessageList = new JSONArray();
var subAcks = new JSONArray();
var subQueryLog = new JSONArray();
for (Map.Entry<String, ?> entry : d1.entrySet())
{
if (entry.getValue() instanceof String) subConfig.put(entry.getKey(), (String)entry.getValue());
if (entry.getValue() instanceof Boolean) subConfig.put(entry.getKey(), (Boolean)entry.getValue());
if (entry.getValue() instanceof Float) subConfig.put(entry.getKey(), (Float)entry.getValue());
if (entry.getValue() instanceof Integer) subConfig.put(entry.getKey(), (Integer)entry.getValue());
if (entry.getValue() instanceof Long) subConfig.put(entry.getKey(), (Long)entry.getValue());
if (entry.getValue() instanceof Set<?>) subConfig.put(entry.getKey(), ((Set<String>)entry.getValue()).toArray());
}
for (int i = 0; i < (Integer)d2.get("c"); i++) {
var obj = new JSONObject();
obj.put("key", d2.get("["+i+"]->key"));
obj.put("value", d2.get("["+i+"]->value"));
subIAB.add(obj);
}
for (int i = 0; i < (Integer)d3.get("message_count"); i++) {
if (d3.get("message["+i+"].scnid") == null)
throw new Exception("ONF");
var obj = new JSONObject();
obj.put("timestamp", d3.get("message["+i+"].timestamp"));
obj.put("title", d3.get("message["+i+"].title"));
obj.put("content", d3.get("message["+i+"].content"));
obj.put("priority", d3.get("message["+i+"].priority"));
obj.put("scnid", d3.get("message["+i+"].scnid"));
subCMessageList.add(obj);
}
subAcks.addAll(((Set<String>)d3.get("acks")).stream().map(p -> Long.decode("0x"+p)).toList());
for (int i = 0; i < (Integer)d4.get("history_count"); i++) {
if (d4.get("message["+(i+1000)+"].Name") == null)
throw new Exception("ONF");
var obj = new JSONObject();
obj.put("Level", d4.get("message["+(i+1000)+"].Level"));
obj.put("Timestamp", d4.get("message["+(i+1000)+"].Timestamp"));
obj.put("Name", d4.get("message["+(i+1000)+"].Name"));
obj.put("URL", d4.get("message["+(i+1000)+"].URL"));
obj.put("Response", d4.get("message["+(i+1000)+"].Response"));
obj.put("ResponseCode", d4.get("message["+(i+1000)+"].ResponseCode"));
obj.put("ExceptionString", d4.get("message["+(i+1000)+"].ExceptionString"));
subQueryLog.add(obj);
}
root.put("config", subConfig);
root.put("iab", subIAB);
root.put("cmessagelist", subCMessageList);
root.put("acks", subAcks);
root.put("querylog", subQueryLog);
System.out.println(new JSONFormatter().format(root.toString()));
} catch (Exception e) {
e.printStackTrace();
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 729 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 237 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More