33 Commits

Author SHA1 Message Date
cae6ff6271 implement usr_msg_id 2018-11-12 13:03:56 +01:00
2163ae4dbf index_more.php 2018-11-12 12:41:59 +01:00
083945852b in-app-purchase: pro-mode 2018-11-12 00:28:57 +01:00
jenkins
e69b1232d7 [Jenkins] Increment version 2018-11-11 19:38:22 +01:00
ac681065fa billing permission 2018-11-11 19:36:58 +01:00
90d8b46179 actually use settings 2018-10-22 16:29:37 +02:00
jenkins
280b5ca4aa [Jenkins] Increment version 2018-10-22 15:29:28 +02:00
54a171bd5e ads 2018-10-22 15:16:39 +02:00
c043367810 icon 2018-10-22 14:37:45 +02:00
5494df1c56 settings kinda fin 2018-10-22 14:24:35 +02:00
f9d04e38a0 ColorPicker 2018-10-22 14:04:50 +02:00
6dfd7e016b setting stuff 2018-10-22 13:53:21 +02:00
96a3cbc40a colors 2018-10-22 01:32:21 +02:00
7261795c99 UltimateMusicPicker 2018-10-22 01:23:25 +02:00
d6becd15c1 switch to androidx 2018-10-22 01:05:05 +02:00
0b22a18088 preferences (1) 2018-10-21 22:58:26 +02:00
39fcddfaf0 small stuff 2018-10-20 19:10:10 +02:00
jenkins
38eaa0c132 [Jenkins] Increment version 2018-10-20 18:59:26 +02:00
e33243e589 images 2018-10-20 18:18:19 +02:00
26f04dec9e prep jenkins CI/CD 2018-10-20 17:09:14 +02:00
8b44df8636 added priority to messages 2018-10-20 14:57:05 +02:00
3bb7386d72 url change 2018-09-27 02:38:46 +02:00
5d4ea0e057 settings + api28 2018-09-27 02:30:12 +02:00
0b030406bb notifications 2018-09-27 01:38:56 +02:00
28ef5cb2f5 api fixes 2018-09-26 23:13:50 +02:00
7ea0572d79 does not really work 2018-09-23 20:00:10 +02:00
63633de256 quargljhh 2018-09-23 16:23:49 +02:00
a4cc8752ff quota 2018-09-22 19:57:00 +02:00
543f359acd web kinda finished 2018-09-22 19:40:50 +02:00
fcdb5217ee style 2018-09-22 18:00:00 +02:00
b6252a1c1a android view and stuff 2018-09-22 03:06:09 +02:00
5fcd33e294 webshit 2018-09-22 01:35:41 +02:00
7ecb64fde7 shits working - yo 2018-09-21 23:15:42 +02:00
124 changed files with 6065 additions and 156 deletions

Binary file not shown.

6
.idea/vcs.xml generated
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

@@ -247,9 +247,5 @@ $RECYCLE.BIN/
######################################################################################### #########################################################################################
.idea/caches
# app/release
#
#
#
#

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="priority_low" />
<entry key="sourceFile" value="C:\Users\Mike\Downloads\Low Priority-595b40b75ba036ed117d9842.svg" />
</map>
</option>
</PersistentState>
</value>
</entry>
</map>
</option>
</PersistentState>
</value>
</entry>
</map>
</option>
</component>
</project>

View File

@@ -0,0 +1,10 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<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>

110
android/app/build.gradle Normal file
View File

@@ -0,0 +1,110 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 28
def versionPropsFile = file('version.properties')
def vNumber
def vName
if (versionPropsFile.canRead()) {
Properties versionProps = new Properties()
new FileInputStream(versionPropsFile).withCloseable { stream -> versionProps.load(stream) }
vNumber = versionProps['VERSION_CODE'].toInteger()
vName = versionProps['VERSION_NAME'].toString()
} else throw new FileNotFoundException("Could not read version.properties!")
defaultConfig {
applicationId "com.blackforestbytes.simplecloudnotifier"
minSdkVersion 21
targetSdkVersion 28
versionCode vNumber
versionName vName
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
compileOptions {
targetCompatibility 1.8
sourceCompatibility 1.8
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.recyclerview:recyclerview:1.0.0'
implementation 'androidx.recyclerview:recyclerview:1.0.0'
implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'
implementation 'com.google.android.material:material:1.0.0'
implementation 'com.google.firebase:firebase-core:16.0.4'
implementation 'com.google.firebase:firebase-messaging:17.3.4'
implementation 'com.google.android.gms:play-services-ads:17.1.0'
implementation 'com.android.billingclient:billing:1.2'
implementation 'com.squareup.okhttp3:okhttp:3.10.0'
implementation 'com.github.kenglxn.QRGen:android:2.5.0'
implementation "com.github.DeweyReed:UltimateMusicPicker:2.0.0"
implementation 'com.github.duanhong169:colorpicker:1.1.5'
}
apply plugin: 'com.google.gms.google-services'
task updateVersion << {
def lastTag = ['git', 'describe', "--abbrev=0", "--tags"].execute().text.trim()
def versionPropsFile = file('version.properties')
if (!versionPropsFile.canRead()) throw new FileNotFoundException("Could not read version.properties!")
Properties versionProps = new Properties()
new FileInputStream(versionPropsFile).withCloseable { fis -> versionProps.load(fis) }
def matcher = lastTag =~ /^v([0-9]+)\.([0-9]+)\.([0-9]+)$/
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
if (new File(".do_publish_beta_release").exists()) new File(".do_publish_beta_release").delete()
if (new File(".do_publish_prod_release").exists()) new File(".do_publish_prod_release").delete()
if (vName == versionProps['VERSION_NAME'].toString()) {
println "This version was already built - skip deployment"
} else if (vName.endsWith(".0")) {
println ""
println "====================================================================="
println "====================================================================="
println "(!) This is a new PRODUCTION release - create deployment trigger file"
println "====================================================================="
println "====================================================================="
println ""
vCode++
new File(".do_publish_prod_release").createNewFile()
versionProps['VERSION_NAME'] = vName.toString()
versionProps['VERSION_CODE'] = vCode.toString()
versionPropsFile.newWriter().withCloseable { w -> versionProps.store(w, null) }
} else {
println ""
println "==============================================================="
println "(!) This is a new beta release - create deployment trigger file"
println "==============================================================="
println ""
vCode++
new File(".do_publish_beta_release").createNewFile()
versionProps['VERSION_NAME'] = vName.toString()
versionProps['VERSION_CODE'] = vCode.toString()
versionPropsFile.newWriter().withCloseable { w -> versionProps.store(w, null) }
}
}

View File

@@ -0,0 +1,55 @@
{
"project_info": {
"project_number": "232728961679",
"firebase_url": "https://simplecloudnotifier-ea7ef.firebaseio.com",
"project_id": "simplecloudnotifier-ea7ef",
"storage_bucket": "simplecloudnotifier-ea7ef.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:232728961679:android:23c75317f79601c9",
"android_client_info": {
"package_name": "com.blackforestbytes.simplecloudnotifier"
}
},
"oauth_client": [
{
"client_id": "232728961679-o7gig6f684mp1l1ok7719v3jf3csejc1.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "com.blackforestbytes.simplecloudnotifier",
"certificate_hash": "3bcafbd39256422f0cb51fd446a228c26543afb4"
}
},
{
"client_id": "232728961679-t1h2eo5keha2lrvhsvdr5kgbkbfkja0o.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyBasR6JLAjM5Ut0rPb0euE_9DdDoTkcvKQ"
}
],
"services": {
"analytics_service": {
"status": 1
},
"appinvite_service": {
"status": 2,
"other_platform_oauth_client": [
{
"client_id": "232728961679-t1h2eo5keha2lrvhsvdr5kgbkbfkja0o.apps.googleusercontent.com",
"client_type": 3
}
]
},
"ads_service": {
"status": 2
}
}
}
],
"configuration_version": "1"
}

View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest
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.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="com.android.vending.BILLING" />
<application
android:allowBackup="false"
android:name="SCNApp"
android:icon="@drawable/icon"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning">
<activity android:name=".view.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<meta-data android:name="com.google.firebase.messaging.default_notification_icon" android:resource="@drawable/icon" />
<meta-data android:name="com.google.firebase.messaging.default_notification_color" android:resource="@color/colorAccent" />
<meta-data android:name="com.google.android.gms.ads.AD_MANAGER_APP" android:value="true"/>
<meta-data android:name="com.google.android.gms.ads.APPLICATION_ID" android:value="ca-app-pub-3320562328966175~7579972005"/>
<service android:name=".service.FBMService" android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
</application>
</manifest>

View File

@@ -0,0 +1,130 @@
package com.blackforestbytes.simplecloudnotifier;
import android.app.Application;
import android.content.Context;
import android.widget.Toast;
import com.android.billingclient.api.BillingClient;
import com.blackforestbytes.simplecloudnotifier.view.AccountFragment;
import com.blackforestbytes.simplecloudnotifier.view.MainActivity;
import com.blackforestbytes.simplecloudnotifier.view.TabAdapter;
import java.lang.ref.WeakReference;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.OnLifecycleEvent;
import androidx.lifecycle.ProcessLifecycleOwner;
public class SCNApp extends Application implements LifecycleObserver
{
private static SCNApp instance;
private static WeakReference<MainActivity> mainActivity;
public static final boolean LOCAL_DEBUG = BuildConfig.DEBUG;
public static final boolean DEBUG = BuildConfig.DEBUG || !BuildConfig.VERSION_NAME.endsWith(".0");
public static final boolean RELEASE = !DEBUG;
private static boolean isBackground = true;
public SCNApp()
{
instance = this;
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
}
public static Context getContext()
{
return instance;
}
public static MainActivity getMainActivity()
{
return mainActivity.get();
}
public static boolean isBackground()
{
return isBackground;
}
public static void showToast(final String msg, final int duration)
{
final MainActivity a = mainActivity.get();
if (a != null)
{
a.runOnUiThread(() -> Toast.makeText(a, msg, duration).show());
}
}
public static boolean runOnUiThread(Runnable r)
{
final MainActivity a = mainActivity.get();
if (a != null) {a.runOnUiThread(r); return true;}
return false;
}
public static void refreshAccountTab()
{
runOnUiThread(() ->
{
MainActivity a = mainActivity.get();
if (a == null) return;
TabAdapter ta = a.adpTabs;
if (ta == null) return;
AccountFragment tf = ta.tab2;
if (tf == null) return;
tf.updateUI();
});
}
public static void register(MainActivity a)
{
mainActivity = new WeakReference<>(a);
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
public void onAppBackgrounded()
{
isBackground = true;
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
public void onAppForegrounded()
{
isBackground = false;
}
}
/*
==TODO==
[X] - Pro mode
[X] - no ads
[X] - more quota
[X] - restore pro mode
[X] - send pro state to server
[X] - prevent duplicate-send
[X] - send custom msg-id in API
[X] - prevent second ack on same msg-id
[X] - more in-depth API doc on website (?)
[X] - perhaps response codes in api (?)
[ ] - test notification channels
[ ] - publish (+ HN post ?)
[ ] - Use for mscom server errrors
[ ] - Use for bfb server errors
[ ] - Use for transmission state
[ ] - Message on connnection lost (seperate process - resend until succ)
[ ] - Message on connnection regained
[ ] - Message on seed-count changed
*/

View File

@@ -0,0 +1,23 @@
package com.blackforestbytes.simplecloudnotifier.lib.android;
import android.util.Log;
public final class ThreadUtils
{
public static void safeSleep(int millisMin, int millisMax)
{
safeSleep(millisMin + (int)(Math.random()*(millisMax-millisMin)));
}
public static void safeSleep(int millis)
{
try
{
Thread.sleep(millis);
}
catch (InterruptedException e)
{
Log.d("ThreadUtils", e.toString());
}
}
}

View File

@@ -0,0 +1,38 @@
package com.blackforestbytes.simplecloudnotifier.lib.collections;
import com.blackforestbytes.simplecloudnotifier.lib.lambda.Func1to1;
import java.util.*;
public final class CollectionHelper
{
public static <T, C> List<T> unique(List<T> input, Func1to1<T, C> mapping)
{
List<T> output = new ArrayList<>(input.size());
HashSet<C> seen = new HashSet<>();
for (T v : input) if (seen.add(mapping.invoke(v))) output.add(v);
return output;
}
public static <T> List<T> sort(List<T> input, Comparator<T> comparator)
{
List<T> output = new ArrayList<>(input);
Collections.sort(output, comparator);
return output;
}
public static <T, U extends Comparable<U>> List<T> sort(List<T> input, Func1to1<T, U> mapper)
{
return sort(input, mapper, 1);
}
public static <T, U extends Comparable<U>> List<T> sort(List<T> input, Func1to1<T, U> mapper, int sortMod)
{
List<T> output = new ArrayList<>(input);
Collections.sort(output, (o1, o2) -> sortMod * mapper.invoke(o1).compareTo(mapper.invoke(o2)));
return output;
}
}

View File

@@ -0,0 +1,14 @@
package com.blackforestbytes.simplecloudnotifier.lib.datatypes;
public class IntRange
{
private int Start;
public int Start() { return Start; }
private int End;
public int End() { return End; }
public IntRange(int s, int e) { Start = s; End = e; }
private IntRange() { }
}

View File

@@ -0,0 +1,10 @@
package com.blackforestbytes.simplecloudnotifier.lib.datatypes;
public class NInt
{
public int Value;
public NInt(int v) { Value = v; }
private NInt() { }
}

View File

@@ -0,0 +1,6 @@
package com.blackforestbytes.simplecloudnotifier.lib.datatypes;
public final class Nothing
{
public final static Nothing Inst = new Nothing();
}

View File

@@ -0,0 +1,11 @@
package com.blackforestbytes.simplecloudnotifier.lib.datatypes;
public class Tuple1<T1>
{
public final T1 Item1;
public Tuple1(T1 i1)
{
Item1 = i1;
}
}

View File

@@ -0,0 +1,13 @@
package com.blackforestbytes.simplecloudnotifier.lib.datatypes;
public class Tuple2<T1, T2>
{
public final T1 Item1;
public final T2 Item2;
public Tuple2(T1 i1, T2 i2)
{
Item1 = i1;
Item2 = i2;
}
}

View File

@@ -0,0 +1,15 @@
package com.blackforestbytes.simplecloudnotifier.lib.datatypes;
public class Tuple3<T1, T2, T3>
{
public final T1 Item1;
public final T2 Item2;
public final T3 Item3;
public Tuple3(T1 i1, T2 i2, T3 i3)
{
Item1 = i1;
Item2 = i2;
Item3 = i3;
}
}

View File

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

View File

@@ -0,0 +1,9 @@
package com.blackforestbytes.simplecloudnotifier.lib.lambda;
@FunctionalInterface
public interface Func0to0 {
Func0to0 EMPTY = ()->{};
void invoke();
}

View File

@@ -0,0 +1,6 @@
package com.blackforestbytes.simplecloudnotifier.lib.lambda;
@FunctionalInterface
public interface Func0to1<TResult> {
TResult invoke();
}

View File

@@ -0,0 +1,8 @@
package com.blackforestbytes.simplecloudnotifier.lib.lambda;
import java.io.IOException;
@FunctionalInterface
public interface Func0to1WithIOException<TResult> {
TResult invoke() throws IOException;
}

View File

@@ -0,0 +1,6 @@
package com.blackforestbytes.simplecloudnotifier.lib.lambda;
@FunctionalInterface
public interface Func1to0<TInput1> {
void invoke(TInput1 value);
}

View File

@@ -0,0 +1,6 @@
package com.blackforestbytes.simplecloudnotifier.lib.lambda;
@FunctionalInterface
public interface Func1to1<TInput1, TResult> {
TResult invoke(TInput1 value);
}

View File

@@ -0,0 +1,6 @@
package com.blackforestbytes.simplecloudnotifier.lib.lambda;
@FunctionalInterface
public interface Func2to0<TInput1, TInput2> {
void invoke(TInput1 value1, TInput2 value2);
}

View File

@@ -0,0 +1,6 @@
package com.blackforestbytes.simplecloudnotifier.lib.lambda;
@FunctionalInterface
public interface Func2to1<TInput1, TInput2, TResult> {
TResult invoke(TInput1 value1, TInput2 value2);
}

View File

@@ -0,0 +1,231 @@
package com.blackforestbytes.simplecloudnotifier.lib.string;
// from MonoSAMFramework.Portable.DebugTools.CompactJsonFormatter
public class CompactJsonFormatter
{
private static final String INDENT_STRING = " ";
public static String formatJSON(String str, int maxIndent)
{
int indent = 0;
boolean quoted = false;
StringBuilder sb = new StringBuilder();
char last = ' ';
for (int i = 0; i < str.length(); i++)
{
char ch = str.charAt(i);
switch (ch)
{
case '\r':
case '\n':
break;
case '{':
case '[':
sb.append(ch);
last = ch;
if (!quoted)
{
indent++;
if (indent >= maxIndent) break;
sb.append("\n");
for (int ix = 0; ix < indent; ix++) sb.append(INDENT_STRING);
last = ' ';
}
break;
case '}':
case ']':
if (!quoted)
{
indent--;
if (indent + 1 >= maxIndent) { sb.append(ch); break; }
sb.append("\n");
for (int ix = 0; ix < indent; ix++) sb.append(INDENT_STRING);
}
sb.append(ch);
last = ch;
break;
case '"':
sb.append(ch);
last = ch;
boolean escaped = false;
int index = i;
while (index > 0 && str.charAt(--index) == '\\')
escaped = !escaped;
if (!escaped)
quoted = !quoted;
break;
case ',':
sb.append(ch);
last = ch;
if (!quoted)
{
if (indent >= maxIndent) { sb.append(' '); last = ' '; break; }
sb.append("\n");
for (int ix = 0; ix < indent; ix++) sb.append(INDENT_STRING);
}
break;
case ':':
sb.append(ch);
last = ch;
if (!quoted) { sb.append(" "); last = ' '; }
break;
case ' ':
case '\t':
if (quoted)
{
sb.append(ch);
last = ch;
}
else if (last != ' ')
{
sb.append(' ');
last = ' ';
}
break;
default:
sb.append(ch);
last = ch;
break;
}
}
return sb.toString();
}
public static String compressJson(String str, int compressionLevel)
{
int indent = 0;
boolean quoted = false;
StringBuilder sb = new StringBuilder();
char last = ' ';
int compress = 0;
for (int i = 0; i < str.length(); i++)
{
char ch = str.charAt(i);
switch (ch)
{
case '\r':
case '\n':
break;
case '{':
case '[':
sb.append(ch);
last = ch;
if (!quoted)
{
if (compress == 0 && getJsonDepth(str, i) <= compressionLevel)
compress = 1;
else if (compress > 0)
compress++;
indent++;
if (compress > 0) break;
sb.append("\n");
for (int ix = 0; ix < indent; ix++) sb.append(INDENT_STRING);
last = ' ';
}
break;
case '}':
case ']':
if (!quoted)
{
indent--;
if (compress > 0) { compress--; sb.append(ch); break; }
compress--;
sb.append("\n");
for (int ix = 0; ix < indent; ix++) sb.append(INDENT_STRING);
}
sb.append(ch);
last = ch;
break;
case '"':
sb.append(ch);
last = ch;
boolean escaped = false;
int index = i;
while (index > 0 && str.charAt(--index) == '\\')
escaped = !escaped;
if (!escaped)
quoted = !quoted;
break;
case ',':
sb.append(ch);
last = ch;
if (!quoted)
{
if (compress > 0) { sb.append(' '); last = ' '; break; }
sb.append("\n");
for (int ix = 0; ix < indent; ix++) sb.append(INDENT_STRING);
}
break;
case ':':
sb.append(ch);
last = ch;
if (!quoted) { sb.append(" "); last = ' '; }
break;
case ' ':
case '\t':
if (quoted)
{
sb.append(ch);
last = ch;
}
else if (last != ' ')
{
sb.append(' ');
last = ' ';
}
break;
default:
sb.append(ch);
last = ch;
break;
}
}
return sb.toString();
}
public static int getJsonDepth(String str, int i)
{
int maxindent = 0;
int indent = 0;
boolean quoted = false;
for (; i < str.length(); i++)
{
char ch = str.charAt(i);
switch (ch)
{
case '{':
case '[':
if (!quoted)
{
indent++;
maxindent = Math.max(indent, maxindent);
}
break;
case '}':
case ']':
if (!quoted)
{
indent--;
if (indent <= 0) return maxindent;
}
break;
case '"':
boolean escaped = false;
int index = i;
while (index > 0 && str.charAt(--index) == '\\')
escaped = !escaped;
if (!escaped)
quoted = !quoted;
break;
default:
break;
}
}
return maxindent;
}
}

View File

@@ -0,0 +1,98 @@
package com.blackforestbytes.simplecloudnotifier.lib.string;
import android.content.Context;
import android.util.Log;
import com.blackforestbytes.simplecloudnotifier.SCNApp;
import com.blackforestbytes.simplecloudnotifier.lib.lambda.Func1to1;
import java.text.MessageFormat;
import java.util.List;
public class Str
{
public final static String Empty = "";
public static String format(String fmt, Object... data)
{
return MessageFormat.format(fmt, data);
}
public static String rformat(int fmtResId, Object... data)
{
Context inst = SCNApp.getContext();
if (inst == null)
{
Log.e("StringFormat", "rformat::NoInstance --> inst==null for" + fmtResId);
return "?ERR?";
}
return MessageFormat.format(inst.getResources().getString(fmtResId), data);
}
public static String firstLine(String content)
{
int idx = content.indexOf('\n');
if (idx == -1) return content;
if (idx == 0) return Str.Empty;
if (content.charAt(idx-1) == '\r') return content.substring(0, idx-1);
return content.substring(0, idx);
}
public static boolean isNullOrWhitespace(String str)
{
return str == null || str.length() == 0 || str.trim().length() == 0;
}
public static boolean isNullOrEmpty(String str)
{
return str == null || str.length() == 0;
}
public static boolean equals(String a, String b)
{
if (a == null) return (b == null);
return a.equals(b);
}
public static String join(String sep, List<String> list)
{
StringBuilder b = new StringBuilder();
boolean first = true;
for (String v : list)
{
if (!first) b.append(sep);
b.append(v);
first = false;
}
return b.toString();
}
public static <T> String join(String sep, List<T> list, Func1to1<T, String> map)
{
StringBuilder b = new StringBuilder();
boolean first = true;
for (T v : list)
{
if (!first) b.append(sep);
b.append(map.invoke(v));
first = false;
}
return b.toString();
}
public static Integer tryParseToInt(String s)
{
try
{
return Integer.parseInt(s);
}
catch (Exception e)
{
return null;
}
}
}

View File

@@ -0,0 +1,37 @@
package com.blackforestbytes.simplecloudnotifier.model;
import android.annotation.SuppressLint;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
public class CMessage
{
public final long Timestamp ;
public final String Title;
public final String Content;
public final PriorityEnum Priority;
private static final SimpleDateFormat _format;
static
{
_format = new SimpleDateFormat("yyyy'-'MM'-'dd HH':'mm':'ss", Locale.getDefault());
_format.setTimeZone(TimeZone.getDefault());
}
public CMessage(long t, String mt, String mc, PriorityEnum p)
{
Timestamp = t;
Title = mt;
Content = mc;
Priority = p;
}
@SuppressLint("SimpleDateFormat")
public String formatTimestamp()
{
return _format.format(new Date(Timestamp*1000));
}
}

View File

@@ -0,0 +1,145 @@
package com.blackforestbytes.simplecloudnotifier.model;
import android.content.Context;
import android.content.SharedPreferences;
import com.blackforestbytes.simplecloudnotifier.view.MessageAdapter;
import com.blackforestbytes.simplecloudnotifier.SCNApp;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
public class CMessageList
{
public ArrayList<CMessage> Messages;
private ArrayList<WeakReference<MessageAdapter>> _listener = new ArrayList<>();
private final static Object _lock = new Object();
private static CMessageList _inst = null;
public static CMessageList inst()
{
synchronized (_lock)
{
if (_inst != null) return _inst;
return _inst = new CMessageList();
}
}
private CMessageList()
{
Messages = new ArrayList<>();
SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("CMessageList", Context.MODE_PRIVATE);
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));
Messages.add(new CMessage(time, title, content, prio));
}
}
public CMessage add(final long time, final String title, final String content, final PriorityEnum pe)
{
CMessage msg = new CMessage(time, title, content, pe);
boolean run = SCNApp.runOnUiThread(() ->
{
SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("CMessageList", Context.MODE_PRIVATE);
int count = sharedPref.getInt("message_count", 0);
SharedPreferences.Editor e = sharedPref.edit();
Messages.add(msg);
while (Messages.size()>SCNSettings.inst().LocalCacheSize) Messages.remove(0);
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.apply();
for (WeakReference<MessageAdapter> ref : _listener)
{
MessageAdapter a = ref.get();
if (a == null) continue;
a.customNotifyItemInserted(count);
}
CleanUpListener();
});
if (!run)
{
Messages.add(new CMessage(time, title, content, pe));
fullSave();
}
return msg;
}
public void clear()
{
Messages.clear();
fullSave();
for (WeakReference<MessageAdapter> ref : _listener)
{
MessageAdapter a = ref.get();
if (a == null) continue;
a.customNotifyDataSetChanged();
}
CleanUpListener();
}
public void fullSave()
{
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++)
{
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.apply();
}
public CMessage tryGet(int pos)
{
if (pos < 0 || pos >= Messages.size()) return null;
return Messages.get(pos);
}
public int size()
{
return Messages.size();
}
public void register(MessageAdapter adp)
{
_listener.add(new WeakReference<>(adp));
CleanUpListener();
}
private void CleanUpListener()
{
for (int i=_listener.size()-1; i >= 0; i--)
{
if (_listener.get(i).get() == null) _listener.remove(i);
}
}
}

View File

@@ -0,0 +1,17 @@
package com.blackforestbytes.simplecloudnotifier.model;
import android.graphics.Color;
import android.net.Uri;
public class NotificationSettings
{
public boolean EnableSound = false;
public String SoundName = "";
public String SoundSource = Uri.EMPTY.toString();
public boolean RepeatSound = false;
public boolean EnableLED = false;
public int LEDColor = Color.BLUE;
public boolean EnableVibration = false;
}

View File

@@ -0,0 +1,30 @@
package com.blackforestbytes.simplecloudnotifier.model;
public enum PriorityEnum
{
LOW(0),
NORMAL(1),
HIGH(2);
public final int ID;
PriorityEnum(int id) { ID = id; }
public static PriorityEnum parseAPI(String v) throws Exception
{
for (PriorityEnum p : values())
{
if (String.valueOf(p.ID).equals(v.trim())) return p;
}
throw new Exception("Invalid value for <PriorityEnum> : '"+v+"'");
}
public static PriorityEnum parseAPI(int v)
{
for (PriorityEnum p : values())
{
if (p.ID == v) return p;
}
return PriorityEnum.NORMAL;
}
}

View File

@@ -0,0 +1,238 @@
package com.blackforestbytes.simplecloudnotifier.model;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Log;
import android.view.View;
import com.android.billingclient.api.Purchase;
import com.blackforestbytes.simplecloudnotifier.SCNApp;
import com.blackforestbytes.simplecloudnotifier.service.IABService;
import com.google.firebase.iid.FirebaseInstanceId;
public class SCNSettings
{
private final static Object _lock = new Object();
private static SCNSettings _inst = null;
public static SCNSettings inst()
{
synchronized (_lock)
{
if (_inst != null) return _inst;
return _inst = new SCNSettings();
}
}
// ------------------------------------------------------------
public final static Integer[] CHOOSABLE_CACHE_SIZES = new Integer[]{20, 50, 100, 200, 500, 1000, 2000, 5000};
// ------------------------------------------------------------
public int quota_curr;
public int quota_max;
public int user_id;
public String user_key;
public String fcm_token_local;
public String fcm_token_server;
public String promode_token;
public boolean promode_local;
public boolean promode_server;
// ------------------------------------------------------------
public boolean Enabled = true;
public int LocalCacheSize = 500;
public final NotificationSettings PriorityLow = new NotificationSettings();
public final NotificationSettings PriorityNorm = new NotificationSettings();
public final NotificationSettings PriorityHigh = new NotificationSettings();
// ------------------------------------------------------------
public SCNSettings()
{
SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("Config", Context.MODE_PRIVATE);
quota_curr = sharedPref.getInt( "quota_curr", 0);
quota_max = sharedPref.getInt( "quota_max", 0);
user_id = sharedPref.getInt( "user_id", -1);
user_key = sharedPref.getString("user_key", "");
fcm_token_local = sharedPref.getString("fcm_token_local", "");
fcm_token_server = sharedPref.getString("fcm_token_server", "");
promode_local = sharedPref.getBoolean("promode_local", false);
promode_server = sharedPref.getBoolean("promode_server", false);
promode_token = sharedPref.getString("promode_token", "");
Enabled = sharedPref.getBoolean("app_enabled", Enabled);
LocalCacheSize = sharedPref.getInt("local_cache_size", LocalCacheSize);
PriorityLow.EnableLED = sharedPref.getBoolean("priority_low:enabled_led", PriorityLow.EnableLED);
PriorityLow.EnableSound = sharedPref.getBoolean("priority_low:enabled_sound", PriorityLow.EnableSound);
PriorityLow.EnableVibration = sharedPref.getBoolean("priority_low:enabled_vibration", PriorityLow.EnableVibration);
PriorityLow.RepeatSound = sharedPref.getBoolean("priority_low:repeat_sound", PriorityLow.RepeatSound);
PriorityLow.SoundName = sharedPref.getString( "priority_low:sound_name", PriorityLow.SoundName);
PriorityLow.SoundSource = sharedPref.getString( "priority_low:sound_source", PriorityLow.SoundSource);
PriorityLow.LEDColor = sharedPref.getInt( "priority_low:led_color", PriorityLow.LEDColor);
PriorityNorm.EnableLED = sharedPref.getBoolean("priority_norm:enabled_led", PriorityNorm.EnableLED);
PriorityNorm.EnableSound = sharedPref.getBoolean("priority_norm:enabled_sound", PriorityNorm.EnableSound);
PriorityNorm.EnableVibration = sharedPref.getBoolean("priority_norm:enabled_vibration", PriorityNorm.EnableVibration);
PriorityNorm.RepeatSound = sharedPref.getBoolean("priority_norm:repeat_sound", PriorityNorm.RepeatSound);
PriorityNorm.SoundName = sharedPref.getString( "priority_norm:sound_name", PriorityNorm.SoundName);
PriorityNorm.SoundSource = sharedPref.getString( "priority_norm:sound_source", PriorityNorm.SoundSource);
PriorityNorm.LEDColor = sharedPref.getInt( "priority_norm:led_color", PriorityNorm.LEDColor);
PriorityHigh.EnableLED = sharedPref.getBoolean("priority_high:enabled_led", PriorityHigh.EnableLED);
PriorityHigh.EnableSound = sharedPref.getBoolean("priority_high:enabled_sound", PriorityHigh.EnableSound);
PriorityHigh.EnableVibration = sharedPref.getBoolean("priority_high:enabled_vibration", PriorityHigh.EnableVibration);
PriorityHigh.RepeatSound = sharedPref.getBoolean("priority_high:repeat_sound", PriorityHigh.RepeatSound);
PriorityHigh.SoundName = sharedPref.getString( "priority_high:sound_name", PriorityHigh.SoundName);
PriorityHigh.SoundSource = sharedPref.getString( "priority_high:sound_source", PriorityHigh.SoundSource);
PriorityHigh.LEDColor = sharedPref.getInt( "priority_high:led_color", PriorityHigh.LEDColor);
}
public void save()
{
SharedPreferences sharedPref = SCNApp.getContext().getSharedPreferences("Config", Context.MODE_PRIVATE);
SharedPreferences.Editor e = sharedPref.edit();
e.putInt( "quota_curr", quota_curr);
e.putInt( "quota_max", quota_max);
e.putInt( "user_id", user_id);
e.putString( "user_key", user_key);
e.putString( "fcm_token_local", fcm_token_local);
e.putString( "fcm_token_server", fcm_token_server);
e.putBoolean("app_enabled", Enabled);
e.putInt( "local_cache_size", LocalCacheSize);
e.putBoolean("priority_low:enabled_led", PriorityLow.EnableLED);
e.putBoolean("priority_low:enabled_sound", PriorityLow.EnableSound);
e.putBoolean("priority_low:enabled_vibration", PriorityLow.EnableVibration);
e.putBoolean("priority_low:repeat_sound", PriorityLow.RepeatSound);
e.putString( "priority_low:sound_name", PriorityLow.SoundName);
e.putString( "priority_low:sound_source", PriorityLow.SoundSource);
e.putInt( "priority_low:led_color", PriorityLow.LEDColor);
e.putBoolean("priority_norm:enabled_led", PriorityNorm.EnableLED);
e.putBoolean("priority_norm:enabled_sound", PriorityNorm.EnableSound);
e.putBoolean("priority_norm:enabled_vibration", PriorityNorm.EnableVibration);
e.putBoolean("priority_norm:repeat_sound", PriorityNorm.RepeatSound);
e.putString( "priority_norm:sound_name", PriorityNorm.SoundName);
e.putString( "priority_norm:sound_source", PriorityNorm.SoundSource);
e.putInt( "priority_norm:led_color", PriorityNorm.LEDColor);
e.putBoolean("priority_high:enabled_led", PriorityHigh.EnableLED);
e.putBoolean("priority_high:enabled_sound", PriorityHigh.EnableSound);
e.putBoolean("priority_high:enabled_vibration", PriorityHigh.EnableVibration);
e.putBoolean("priority_high:repeat_sound", PriorityHigh.RepeatSound);
e.putString( "priority_high:sound_name", PriorityHigh.SoundName);
e.putString( "priority_high:sound_source", PriorityHigh.SoundSource);
e.putInt( "priority_high:led_color", PriorityHigh.LEDColor);
e.apply();
}
public boolean isConnected()
{
return user_id>=0 && user_key != null && !user_key.isEmpty();
}
public String createOnlineURL()
{
if (!isConnected()) return ServerCommunication.BASE_URL + "index.php";
return ServerCommunication.BASE_URL + "index.php?preset_user_id="+user_id+"&preset_user_key="+user_key;
}
public void setServerToken(String token, View loader)
{
if (isConnected())
{
fcm_token_local = token;
save();
if (!fcm_token_local.equals(fcm_token_server)) ServerCommunication.update(user_id, user_key, fcm_token_local, loader);
}
else
{
fcm_token_local = token;
save();
ServerCommunication.register(fcm_token_local, loader, promode_local, promode_token);
updateProState(loader);
}
}
// called at app start
public void work(Activity a)
{
FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(a, instanceIdResult ->
{
String newToken = instanceIdResult.getToken();
Log.e("FB::GetInstanceId", newToken);
SCNSettings.inst().setServerToken(newToken, null);
}).addOnCompleteListener(r ->
{
if (isConnected()) ServerCommunication.info(user_id, user_key, null);
});
updateProState(null);
}
// reset account key
public void reset(View loader)
{
if (!isConnected()) return;
ServerCommunication.update(user_id, user_key, loader);
}
// refresh account data
public void refresh(View loader, Activity a)
{
if (isConnected())
{
ServerCommunication.info(user_id, user_key, loader);
if (promode_server != promode_local)
{
updateProState(loader);
}
}
else
{
// get token then register
FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(a, instanceIdResult ->
{
String newToken = instanceIdResult.getToken();
Log.e("FB::GetInstanceId", newToken);
SCNSettings.inst().setServerToken(newToken, loader); // does register in here
}).addOnCompleteListener(r ->
{
if (isConnected()) ServerCommunication.info(user_id, user_key, null); // info again for safety
});
}
}
public void updateProState(View loader)
{
Purchase purch = IABService.inst().getPurchaseCached(IABService.IAB_PRO_MODE);
boolean promode_real = (purch != null);
if (promode_real != promode_local || promode_real != promode_server)
{
promode_local = promode_real;
promode_token = promode_real ? purch.getPurchaseToken() : "";
updateProStateOnServer(loader);
}
}
public void updateProStateOnServer(View loader)
{
if (!isConnected()) return;
ServerCommunication.upgrade(user_id, user_key, loader, promode_local, promode_token);
}
}

View File

@@ -0,0 +1,337 @@
package com.blackforestbytes.simplecloudnotifier.model;
import android.util.Log;
import android.view.View;
import com.blackforestbytes.simplecloudnotifier.SCNApp;
import org.json.JSONObject;
import org.json.JSONTokener;
import java.io.IOException;
import java.net.URLEncoder;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
public class ServerCommunication
{
public static final String BASE_URL = /*SCNApp.LOCAL_DEBUG ? "http://localhost:1010/" : */"https://scn.blackforestbytes.com/";
private static final OkHttpClient client = new OkHttpClient();
private ServerCommunication(){ throw new Error("no."); }
public static void register(String token, View loader, boolean pro, String pro_token)
{
try
{
Request request = new Request.Builder()
.url(BASE_URL + "register.php?fcm_token=" + token + "&pro=" + pro + "&pro_token=" + URLEncoder.encode(pro_token, "utf-8"))
.build();
client.newCall(request).enqueue(new Callback()
{
@Override
public void onFailure(Call call, IOException e)
{
e.printStackTrace();
SCNApp.showToast("Communication with server failed", 4000);
SCNApp.runOnUiThread(() -> { if (loader!=null)loader.setVisibility(View.GONE); });
}
@Override
public void onResponse(Call call, Response response)
{
try (ResponseBody responseBody = response.body())
{
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
if (responseBody == null) throw new IOException("No response");
String r = responseBody.string();
Log.d("Server::Response", r);
JSONObject json = (JSONObject) new JSONTokener(r).nextValue();
if (!json.getBoolean("success"))
{
SCNApp.showToast(json.getString("message"), 4000);
return;
}
SCNSettings.inst().user_id = json.getInt("user_id");
SCNSettings.inst().user_key = json.getString("user_key");
SCNSettings.inst().fcm_token_server = token;
SCNSettings.inst().quota_curr = json.getInt("quota");
SCNSettings.inst().quota_max = json.getInt("quota_max");
SCNSettings.inst().promode_server = json.getBoolean("is_pro");
SCNSettings.inst().save();
SCNApp.refreshAccountTab();
}
catch (Exception e)
{
e.printStackTrace();
SCNApp.showToast("Communication with server failed", 4000);
}
finally
{
SCNApp.runOnUiThread(() -> { if (loader!=null)loader.setVisibility(View.GONE); });
}
}
});
}
catch (Exception e)
{
e.printStackTrace();
SCNApp.showToast("Communication with server failed", 4000);
}
}
public static void update(int id, String key, String token, View loader)
{
try
{
Request request = new Request.Builder()
.url(BASE_URL + "update.php?user_id="+id+"&user_key="+key+"&fcm_token="+token)
.build();
client.newCall(request).enqueue(new Callback()
{
@Override
public void onFailure(Call call, IOException e)
{
e.printStackTrace();
SCNApp.showToast("Communication with server failed", 4000);
SCNApp.runOnUiThread(() -> { if (loader!=null)loader.setVisibility(View.GONE); });
}
@Override
public void onResponse(Call call, Response response)
{
try (ResponseBody responseBody = response.body())
{
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
if (responseBody == null) throw new IOException("No response");
String r = responseBody.string();
Log.d("Server::Response", r);
JSONObject json = (JSONObject) new JSONTokener(r).nextValue();
if (!json.getBoolean("success"))
{
SCNApp.showToast(json.getString("message"), 4000);
return;
}
SCNSettings.inst().user_id = json.getInt("user_id");
SCNSettings.inst().user_key = json.getString("user_key");
SCNSettings.inst().fcm_token_server = token;
SCNSettings.inst().quota_curr = json.getInt("quota");
SCNSettings.inst().quota_max = json.getInt("quota_max");
SCNSettings.inst().promode_server = json.getBoolean("is_pro");
SCNSettings.inst().save();
SCNApp.refreshAccountTab();
}
catch (Exception e)
{
e.printStackTrace();
SCNApp.showToast("Communication with server failed", 4000);
}
finally
{
SCNApp.runOnUiThread(() -> { if (loader!=null)loader.setVisibility(View.GONE); });
}
}
});
}
catch (Exception e)
{
e.printStackTrace();
SCNApp.showToast("Communication with server failed", 4000);
}
}
public static void update(int id, String key, View loader)
{
try
{
Request request = new Request.Builder()
.url(BASE_URL + "update.php?user_id=" + id + "&user_key=" + key)
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
SCNApp.showToast("Communication with server failed", 4000);
}
@Override
public void onResponse(Call call, Response response) {
try (ResponseBody responseBody = response.body()) {
if (!response.isSuccessful())
throw new IOException("Unexpected code " + response);
if (responseBody == null) throw new IOException("No response");
String r = responseBody.string();
Log.d("Server::Response", r);
JSONObject json = (JSONObject) new JSONTokener(r).nextValue();
if (!json.getBoolean("success")) {
SCNApp.showToast(json.getString("message"), 4000);
return;
}
SCNSettings.inst().user_id = json.getInt("user_id");
SCNSettings.inst().user_key = json.getString("user_key");
SCNSettings.inst().quota_curr = json.getInt("quota");
SCNSettings.inst().quota_max = json.getInt("quota_max");
SCNSettings.inst().promode_server = json.getBoolean("is_pro");
SCNSettings.inst().save();
SCNApp.refreshAccountTab();
} catch (Exception e) {
e.printStackTrace();
SCNApp.showToast("Communication with server failed", 4000);
} finally {
SCNApp.runOnUiThread(() -> {
if (loader != null) loader.setVisibility(View.GONE);
});
}
}
});
}
catch (Exception e)
{
e.printStackTrace();
SCNApp.showToast("Communication with server failed", 4000);
}
}
public static void info(int id, String key, View loader)
{
try
{
Request request = new Request.Builder()
.url(BASE_URL + "info.php?user_id=" + id + "&user_key=" + key)
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
SCNApp.showToast("Communication with server failed", 4000);
SCNApp.runOnUiThread(() -> {
if (loader != null) loader.setVisibility(View.GONE);
});
}
@Override
public void onResponse(Call call, Response response) {
try (ResponseBody responseBody = response.body()) {
if (!response.isSuccessful())
throw new IOException("Unexpected code " + response);
if (responseBody == null) throw new IOException("No response");
String r = responseBody.string();
Log.d("Server::Response", r);
JSONObject json = (JSONObject) new JSONTokener(r).nextValue();
if (!json.getBoolean("success")) {
SCNApp.showToast(json.getString("message"), 4000);
return;
}
SCNSettings.inst().user_id = json.getInt("user_id");
SCNSettings.inst().quota_curr = json.getInt("quota");
SCNSettings.inst().quota_max = json.getInt("quota_max");
SCNSettings.inst().promode_server = json.getBoolean("is_pro");
SCNSettings.inst().save();
SCNApp.refreshAccountTab();
} catch (Exception e) {
e.printStackTrace();
SCNApp.showToast("Communication with server failed", 4000);
} finally {
SCNApp.runOnUiThread(() -> {
if (loader != null) loader.setVisibility(View.GONE);
});
}
}
});
}
catch (Exception e)
{
e.printStackTrace();
SCNApp.showToast("Communication with server failed", 4000);
}
}
public static void upgrade(int id, String key, View loader, boolean pro, String pro_token)
{
try
{
SCNApp.runOnUiThread(() -> { if (loader != null) loader.setVisibility(View.GONE); });
Request request = new Request.Builder()
.url(BASE_URL + "upgrade.php?user_id=" + id + "&user_key=" + key + "&pro=" + pro + "&pro_token=" + URLEncoder.encode(pro_token, "utf-8"))
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
SCNApp.showToast("Communication with server failed", 4000);
}
@Override
public void onResponse(Call call, Response response) {
try (ResponseBody responseBody = response.body()) {
if (!response.isSuccessful())
throw new IOException("Unexpected code " + response);
if (responseBody == null) throw new IOException("No response");
String r = responseBody.string();
Log.d("Server::Response", r);
JSONObject json = (JSONObject) new JSONTokener(r).nextValue();
if (!json.getBoolean("success")) {
SCNApp.showToast(json.getString("message"), 4000);
return;
}
SCNSettings.inst().user_id = json.getInt("user_id");
SCNSettings.inst().user_key = json.getString("user_key");
SCNSettings.inst().quota_curr = json.getInt("quota");
SCNSettings.inst().quota_max = json.getInt("quota_max");
SCNSettings.inst().promode_server = json.getBoolean("is_pro");
SCNSettings.inst().save();
SCNApp.refreshAccountTab();
} catch (Exception e) {
e.printStackTrace();
SCNApp.showToast("Communication with server failed", 4000);
} finally {
SCNApp.runOnUiThread(() -> { if (loader != null) loader.setVisibility(View.GONE); });
}
}
});
}
catch (Exception e)
{
e.printStackTrace();
SCNApp.showToast("Communication with server failed", 4000);
}
}
}

View File

@@ -0,0 +1,58 @@
package com.blackforestbytes.simplecloudnotifier.service;
import android.util.Log;
import android.widget.Toast;
import com.blackforestbytes.simplecloudnotifier.SCNApp;
import com.blackforestbytes.simplecloudnotifier.model.CMessage;
import com.blackforestbytes.simplecloudnotifier.model.CMessageList;
import com.blackforestbytes.simplecloudnotifier.model.PriorityEnum;
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
public class FBMService extends FirebaseMessagingService
{
@Override
public void onNewToken(String token)
{
Log.i("Firebase::NewToken", token);
SCNSettings.inst().setServerToken(token, null);
}
@Override
public void onMessageReceived(RemoteMessage remoteMessage)
{
try
{
if (!SCNSettings.inst().Enabled) return;
Log.i("FB::MessageReceived", "From: " + remoteMessage.getFrom());
Log.i("FB::MessageReceived", "Payload: " + remoteMessage.getData());
if (remoteMessage.getNotification() != null) Log.i("FB::MessageReceived", "Notify_Title: " + remoteMessage.getNotification().getTitle());
if (remoteMessage.getNotification() != null) Log.i("FB::MessageReceived", "Notify_Body: " + remoteMessage.getNotification().getBody());
long time = Long.parseLong(remoteMessage.getData().get("timestamp"));
String title = remoteMessage.getData().get("title");
String content = remoteMessage.getData().get("body");
PriorityEnum prio = PriorityEnum.parseAPI(remoteMessage.getData().get("priority"));
CMessage msg = CMessageList.inst().add(time, title, content, prio);
if (SCNApp.isBackground())
{
NotificationService.inst().showBackground(msg);
}
else
{
NotificationService.inst().showForeground(msg);
}
}
catch (Exception e)
{
Log.e("FB:Err", e.toString());
SCNApp.showToast("Recieved invalid message from server", Toast.LENGTH_LONG);
}
}
}

View File

@@ -0,0 +1,195 @@
package com.blackforestbytes.simplecloudnotifier.service;
import android.app.Activity;
import android.content.Context;
import android.util.Log;
import android.widget.Toast;
import com.android.billingclient.api.BillingClient;
import com.android.billingclient.api.BillingClientStateListener;
import com.android.billingclient.api.BillingFlowParams;
import com.android.billingclient.api.Purchase;
import com.android.billingclient.api.PurchasesUpdatedListener;
import com.blackforestbytes.simplecloudnotifier.SCNApp;
import com.blackforestbytes.simplecloudnotifier.lib.lambda.Func0to0;
import com.blackforestbytes.simplecloudnotifier.lib.string.Str;
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
import com.blackforestbytes.simplecloudnotifier.view.MainActivity;
import java.util.ArrayList;
import java.util.List;
import androidx.annotation.Nullable;
import static androidx.constraintlayout.widget.Constraints.TAG;
public class IABService implements PurchasesUpdatedListener
{
public static final String IAB_PRO_MODE = "scn.pro.tier1";
private final static Object _lock = new Object();
private static IABService _inst = null;
public static IABService inst()
{
synchronized (_lock)
{
if (_inst != null) return _inst;
throw new Error("IABService == null");
}
}
public static void startup(MainActivity a)
{
synchronized (_lock)
{
_inst = new IABService(a);
}
}
private BillingClient client;
private boolean isServiceConnected;
private final List<Purchase> purchases = new ArrayList<>();
public IABService(Context c)
{
client = BillingClient
.newBuilder(c)
.setListener(this)
.build();
startServiceConnection(this::queryPurchases, false);
}
public void queryPurchases()
{
Func0to0 queryToExecute = () ->
{
long time = System.currentTimeMillis();
Purchase.PurchasesResult purchasesResult = client.queryPurchases(BillingClient.SkuType.INAPP);
Log.i(TAG, "Querying purchases elapsed time: " + (System.currentTimeMillis() - time) + "ms");
if (purchasesResult.getResponseCode() == BillingClient.BillingResponse.OK)
{
for (Purchase p : purchasesResult.getPurchasesList())
{
handlePurchase(p);
}
boolean newProMode = getPurchaseCached(IAB_PRO_MODE) != null;
if (newProMode != SCNSettings.inst().promode_local)
{
refreshProModeListener();
}
}
else
{
Log.w(TAG, "queryPurchases() got an error response code: " + purchasesResult.getResponseCode());
}
};
executeServiceRequest(queryToExecute, false);
}
public void purchase(Activity a, String id)
{
executeServiceRequest(() ->
{
BillingFlowParams flowParams = BillingFlowParams
.newBuilder()
.setSku(id)
.setType(BillingClient.SkuType.INAPP) // SkuType.SUB for subscription
.build();
client.launchBillingFlow(a, flowParams);
}, true);
}
private void executeServiceRequest(Func0to0 runnable, final boolean userRequest) {
if (isServiceConnected)
{
runnable.invoke();
}
else
{
// If billing service was disconnected, we try to reconnect 1 time.
// (feel free to introduce your retry policy here).
startServiceConnection(runnable, userRequest);
}
}
public void destroy()
{
if (client != null && client.isReady()) {
client.endConnection();
client = null;
isServiceConnected = false;
}
}
@Override
public void onPurchasesUpdated(int responseCode, @Nullable List<Purchase> purchases)
{
if (responseCode == BillingClient.BillingResponse.OK && purchases != null)
{
for (Purchase purchase : purchases)
{
handlePurchase(purchase);
}
}
else if (responseCode == BillingClient.BillingResponse.ITEM_ALREADY_OWNED && purchases != null)
{
for (Purchase purchase : purchases)
{
handlePurchase(purchase);
}
}
}
private void handlePurchase(Purchase purchase)
{
Log.d(TAG, "Got a verified purchase: " + purchase);
purchases.add(purchase);
refreshProModeListener();
}
private void refreshProModeListener() {
MainActivity ma = SCNApp.getMainActivity();
if (ma != null) ma.adpTabs.tab3.updateProState();
if (ma != null) ma.adpTabs.tab1.updateProState();
SCNSettings.inst().updateProState(null);
}
public void startServiceConnection(final Func0to0 executeOnSuccess, final boolean userRequest)
{
client.startConnection(new BillingClientStateListener()
{
@Override
public void onBillingSetupFinished(@BillingClient.BillingResponse int billingResponseCode)
{
if (billingResponseCode == BillingClient.BillingResponse.OK)
{
isServiceConnected = true;
if (executeOnSuccess != null) executeOnSuccess.invoke();
}
else
{
if (userRequest) SCNApp.showToast("Could not connect to google services", Toast.LENGTH_SHORT);
}
}
@Override
public void onBillingServiceDisconnected() {
isServiceConnected = false;
}
});
}
public Purchase getPurchaseCached(String id)
{
for (Purchase p : purchases)
{
if (Str.equals(p.getSku(), id)) return p;
}
return null;
}
}

View File

@@ -0,0 +1,131 @@
package com.blackforestbytes.simplecloudnotifier.service;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Build;
import android.widget.Toast;
import com.blackforestbytes.simplecloudnotifier.R;
import com.blackforestbytes.simplecloudnotifier.SCNApp;
import com.blackforestbytes.simplecloudnotifier.model.CMessage;
import com.blackforestbytes.simplecloudnotifier.model.NotificationSettings;
import com.blackforestbytes.simplecloudnotifier.model.PriorityEnum;
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
import com.blackforestbytes.simplecloudnotifier.view.MainActivity;
import androidx.core.app.NotificationCompat;
public class NotificationService
{
private final static String CHANNEL_ID_LOW = "CHAN_BFB_SCN_MESSAGES_LOW";
private final static String CHANNEL_ID_NORM = "CHAN_BFB_SCN_MESSAGES_NORM";
private final static String CHANNEL_ID_HIGH = "CHAN_BFB_SCN_MESSAGES_HIGH";
private final static Object _lock = new Object();
private static NotificationService _inst = null;
public static NotificationService inst()
{
synchronized (_lock)
{
if (_inst != null) return _inst;
return _inst = new NotificationService();
}
}
private NotificationService()
{
updateChannels();
}
public void updateChannels()
{
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return;
Context ctxt = SCNApp.getContext();
NotificationManager notifman = ctxt.getSystemService(NotificationManager.class);
if (notifman == null) return;
NotificationChannel channelLow = notifman.getNotificationChannel(CHANNEL_ID_LOW);
if (channelLow == null) notifman.createNotificationChannel(channelLow = new NotificationChannel(CHANNEL_ID_LOW, "Push notifications (low priority)", NotificationManager.IMPORTANCE_LOW));
NotificationChannel channelNorm = notifman.getNotificationChannel(CHANNEL_ID_NORM);
if (channelNorm == null) notifman.createNotificationChannel(channelNorm = new NotificationChannel(CHANNEL_ID_NORM, "Push notifications (normal priority)", NotificationManager.IMPORTANCE_DEFAULT));
NotificationChannel channelHigh = notifman.getNotificationChannel(CHANNEL_ID_HIGH);
if (channelHigh == null) notifman.createNotificationChannel(channelHigh = new NotificationChannel(CHANNEL_ID_HIGH, "Push notifications (high priority)", NotificationManager.IMPORTANCE_HIGH));
channelLow.setDescription("Messages from the API with priority set to low");
channelLow.enableLights(SCNSettings.inst().PriorityLow.EnableLED);
channelLow.setLightColor(SCNSettings.inst().PriorityLow.LEDColor);
channelLow.enableVibration(SCNSettings.inst().PriorityLow.EnableVibration);
channelLow.setVibrationPattern(new long[]{200});
channelNorm.setDescription("Messages from the API with priority set to normal");
channelNorm.enableLights(SCNSettings.inst().PriorityNorm.EnableLED);
channelNorm.setLightColor(SCNSettings.inst().PriorityNorm.LEDColor);
channelNorm.enableVibration(SCNSettings.inst().PriorityNorm.EnableVibration);
channelNorm.setVibrationPattern(new long[]{200});
channelHigh.setDescription("Messages from the API with priority set to high");
channelHigh.enableLights(SCNSettings.inst().PriorityHigh.EnableLED);
channelHigh.setLightColor(SCNSettings.inst().PriorityHigh.LEDColor);
channelHigh.enableVibration(SCNSettings.inst().PriorityHigh.EnableVibration);
channelHigh.setVibrationPattern(new long[]{200});
channelLow.setBypassDnd(true);
channelLow.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
}
public void showForeground(CMessage msg)
{
SCNApp.showToast("Message recieved: " + msg.Title, Toast.LENGTH_LONG);
}
public void showBackground(CMessage msg)
{
Context ctxt = SCNApp.getContext();
String channel = CHANNEL_ID_NORM;
NotificationSettings ns = SCNSettings.inst().PriorityNorm;
switch (msg.Priority)
{
case LOW: ns = SCNSettings.inst().PriorityLow; channel = CHANNEL_ID_LOW; break;
case NORMAL: ns = SCNSettings.inst().PriorityNorm; channel = CHANNEL_ID_NORM; break;
case HIGH: ns = SCNSettings.inst().PriorityHigh; channel = CHANNEL_ID_HIGH; break;
}
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(ctxt, channel);
mBuilder.setSmallIcon(R.drawable.ic_bfb);
mBuilder.setContentTitle(msg.Title);
mBuilder.setContentText(msg.Content);
mBuilder.setShowWhen(true);
mBuilder.setWhen(msg.Timestamp);
mBuilder.setAutoCancel(true);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
{
if (msg.Priority == PriorityEnum.LOW) mBuilder.setPriority(NotificationCompat.PRIORITY_LOW);
if (msg.Priority == PriorityEnum.NORMAL) mBuilder.setPriority(NotificationCompat.PRIORITY_DEFAULT);
if (msg.Priority == PriorityEnum.HIGH) mBuilder.setPriority(NotificationCompat.PRIORITY_HIGH);
if (ns.EnableVibration) mBuilder.setVibrate(new long[]{200});
if (ns.EnableLED) mBuilder.setLights(ns.LEDColor, 500, 500);
}
if (ns.EnableSound && !ns.SoundSource.isEmpty())
{
mBuilder.setSound(Uri.parse(ns.SoundSource), AudioManager.STREAM_ALARM);
}
Intent intent = new Intent(ctxt, MainActivity.class);
PendingIntent pi = PendingIntent.getActivity(ctxt, 0, intent, 0);
mBuilder.setContentIntent(pi);
NotificationManager mNotificationManager = (NotificationManager) ctxt.getSystemService(Context.NOTIFICATION_SERVICE);
Notification n = mBuilder.build();
if (ns.EnableSound && !ns.SoundSource.isEmpty() && ns.RepeatSound) n.flags |= Notification.FLAG_INSISTENT;
if (mNotificationManager != null) mNotificationManager.notify(0, n);
}
}

View File

@@ -0,0 +1,116 @@
package com.blackforestbytes.simplecloudnotifier.view;
import android.annotation.SuppressLint;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.TextView;
import com.blackforestbytes.simplecloudnotifier.R;
import com.blackforestbytes.simplecloudnotifier.SCNApp;
import com.blackforestbytes.simplecloudnotifier.model.CMessageList;
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
import net.glxn.qrgen.android.QRCode;
import net.glxn.qrgen.core.image.ImageType;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import static android.content.Context.CLIPBOARD_SERVICE;
public class AccountFragment extends Fragment
{
public AccountFragment()
{
// Required empty public constructor
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View v = inflater.inflate(R.layout.fragment_account, container, false);
updateUI(v);
v.findViewById(R.id.btnCopyUserID).setOnClickListener(cv ->
{
ClipboardManager clipboard = (ClipboardManager) cv.getContext().getSystemService(CLIPBOARD_SERVICE);
clipboard.setPrimaryClip(ClipData.newPlainText("UserID", String.valueOf(SCNSettings.inst().user_id)));
SCNApp.showToast("Copied userID to clipboard", 1000);
});
v.findViewById(R.id.btnCopyUserKey).setOnClickListener(cv ->
{
ClipboardManager clipboard = (ClipboardManager) cv.getContext().getSystemService(CLIPBOARD_SERVICE);
clipboard.setPrimaryClip(ClipData.newPlainText("UserKey", String.valueOf(SCNSettings.inst().user_key)));
SCNApp.showToast("Copied key to clipboard", 1000);
});
v.findViewById(R.id.btnAccountReset).setOnClickListener(cv ->
{
View lpnl = v.findViewById(R.id.loadingPanel);
lpnl.setVisibility(View.VISIBLE);
SCNSettings.inst().reset(lpnl);
});
v.findViewById(R.id.btnClearLocalStorage).setOnClickListener(cv ->
{
CMessageList.inst().clear();
SCNApp.showToast("Notifications cleared", 1000);
});
v.findViewById(R.id.btnQR).setOnClickListener(cv ->
{
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(SCNSettings.inst().createOnlineURL()));
startActivity(browserIntent);
});
v.findViewById(R.id.btnRefresh).setOnClickListener(cv ->
{
View lpnl = v.findViewById(R.id.loadingPanel);
lpnl.setVisibility(View.VISIBLE);
SCNSettings.inst().refresh(lpnl, getActivity());
});
return v;
}
public void updateUI()
{
updateUI(getView());
}
@SuppressLint("DefaultLocale")
public void updateUI(View v)
{
if (v == null) return;
TextView tvUserID = v.findViewById(R.id.tvUserID);
TextView tvUserKey = v.findViewById(R.id.tvUserKey);
TextView tvQuota = v.findViewById(R.id.tvQuota);
ImageButton btnQR = v.findViewById(R.id.btnQR);
SCNSettings s = SCNSettings.inst();
if (s.isConnected())
{
tvUserID.setText(String.valueOf(s.user_id));
tvUserKey.setText(s.user_key);
tvQuota.setText(String.format("%d / %d", s.quota_curr, s.quota_max));
btnQR.setImageBitmap(QRCode.from(s.createOnlineURL()).to(ImageType.PNG).withSize(512, 512).bitmap());
}
else
{
tvUserID.setText(R.string.str_not_connected);
tvUserKey.setText(R.string.str_not_connected);
tvQuota.setText(R.string.str_not_connected);
btnQR.setImageResource(R.drawable.qr_default);
}
}
}

View File

@@ -0,0 +1,64 @@
package com.blackforestbytes.simplecloudnotifier.view;
import android.os.Bundle;
import com.blackforestbytes.simplecloudnotifier.R;
import com.blackforestbytes.simplecloudnotifier.SCNApp;
import com.blackforestbytes.simplecloudnotifier.model.CMessageList;
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
import com.blackforestbytes.simplecloudnotifier.service.IABService;
import com.blackforestbytes.simplecloudnotifier.service.NotificationService;
import com.google.android.material.tabs.TabLayout;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager;
public class MainActivity extends AppCompatActivity
{
public TabAdapter adpTabs;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
NotificationService.inst();
CMessageList.inst();
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.setTabGravity(TabLayout.GRAVITY_FILL);
tabLayout.setupWithViewPager(viewPager);
SCNApp.register(this);
IABService.startup(this);
SCNSettings.inst().work(this);
}
@Override
protected void onStop()
{
super.onStop();
SCNSettings.inst().save();
CMessageList.inst().fullSave();
}
@Override
protected void onDestroy()
{
super.onDestroy();
CMessageList.inst().fullSave();
IABService.inst().destroy();
}
}

View File

@@ -0,0 +1,111 @@
package com.blackforestbytes.simplecloudnotifier.view;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.blackforestbytes.simplecloudnotifier.R;
import com.blackforestbytes.simplecloudnotifier.model.CMessage;
import com.blackforestbytes.simplecloudnotifier.model.CMessageList;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
public class MessageAdapter extends RecyclerView.Adapter
{
private final View vNoElements;
public MessageAdapter(View noElementsView)
{
vNoElements = noElementsView;
CMessageList.inst().register(this);
vNoElements.setVisibility(getItemCount()>0 ? View.GONE : View.VISIBLE);
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType)
{
View myView = LayoutInflater.from(parent.getContext()).inflate(R.layout.message_card, parent, false);
return new MessagePresenter(myView);
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position)
{
CMessage msg = CMessageList.inst().tryGet(position);
MessagePresenter view = (MessagePresenter) holder;
view.setMessage(msg);
}
@Override
public int getItemCount()
{
return CMessageList.inst().size();
}
public void customNotifyItemInserted(int idx)
{
notifyItemInserted(idx);
vNoElements.setVisibility(getItemCount()>0 ? View.GONE : View.VISIBLE);
}
public void customNotifyDataSetChanged()
{
notifyDataSetChanged();
vNoElements.setVisibility(getItemCount()>0 ? View.GONE : View.VISIBLE);
}
private class MessagePresenter extends RecyclerView.ViewHolder implements View.OnClickListener
{
private TextView tvTimestamp;
private TextView tvTitle;
private TextView tvMessage;
private ImageView ivPriority;
private CMessage data;
MessagePresenter(View itemView)
{
super(itemView);
tvTimestamp = itemView.findViewById(R.id.tvTimestamp);
tvTitle = itemView.findViewById(R.id.tvTitle);
tvMessage = itemView.findViewById(R.id.tvMessage);
ivPriority = itemView.findViewById(R.id.ivPriority);
itemView.setOnClickListener(this);
}
void setMessage(CMessage msg)
{
tvTimestamp.setText(msg.formatTimestamp());
tvTitle.setText(msg.Title);
tvMessage.setText(msg.Content);
switch (msg.Priority)
{
case LOW:
ivPriority.setVisibility(View.VISIBLE);
ivPriority.setImageResource(R.drawable.priority_low);
break;
case NORMAL:
ivPriority.setVisibility(View.GONE);
break;
case HIGH:
ivPriority.setVisibility(View.VISIBLE);
ivPriority.setImageResource(R.drawable.priority_high);
break;
}
data = msg;
}
@Override
public void onClick(View v)
{
//SCNApp.showToast(data.Title, Toast.LENGTH_LONG);
}
}
}

View File

@@ -0,0 +1,50 @@
package com.blackforestbytes.simplecloudnotifier.view;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.blackforestbytes.simplecloudnotifier.R;
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
import com.blackforestbytes.simplecloudnotifier.service.IABService;
import com.google.android.gms.ads.doubleclick.PublisherAdRequest;
import com.google.android.gms.ads.doubleclick.PublisherAdView;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
public class NotificationsFragment extends Fragment
{
private PublisherAdView adView;
public NotificationsFragment()
{
// Required empty public constructor
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View v = inflater.inflate(R.layout.fragment_notifications, container, false);
RecyclerView rvMessages = v.findViewById(R.id.rvMessages);
rvMessages.setLayoutManager(new LinearLayoutManager(this.getContext(), RecyclerView.VERTICAL, true));
rvMessages.setAdapter(new MessageAdapter(v.findViewById(R.id.tvNoElements)));
adView = v.findViewById(R.id.adBanner);
PublisherAdRequest adRequest = new PublisherAdRequest.Builder().build();
adView.loadAd(adRequest);
adView.setVisibility(SCNSettings.inst().promode_local ? View.GONE : View.VISIBLE);
return v;
}
public void updateProState()
{
adView.setVisibility(IABService.inst().getPurchaseCached(IABService.IAB_PRO_MODE) != null ? View.GONE : View.VISIBLE);
}
}

View File

@@ -0,0 +1,359 @@
package com.blackforestbytes.simplecloudnotifier.view;
import android.content.Context;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Spinner;
import android.widget.Switch;
import android.widget.TextView;
import com.android.billingclient.api.Purchase;
import com.blackforestbytes.simplecloudnotifier.R;
import com.blackforestbytes.simplecloudnotifier.model.SCNSettings;
import com.blackforestbytes.simplecloudnotifier.service.IABService;
import com.blackforestbytes.simplecloudnotifier.service.NotificationService;
import org.jetbrains.annotations.NotNull;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import top.defaults.colorpicker.ColorPickerPopup;
import xyz.aprildown.ultimatemusicpicker.MusicPickerListener;
import xyz.aprildown.ultimatemusicpicker.UltimateMusicPicker;
public class SettingsFragment extends Fragment implements MusicPickerListener
{
private Switch prefAppEnabled;
private Spinner prefLocalCacheSize;
private Button prefUpgradeAccount;
private TextView prefUpgradeAccount_msg;
private TextView prefUpgradeAccount_info;
private Switch prefMsgLowEnableSound;
private TextView prefMsgLowRingtone_value;
private View prefMsgLowRingtone_container;
private Switch prefMsgLowRepeatSound;
private Switch prefMsgLowEnableLED;
private View prefMsgLowLedColor_container;
private ImageView prefMsgLowLedColor_value;
private Switch prefMsgLowEnableVibrations;
private Switch prefMsgNormEnableSound;
private TextView prefMsgNormRingtone_value;
private View prefMsgNormRingtone_container;
private Switch prefMsgNormRepeatSound;
private Switch prefMsgNormEnableLED;
private View prefMsgNormLedColor_container;
private ImageView prefMsgNormLedColor_value;
private Switch prefMsgNormEnableVibrations;
private Switch prefMsgHighEnableSound;
private TextView prefMsgHighRingtone_value;
private View prefMsgHighRingtone_container;
private Switch prefMsgHighRepeatSound;
private Switch prefMsgHighEnableLED;
private View prefMsgHighLedColor_container;
private ImageView prefMsgHighLedColor_value;
private Switch prefMsgHighEnableVibrations;
private int musicPickerSwitch = -1;
public SettingsFragment()
{
// Required empty public constructor
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View v = inflater.inflate(R.layout.fragment_settings, container, false);
initFields(v);
updateUI();
initListener();
return v;
}
private void initFields(View v)
{
prefAppEnabled = v.findViewById(R.id.prefAppEnabled);
prefLocalCacheSize = v.findViewById(R.id.prefLocalCacheSize);
prefUpgradeAccount = v.findViewById(R.id.prefUpgradeAccount);
prefUpgradeAccount_msg = v.findViewById(R.id.prefUpgradeAccount2);
prefUpgradeAccount_info = v.findViewById(R.id.prefUpgradeAccount_info);
prefMsgLowEnableSound = v.findViewById(R.id.prefMsgLowEnableSound);
prefMsgLowRingtone_value = v.findViewById(R.id.prefMsgLowRingtone_value);
prefMsgLowRingtone_container = v.findViewById(R.id.prefMsgLowRingtone_container);
prefMsgLowRepeatSound = v.findViewById(R.id.prefMsgLowRepeatSound);
prefMsgLowEnableLED = v.findViewById(R.id.prefMsgLowEnableLED);
prefMsgLowLedColor_value = v.findViewById(R.id.prefMsgLowLedColor_value);
prefMsgLowLedColor_container = v.findViewById(R.id.prefMsgLowLedColor_container);
prefMsgLowEnableVibrations = v.findViewById(R.id.prefMsgLowEnableVibrations);
prefMsgNormEnableSound = v.findViewById(R.id.prefMsgNormEnableSound);
prefMsgNormRingtone_value = v.findViewById(R.id.prefMsgNormRingtone_value);
prefMsgNormRingtone_container = v.findViewById(R.id.prefMsgNormRingtone_container);
prefMsgNormRepeatSound = v.findViewById(R.id.prefMsgNormRepeatSound);
prefMsgNormEnableLED = v.findViewById(R.id.prefMsgNormEnableLED);
prefMsgNormLedColor_value = v.findViewById(R.id.prefMsgNormLedColor_value);
prefMsgNormLedColor_container = v.findViewById(R.id.prefMsgNormLedColor_container);
prefMsgNormEnableVibrations = v.findViewById(R.id.prefMsgNormEnableVibrations);
prefMsgHighEnableSound = v.findViewById(R.id.prefMsgHighEnableSound);
prefMsgHighRingtone_value = v.findViewById(R.id.prefMsgHighRingtone_value);
prefMsgHighRingtone_container = v.findViewById(R.id.prefMsgHighRingtone_container);
prefMsgHighRepeatSound = v.findViewById(R.id.prefMsgHighRepeatSound);
prefMsgHighEnableLED = v.findViewById(R.id.prefMsgHighEnableLED);
prefMsgHighLedColor_value = v.findViewById(R.id.prefMsgHighLedColor_value);
prefMsgHighLedColor_container = v.findViewById(R.id.prefMsgHighLedColor_container);
prefMsgHighEnableVibrations = v.findViewById(R.id.prefMsgHighEnableVibrations);
}
private void updateUI()
{
SCNSettings s = SCNSettings.inst();
Context c = getContext();
if (c == null) return;
if (prefAppEnabled.isChecked() != s.Enabled) prefAppEnabled.setChecked(s.Enabled);
prefUpgradeAccount.setVisibility( SCNSettings.inst().promode_local ? View.GONE : View.VISIBLE);
prefUpgradeAccount_info.setVisibility(SCNSettings.inst().promode_local ? View.GONE : View.VISIBLE);
prefUpgradeAccount_msg.setVisibility( SCNSettings.inst().promode_local ? View.VISIBLE : View.GONE );
ArrayAdapter<Integer> plcsa = new ArrayAdapter<>(c, android.R.layout.simple_spinner_item, SCNSettings.CHOOSABLE_CACHE_SIZES);
plcsa.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
prefLocalCacheSize.setAdapter(plcsa);
prefLocalCacheSize.setSelection(getCacheSizeIndex(s.LocalCacheSize));
if (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 (prefMsgLowRepeatSound.isChecked() != s.PriorityLow.RepeatSound) prefMsgLowRepeatSound.setChecked(s.PriorityLow.RepeatSound);
if (prefMsgLowEnableLED.isChecked() != s.PriorityLow.EnableLED) prefMsgLowEnableLED.setChecked(s.PriorityLow.EnableLED);
prefMsgLowLedColor_value.setColorFilter(s.PriorityLow.LEDColor);
if (prefMsgLowEnableVibrations.isChecked() != s.PriorityLow.EnableVibration) prefMsgLowEnableVibrations.setChecked(s.PriorityLow.EnableVibration);
if (prefMsgNormEnableSound.isChecked() != s.PriorityNorm.EnableSound) prefMsgNormEnableSound.setChecked(s.PriorityNorm.EnableSound);
if (!prefMsgNormRingtone_value.getText().equals(s.PriorityNorm.SoundName)) prefMsgNormRingtone_value.setText(s.PriorityNorm.SoundName);
if (prefMsgNormRepeatSound.isChecked() != s.PriorityNorm.RepeatSound) prefMsgNormRepeatSound.setChecked(s.PriorityNorm.RepeatSound);
if (prefMsgNormEnableLED.isChecked() != s.PriorityNorm.EnableLED) prefMsgNormEnableLED.setChecked(s.PriorityNorm.EnableLED);
prefMsgNormLedColor_value.setColorFilter(s.PriorityNorm.LEDColor);
if (prefMsgNormEnableVibrations.isChecked() != s.PriorityNorm.EnableVibration) prefMsgNormEnableVibrations.setChecked(s.PriorityNorm.EnableVibration);
if (prefMsgHighEnableSound.isChecked() != s.PriorityHigh.EnableSound) prefMsgHighEnableSound.setChecked(s.PriorityHigh.EnableSound);
if (!prefMsgHighRingtone_value.getText().equals(s.PriorityHigh.SoundName)) prefMsgHighRingtone_value.setText(s.PriorityHigh.SoundName);
if (prefMsgHighRepeatSound.isChecked() != s.PriorityHigh.RepeatSound) prefMsgHighRepeatSound.setChecked(s.PriorityHigh.RepeatSound);
if (prefMsgHighEnableLED.isChecked() != s.PriorityHigh.EnableLED) prefMsgHighEnableLED.setChecked(s.PriorityHigh.EnableLED);
prefMsgHighLedColor_value.setColorFilter(s.PriorityHigh.LEDColor);
if (prefMsgHighEnableVibrations.isChecked() != s.PriorityHigh.EnableVibration) prefMsgHighEnableVibrations.setChecked(s.PriorityHigh.EnableVibration);
}
private void initListener()
{
SCNSettings s = SCNSettings.inst();
prefAppEnabled.setOnCheckedChangeListener((a,b) -> { s.Enabled=b; saveAndUpdate(); });
prefLocalCacheSize.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener()
{
@Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
s.LocalCacheSize = prefLocalCacheSize.getSelectedItemPosition()>=0 ? SCNSettings.CHOOSABLE_CACHE_SIZES[prefLocalCacheSize.getSelectedItemPosition()] : 100;
saveAndUpdate();
}
@Override public void onNothingSelected(AdapterView<?> parent) { /* */ }
});
prefUpgradeAccount.setOnClickListener(a -> onUpgradeAccount());
prefMsgLowEnableSound.setOnCheckedChangeListener((a,b) -> { s.PriorityLow.EnableSound=b; saveAndUpdate(); });
prefMsgLowRingtone_container.setOnClickListener(a -> chooseRingtoneLow());
prefMsgLowRepeatSound.setOnCheckedChangeListener((a,b) -> { s.PriorityLow.RepeatSound=b; saveAndUpdate(); });
prefMsgLowEnableLED.setOnCheckedChangeListener((a,b) -> { s.PriorityLow.EnableLED=b; saveAndUpdate(); });
prefMsgLowLedColor_container.setOnClickListener(a -> chooseLEDColorLow());
prefMsgLowEnableVibrations.setOnCheckedChangeListener((a,b) -> { s.PriorityLow.EnableVibration=b; saveAndUpdate(); });
prefMsgNormEnableSound.setOnCheckedChangeListener((a,b) -> { s.PriorityNorm.EnableSound=b; saveAndUpdate(); });
prefMsgNormRingtone_container.setOnClickListener(a -> chooseRingtoneNorm());
prefMsgNormRepeatSound.setOnCheckedChangeListener((a,b) -> { s.PriorityNorm.RepeatSound=b; saveAndUpdate(); });
prefMsgNormEnableLED.setOnCheckedChangeListener((a,b) -> { s.PriorityNorm.EnableLED=b; saveAndUpdate(); });
prefMsgNormLedColor_container.setOnClickListener(a -> chooseLEDColorNorm());
prefMsgNormEnableVibrations.setOnCheckedChangeListener((a,b) -> { s.PriorityNorm.EnableVibration=b; saveAndUpdate(); });
prefMsgHighEnableSound.setOnCheckedChangeListener((a,b) -> { s.PriorityHigh.EnableSound=b; saveAndUpdate(); });
prefMsgHighRingtone_container.setOnClickListener(a -> chooseRingtoneHigh());
prefMsgHighRepeatSound.setOnCheckedChangeListener((a,b) -> { s.PriorityHigh.RepeatSound=b; saveAndUpdate(); });
prefMsgHighEnableLED.setOnCheckedChangeListener((a,b) -> { s.PriorityHigh.EnableLED=b; saveAndUpdate(); });
prefMsgHighLedColor_container.setOnClickListener(a -> chooseLEDColorHigh());
prefMsgHighEnableVibrations.setOnCheckedChangeListener((a,b) -> { s.PriorityHigh.EnableVibration=b; saveAndUpdate(); });
}
private void saveAndUpdate()
{
SCNSettings.inst().save();
updateUI();
NotificationService.inst().updateChannels();
}
private void onUpgradeAccount()
{
IABService.inst().purchase(getActivity(), IABService.IAB_PRO_MODE);
}
public void updateProState()
{
Purchase p = IABService.inst().getPurchaseCached(IABService.IAB_PRO_MODE);
prefUpgradeAccount.setVisibility( p != null ? View.GONE : View.VISIBLE);
prefUpgradeAccount_info.setVisibility(p != null ? View.GONE : View.VISIBLE);
prefUpgradeAccount_msg.setVisibility( p != null ? View.VISIBLE : View.GONE );
}
private int getCacheSizeIndex(int value)
{
for (int i = 0; i < SCNSettings.CHOOSABLE_CACHE_SIZES.length; i++)
{
if (SCNSettings.CHOOSABLE_CACHE_SIZES[i] == value) return i;
}
return 2;
}
private void chooseRingtoneLow()
{
musicPickerSwitch = 1;
UltimateMusicPicker ump = new UltimateMusicPicker();
ump.windowTitle("Choose notification sound");
ump.removeSilent();
ump.streamType(AudioManager.STREAM_ALARM);
ump.ringtone();
ump.notification();
ump.alarm();
ump.music();
if (!SCNSettings.inst().PriorityLow.SoundSource.isEmpty())ump.selectUri(Uri.parse(SCNSettings.inst().PriorityLow.SoundSource));
ump.goWithDialog(getChildFragmentManager());
}
private void chooseRingtoneNorm()
{
musicPickerSwitch = 2;
UltimateMusicPicker ump = new UltimateMusicPicker();
ump.windowTitle("Choose notification sound");
ump.removeSilent();
ump.streamType(AudioManager.STREAM_ALARM);
ump.ringtone();
ump.notification();
ump.alarm();
ump.music();
if (!SCNSettings.inst().PriorityNorm.SoundSource.isEmpty())ump.defaultUri(Uri.parse(SCNSettings.inst().PriorityNorm.SoundSource));
ump.goWithDialog(getChildFragmentManager());
}
private void chooseRingtoneHigh()
{
musicPickerSwitch = 3;
UltimateMusicPicker ump = new UltimateMusicPicker();
ump.windowTitle("Choose notification sound");
ump.removeSilent();
ump.streamType(AudioManager.STREAM_ALARM);
ump.ringtone();
ump.notification();
ump.alarm();
ump.music();
if (!SCNSettings.inst().PriorityHigh.SoundSource.isEmpty())ump.defaultUri(Uri.parse(SCNSettings.inst().PriorityHigh.SoundSource));
ump.goWithDialog(getChildFragmentManager());
}
private void chooseLEDColorLow()
{
new ColorPickerPopup.Builder(getContext())
.initialColor(SCNSettings.inst().PriorityLow.LEDColor) // Set initial color
.enableBrightness(true) // Enable brightness slider or not
.okTitle("Choose")
.cancelTitle("Cancel")
.showIndicator(true)
.showValue(false)
.build()
.show(getView(), new ColorPickerPopup.ColorPickerObserver()
{
@Override
public void onColorPicked(int color) {
SCNSettings.inst().PriorityLow.LEDColor = color;
saveAndUpdate();
}
@Override
public void onColor(int color, boolean fromUser) { }
});
}
private void chooseLEDColorNorm()
{
new ColorPickerPopup.Builder(getContext())
.initialColor(SCNSettings.inst().PriorityNorm.LEDColor) // Set initial color
.enableBrightness(true) // Enable brightness slider or not
.okTitle("Choose")
.cancelTitle("Cancel")
.showIndicator(true)
.showValue(false)
.build()
.show(getView(), new ColorPickerPopup.ColorPickerObserver()
{
@Override
public void onColorPicked(int color) {
SCNSettings.inst().PriorityNorm.LEDColor = color;
saveAndUpdate();
}
@Override
public void onColor(int color, boolean fromUser) { }
});
}
private void chooseLEDColorHigh()
{
new ColorPickerPopup.Builder(getContext())
.initialColor(SCNSettings.inst().PriorityHigh.LEDColor) // Set initial color
.enableBrightness(true) // Enable brightness slider or not
.okTitle("Choose")
.cancelTitle("Cancel")
.showIndicator(true)
.showValue(false)
.build()
.show(getView(), new ColorPickerPopup.ColorPickerObserver()
{
@Override
public void onColorPicked(int color) {
SCNSettings.inst().PriorityHigh.LEDColor = color;
saveAndUpdate();
}
@Override
public void onColor(int color, boolean fromUser) { }
});
}
@Override
public void onMusicPick(@NotNull Uri uri, @NotNull String s)
{
if (musicPickerSwitch == 1) { SCNSettings.inst().PriorityLow.SoundSource =uri.toString(); SCNSettings.inst().PriorityLow.SoundName =s; saveAndUpdate(); }
if (musicPickerSwitch == 2) { SCNSettings.inst().PriorityNorm.SoundSource=uri.toString(); SCNSettings.inst().PriorityNorm.SoundName=s; saveAndUpdate(); }
if (musicPickerSwitch == 3) { SCNSettings.inst().PriorityHigh.SoundSource=uri.toString(); SCNSettings.inst().PriorityHigh.SoundName=s; saveAndUpdate(); }
musicPickerSwitch = -1;
}
@Override
public void onPickCanceled()
{
musicPickerSwitch = -1;
}
}

View File

@@ -0,0 +1,49 @@
package com.blackforestbytes.simplecloudnotifier.view;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentStatePagerAdapter;
public class TabAdapter extends FragmentStatePagerAdapter {
public NotificationsFragment tab1 = new NotificationsFragment();
public AccountFragment tab2 = new AccountFragment();
public SettingsFragment tab3 = new SettingsFragment();
public TabAdapter(FragmentManager fm)
{
super(fm);
}
@Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return tab1;
case 1:
return tab2;
case 2:
return tab3;
default:
return null;
}
}
@Override
public CharSequence getPageTitle(int position)
{
switch (position)
{
case 0: return "Notifications";
case 1: return "Account";
case 2: return "Settings";
default: return null;
}
}
@Override
public int getCount() {
return 3;
}
}

View File

@@ -0,0 +1,6 @@
<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"/>
</vector>

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

@@ -0,0 +1,6 @@
<vector android:height="24dp" android:viewportHeight="22"
android:viewportWidth="21" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#000000" android:fillType="evenOdd"
android:pathData="M14.5,0L2.5,0C1.4,0 0.5,0.9 0.5,2L0.5,16L2.5,16L2.5,2L14.5,2L14.5,0L14.5,0ZM17.5,4L6.5,4C5.4,4 4.5,4.9 4.5,6L4.5,20C4.5,21.1 5.4,22 6.5,22L17.5,22C18.6,22 19.5,21.1 19.5,20L19.5,6C19.5,4.9 18.6,4 17.5,4L17.5,4ZM17.5,20L6.5,20L6.5,6L17.5,6L17.5,20L17.5,20Z"
android:strokeColor="#00000000" android:strokeWidth="1"/>
</vector>

View File

@@ -0,0 +1,7 @@
<vector android:height="24dp" android:viewportHeight="100"
android:viewportWidth="100" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillAlpha="1" android:fillColor="#000000"
android:fillType="nonZero"
android:pathData="m22.917,12.5c-2.308,0 -4.167,1.858 -4.167,4.167l0,66.667c0,2.083 2.083,4.167 4.167,4.167l37.5,0c2.083,0 4.167,-2.083 4.167,-4.167l0,-20.833 2.083,0c2.083,0 2.083,2.083 2.083,2.083l0,8.333c0,4.167 2.083,6.25 6.25,6.25 4.167,0 6.25,-2.083 6.25,-6.25 0,-4.861 0,-18.75 0,-22.917 0,-4.167 -8.333,-8.333 -8.333,-12.5l0,-12.5 -4.167,0 -4.167,-4.167 0,-4.167c0,-2.308 -1.858,-4.167 -4.167,-4.167zM27.083,20.833 L56.25,20.833 56.25,37.5 27.083,37.5zM64.583,33.333 L68.75,33.333c0,0 0,3.472 0,6.25 0,4.167 8.333,8.333 8.333,12.5l0,20.833C77.083,75 75,75 75,75c0,0 -2.083,0 -2.083,-2.083 0,0 0,-8.333 0,-10.417 0,-2.083 -2.083,-4.167 -4.167,-4.167 -1.389,0 -4.167,0 -4.167,0z"
android:strokeColor="#00000000" android:strokeWidth="2"/>
</vector>

View File

@@ -0,0 +1,4 @@
<vector android:height="24dp" android:viewportHeight="48"
android:viewportWidth="48" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M25.3,20c-1.65,-4.66 -6.08,-8 -11.3,-8 -6.63,0 -12,5.37 -12,12s5.37,12 12,12c5.22,0 9.65,-3.34 11.3,-8h8.7v8h8v-8h4v-8h-20.7zM14,28c-2.21,0 -4,-1.79 -4,-4s1.79,-4 4,-4 4,1.79 4,4 -1.79,4 -4,4z"/>
</vector>

View File

@@ -0,0 +1,6 @@
<vector
android:width="24dp" android:height="24dp"
android:viewportWidth="16" android:viewportHeight="16"
xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#231F20" android:pathData="M14,8c-0.609,0-0.898,0.43-1,0.883C12.635,10.516,11.084,13,8,13c-0.757,0-1.473-0.172-2.114-0.474L6.414,12 C6.773,11.656,7,11.445,7,11c0-0.523-0.438-1-1-1H3c-0.609,0-1,0.492-1,1v3c0,0.541,0.428,1,1,1c0.484,0,0.688-0.273,1-0.594 l0.408-0.407C5.458,14.632,6.685,15,8,15c4.99,0,7-4.75,7-5.938C15,8.336,14.469,8,14,8z M3,7.117C3.365,5.485,4.916,3,8,3 c0.757,0,1.473,0.171,2.114,0.473L9.586,4C9.227,4.344,9,4.555,9,5c0,0.523,0.438,1,1,1h3c0.609,0,1-0.492,1-1V2 c0-0.541-0.428-1-1-1c-0.484,0-0.688,0.273-1,0.594l-0.408,0.407C10.542,1.368,9.315,1,8,1C3.01,1,1,5.75,1,6.938 C1,7.664,1.531,8,2,8C2.609,8,2.898,7.57,3,7.117z"/>
</vector>

View File

@@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M7,8a5,6 0,1 0,10 0a5,6 0,1 0,-10 0z"/>
<path
android:fillColor="#FF000000"
android:pathData="M21.8,19.1c-0.9,-1.8 -2.6,-3.3 -4.8,-4.2c-0.6,-0.2 -1.3,-0.2 -1.8,0.1c-1,0.6 -2,0.9 -3.2,0.9s-2.2,-0.3 -3.2,-0.9C8.3,14.8 7.6,14.7 7,15c-2.2,0.9 -3.9,2.4 -4.8,4.2C1.5,20.5 2.6,22 4.1,22h15.8C21.4,22 22.5,20.5 21.8,19.1z"/>
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M21.4,10.6l-8,-8c-0.8,-0.8 -2,-0.8 -2.8,0l-8,8c-0.8,0.8 -0.8,2 0,2.8l8,8c0.8,0.8 2,0.8 2.8,0l8,-8C22.2,12.6 22.2,11.4 21.4,10.6zM13,17h-2v-2h2V17zM13,13h-2V7h2V13z"/>
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M21.4,10.6l-8,-8c-0.8,-0.8 -2,-0.8 -2.8,0l-8,8c-0.8,0.8 -0.8,2 0,2.8l8,8c0.8,0.8 2,0.8 2.8,0l8,-8C22.2,12.6 22.2,11.4 21.4,10.6zM12,17l-3,-3h2V7h2v7h2L12,17z"/>
</vector>

View File

@@ -0,0 +1,422 @@
<vector
android:width="24dp" android:height="24dp"
android:viewportWidth="296" android:viewportHeight="296"
xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#000000" android:pathData="M32,32h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M32,40h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M32,48h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M32,56h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M32,64h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M32,72h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M32,80h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M32,96h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M32,104h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M32,128h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M32,136h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M32,152h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M32,208h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M32,216h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M32,224h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M32,232h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M32,240h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M32,248h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M32,256h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M40,32h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M40,80h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M40,96h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M40,104h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M40,112h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M40,120h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M40,136h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M40,160h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M40,184h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M40,192h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M40,208h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M40,256h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M48,32h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M48,48h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M48,56h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M48,64h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M48,80h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M48,120h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M48,136h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M48,160h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M48,176h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M48,208h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M48,224h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M48,232h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M48,240h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M48,256h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M56,32h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M56,48h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M56,56h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M56,64h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M56,80h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M56,104h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M56,120h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M56,136h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M56,152h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M56,176h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M56,192h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M56,208h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M56,224h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M56,232h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M56,240h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M56,256h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M64,32h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M64,48h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M64,56h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M64,64h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M64,80h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M64,104h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M64,152h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M64,168h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M64,184h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M64,208h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M64,224h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M64,232h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M64,240h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M64,256h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M72,32h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M72,80h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M72,96h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M72,104h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M72,112h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M72,120h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M72,128h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M72,136h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M72,144h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M72,160h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M72,176h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M72,184h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M72,192h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M72,208h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M72,256h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M80,32h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M80,40h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M80,48h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M80,56h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M80,64h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M80,72h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M80,80h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M80,96h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M80,112h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M80,128h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M80,144h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M80,160h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M80,176h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M80,192h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M80,208h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M80,216h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M80,224h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M80,232h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M80,240h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M80,248h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M80,256h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M88,96h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M88,128h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M88,144h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M88,152h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M88,176h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M88,184h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M88,192h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M96,56h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M96,64h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M96,80h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M96,104h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M96,136h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M96,152h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M96,160h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M96,184h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M96,200h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M96,208h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M96,216h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M96,248h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M96,256h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M104,40h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M104,64h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M104,96h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M104,104h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M104,120h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M104,128h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M104,136h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M104,160h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M104,184h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M104,232h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M104,248h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M112,40h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M112,80h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M112,88h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M112,112h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M112,160h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M112,168h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M112,176h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M112,184h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M112,192h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M112,200h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M112,208h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M112,216h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M112,224h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M112,256h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M120,32h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M120,72h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M120,88h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M120,96h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M120,136h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M120,152h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M120,160h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M120,168h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M120,176h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M120,184h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M120,200h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M120,216h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M120,232h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M120,248h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M128,32h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M128,48h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M128,56h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M128,72h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M128,80h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M128,96h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M128,104h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M128,128h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M128,160h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M128,184h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M128,192h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M128,208h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M128,224h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M128,232h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M128,248h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M136,32h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M136,56h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M136,72h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M136,96h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M136,144h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M136,160h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M136,168h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M136,176h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M136,192h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M136,256h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M144,32h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M144,40h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M144,48h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M144,64h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M144,80h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M144,88h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M144,96h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M144,112h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M144,152h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M144,160h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M144,168h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M144,176h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M144,184h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M144,192h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M144,208h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M144,240h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M144,256h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M152,88h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M152,104h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M152,112h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M152,120h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M152,128h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M152,136h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M152,144h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M152,152h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M152,168h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M152,176h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M152,184h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M152,192h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M152,200h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M152,208h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M152,224h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M152,232h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M152,240h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M160,64h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M160,72h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M160,80h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M160,88h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M160,120h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M160,128h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M160,136h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M160,144h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M160,152h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M160,168h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M160,208h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M160,224h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M160,232h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M168,56h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M168,72h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M168,96h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M168,104h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M168,160h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M168,192h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M168,200h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M168,208h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M168,240h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M168,256h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M176,32h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M176,40h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M176,64h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M176,80h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M176,88h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M176,104h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M176,112h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M176,136h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M176,176h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M176,184h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M176,192h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M176,200h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M176,216h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M176,232h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M176,240h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M176,248h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M184,64h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M184,88h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M184,96h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M184,104h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M184,120h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M184,136h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M184,168h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M184,176h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M184,200h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M184,208h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M184,216h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M184,224h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M184,232h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M184,240h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M184,256h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M192,48h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M192,64h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M192,72h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M192,80h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M192,88h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M192,120h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M192,128h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M192,136h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M192,168h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M192,184h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M192,192h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M192,200h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M192,208h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M192,216h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M192,224h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M192,232h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M192,240h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M200,152h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M200,176h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M200,184h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M200,192h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M200,224h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M200,232h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M200,240h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M200,248h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M200,256h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M208,32h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M208,40h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M208,48h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M208,56h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M208,64h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M208,72h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M208,80h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M208,112h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M208,128h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M208,136h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M208,152h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M208,168h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M208,176h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M208,184h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M208,192h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M208,208h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M208,224h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M208,240h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M216,32h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M216,80h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M216,104h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M216,136h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M216,144h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M216,152h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M216,168h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M216,192h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M216,224h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M216,240h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M216,248h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M224,32h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M224,48h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M224,56h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M224,64h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M224,80h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M224,96h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M224,104h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M224,112h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M224,120h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M224,136h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M224,144h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M224,168h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M224,176h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M224,192h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M224,200h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M224,208h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M224,216h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M224,224h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M224,240h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M224,256h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M232,32h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M232,48h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M232,56h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M232,64h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M232,80h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M232,96h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M232,120h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M232,144h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M232,160h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M232,176h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M232,200h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M232,208h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M232,224h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M232,232h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M232,240h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M232,248h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M232,256h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M240,32h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M240,48h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M240,56h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M240,64h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M240,80h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M240,104h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M240,144h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M240,152h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M240,160h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M240,168h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M240,192h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M240,208h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M240,232h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M240,240h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M240,248h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M240,256h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M248,32h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M248,80h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M248,104h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M248,136h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M248,168h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M248,192h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M248,224h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M248,232h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M248,240h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M256,32h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M256,40h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M256,48h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M256,56h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M256,64h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M256,72h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M256,80h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M256,128h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M256,136h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M256,152h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M256,168h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M256,176h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M256,192h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M256,224h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M256,232h8v8h-8z"/>
<path android:fillColor="#000000" android:pathData="M256,248h8v8h-8z"/>
</vector>

View File

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

View File

@@ -0,0 +1,239 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".view.AccountFragment">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/ic_img_user"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_margin="16dp"
android:layout_marginStart="16dp"
android:layout_marginTop="92dp"
android:contentDescription="@string/ic_img_user_desc"
android:src="@drawable/ic_user"
android:tint="#888"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/lblUserID"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:text="@string/str_userid"
app:layout_constraintEnd_toStartOf="@+id/btnRefresh"
app:layout_constraintStart_toEndOf="@+id/ic_img_user"
app:layout_constraintTop_toTopOf="@+id/ic_img_user" />
<HorizontalScrollView
android:id="@+id/svUserID"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="4dp"
app:layout_constraintEnd_toStartOf="@+id/btnCopyUserID"
app:layout_constraintStart_toEndOf="@+id/ic_img_user"
app:layout_constraintTop_toBottomOf="@+id/lblUserID">
<TextView
android:id="@+id/tvUserID"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
tools:layout_editor_absoluteX="8dp" />
</HorizontalScrollView>
<ImageButton
android:id="@+id/btnCopyUserID"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginEnd="8dp"
android:background="@null"
android:contentDescription="@string/str_copy"
android:padding="2dp"
android:scaleType="fitXY"
android:src="?android:attr/actionModeCopyDrawable"
app:layout_constraintBottom_toBottomOf="@+id/svUserID"
app:layout_constraintEnd_toStartOf="@+id/btnRefresh"
app:layout_constraintTop_toTopOf="@+id/svUserID" />
<ImageButton
android:id="@+id/btnRefresh"
android:layout_width="42dp"
android:layout_height="0dp"
android:layout_alignParentEnd="true"
android:layout_alignParentTop="true"
android:layout_marginEnd="16dp"
android:background="@null"
android:contentDescription="@string/str_reload"
android:padding="2dp"
android:src="@drawable/ic_refresh"
android:tint="#666"
app:layout_constraintBottom_toBottomOf="@+id/btnCopyUserID"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/lblUserID" />
<ImageView
android:id="@+id/ic_img_key"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_margin="16dp"
android:layout_marginStart="16dp"
android:layout_marginTop="92dp"
android:contentDescription="@string/ic_img_key_desc"
android:src="@drawable/ic_key"
android:tint="#888"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/ic_img_user" />
<TextView
android:id="@+id/lblUserKey"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:text="@string/str_userkey"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/ic_img_key"
app:layout_constraintTop_toTopOf="@+id/ic_img_key" />
<HorizontalScrollView
android:id="@+id/svUserKey"
android:scrollbars="none"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="4dp"
app:layout_constraintEnd_toStartOf="@+id/btnCopyUserKey"
app:layout_constraintStart_toEndOf="@+id/ic_img_key"
app:layout_constraintTop_toBottomOf="@+id/lblUserKey">
<TextView
android:id="@+id/tvUserKey"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
tools:layout_editor_absoluteX="8dp" />
</HorizontalScrollView>
<ImageButton
android:id="@+id/btnCopyUserKey"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginEnd="8dp"
android:background="@null"
android:contentDescription="@string/str_copy"
android:padding="2dp"
android:scaleType="fitXY"
android:src="?android:attr/actionModeCopyDrawable"
app:layout_constraintBottom_toBottomOf="@+id/svUserKey"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/svUserKey" />
<ImageView
android:id="@+id/ic_img_quota"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_margin="16dp"
android:layout_marginStart="16dp"
android:layout_marginTop="92dp"
android:contentDescription="@string/ic_img_fuel_desc"
android:src="@drawable/ic_fuel"
android:tint="#888"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/ic_img_key" />
<TextView
android:id="@+id/lblQuota"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:text="@string/str_quota"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/ic_img_quota"
app:layout_constraintTop_toTopOf="@+id/ic_img_quota" />
<HorizontalScrollView
android:id="@+id/svQuota"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="4dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/ic_img_quota"
app:layout_constraintTop_toBottomOf="@+id/lblQuota">
<TextView
android:id="@+id/tvQuota"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
tools:layout_editor_absoluteX="8dp" />
</HorizontalScrollView>
<ImageButton
android:id="@+id/btnQR"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="8dp"
android:background="@null"
android:contentDescription="@string/str_qr_code"
android:scaleType="fitCenter"
android:src="@drawable/qr_default"
app:layout_constraintBottom_toTopOf="@+id/btnAccountReset"
app:layout_constraintTop_toBottomOf="@+id/ic_img_quota" />
<Button
android:id="@+id/btnAccountReset"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/str_reset_account"
app:layout_constraintBottom_toTopOf="@+id/btnClearLocalStorage" />
<Button
android:id="@+id/btnClearLocalStorage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Clear Messages"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:layout_editor_absoluteY="352dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
<RelativeLayout
android:id="@+id/loadingPanel"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#DD000000"
android:clickable="true"
android:focusable="true"
android:gravity="center"
android:visibility="gone">
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminate="true"
android:indeterminateTint="#FFF"
tools:ignore="UnusedAttribute" />
</RelativeLayout>
</RelativeLayout>

View File

@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:ads="http://schemas.android.com/apk/res-auto"
tools:context=".view.NotificationsFragment">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:id="@+id/pnlMessages"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@+id/adBanner"
android:layout_width="match_parent"
android:layout_height="0dp">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvMessages"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false"
android:scrollbars="vertical" />
<TextView
android:id="@+id/tvNoElements"
android:textAlignment="center"
android:gravity="center"
android:text="@string/no_notifications"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
<com.google.android.gms.ads.doubleclick.PublisherAdView
android:id="@+id/adBanner"
app:layout_constraintTop_toBottomOf="@+id/pnlMessages"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
ads:adSize="SMART_BANNER"
ads:adUnitId="ca-app-pub-3320562328966175/5524654300" />
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>

View File

@@ -0,0 +1,731 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:ignore="TooManyViews"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".view.SettingsFragment">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="12dp">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:background="@color/colorHeader"
android:textColor="@color/colorHeaderForeground"
android:textSize="16sp"
android:padding="4dp"
android:text="@string/str_common_settings"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<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">
<Switch
android:id="@+id/prefAppEnabled"
android:text="@string/str_enabled"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</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"/>
<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/tvLocalCacheSize"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/str_localcachesize"
android:textColor="#000"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/prefLocalCacheSize"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Spinner
android:minWidth="64dp"
android:id="@+id/prefLocalCacheSize"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:spinnerMode="dialog"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</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"/>
<LinearLayout
android:orientation="vertical"
android:layout_marginStart="4dp"
android:layout_marginEnd="4dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="48dp">
<Button
android:id="@+id/prefUpgradeAccount"
android:text="@string/str_upgrade_account"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/prefUpgradeAccount_info"
android:textAlignment="center"
android:text="@string/str_promode_info"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/prefUpgradeAccount2"
android:textColor="#FF4D00"
android:textStyle="bold"
android:textAlignment="center"
android:text="@string/str_promode"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="12dp">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:background="@color/colorHeader"
android:textColor="@color/colorHeaderForeground"
android:textSize="16sp"
android:padding="4dp"
android:text="@string/str_header_prio0"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<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">
<Switch
android:id="@+id/prefMsgLowEnableSound"
android:text="@string/str_msg_enablesound"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</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"/>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginEnd="4dp"
android:minHeight="48dp">
<LinearLayout
android:id="@+id/prefMsgLowRingtone_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/tvMsgLowRingtone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/str_notificationsound"
android:textColor="#000" />
<TextView
android:id="@+id/prefMsgLowRingtone_value"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minWidth="64dp"
android:spinnerMode="dialog"
android:text="Whatever"
tools:ignore="HardcodedText" />
</LinearLayout>
</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"/>
<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">
<Switch
android:id="@+id/prefMsgLowRepeatSound"
android:text="@string/str_repeatnotificationsound"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</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"/>
<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">
<Switch
android:id="@+id/prefMsgLowEnableLED"
android:text="@string/str_enable_led"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</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"/>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/prefMsgLowLedColor_container"
android:layout_marginStart="4dp"
android:layout_marginEnd="4dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="48dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/str_ledcolor"
android:textColor="#000"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/prefMsgLowLedColor_value"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:src="@drawable/circle"
android:layout_marginEnd="4dp"
android:id="@+id/prefMsgLowLedColor_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:spinnerMode="dialog"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription" />
</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"/>
<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">
<Switch
android:id="@+id/prefMsgLowEnableVibrations"
android:text="@string/str_enable_vibration"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="12dp">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:background="@color/colorHeader"
android:textColor="@color/colorHeaderForeground"
android:textSize="16sp"
android:padding="4dp"
android:text="@string/str_header_prio1"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<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">
<Switch
android:id="@+id/prefMsgNormEnableSound"
android:text="@string/str_msg_enablesound"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</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"/>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginEnd="4dp"
android:minHeight="48dp">
<LinearLayout
android:id="@+id/prefMsgNormRingtone_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/tvMsgNormRingtone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/str_notificationsound"
android:textColor="#000" />
<TextView
android:id="@+id/prefMsgNormRingtone_value"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minWidth="64dp"
android:spinnerMode="dialog"
android:text="Whatever"
tools:ignore="HardcodedText" />
</LinearLayout>
</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"/>
<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">
<Switch
android:id="@+id/prefMsgNormRepeatSound"
android:text="@string/str_repeatnotificationsound"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</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"/>
<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">
<Switch
android:id="@+id/prefMsgNormEnableLED"
android:text="@string/str_enable_led"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</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"/>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/prefMsgNormLedColor_container"
android:layout_marginStart="4dp"
android:layout_marginEnd="4dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="48dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/str_ledcolor"
android:textColor="#000"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/prefMsgNormLedColor_value"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:src="@drawable/circle"
android:layout_marginEnd="4dp"
android:id="@+id/prefMsgNormLedColor_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:spinnerMode="dialog"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription" />
</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"/>
<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">
<Switch
android:id="@+id/prefMsgNormEnableVibrations"
android:text="@string/str_enable_vibration"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="12dp">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:background="@color/colorHeader"
android:textColor="@color/colorHeaderForeground"
android:textSize="16sp"
android:padding="4dp"
android:text="@string/str_header_prio2"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<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">
<Switch
android:id="@+id/prefMsgHighEnableSound"
android:text="@string/str_msg_enablesound"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</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"/>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginEnd="4dp"
android:minHeight="48dp">
<LinearLayout
android:id="@+id/prefMsgHighRingtone_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/tvMsgHighRingtone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/str_notificationsound"
android:textColor="#000" />
<TextView
android:id="@+id/prefMsgHighRingtone_value"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minWidth="64dp"
android:spinnerMode="dialog"
android:text="Whatever"
tools:ignore="HardcodedText" />
</LinearLayout>
</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"/>
<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">
<Switch
android:id="@+id/prefMsgHighRepeatSound"
android:text="@string/str_repeatnotificationsound"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</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"/>
<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">
<Switch
android:id="@+id/prefMsgHighEnableLED"
android:text="@string/str_enable_led"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</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"/>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/prefMsgHighLedColor_container"
android:layout_marginStart="4dp"
android:layout_marginEnd="4dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="48dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/str_ledcolor"
android:textColor="#000"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/prefMsgHighLedColor_value"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:src="@drawable/circle"
android:layout_marginEnd="4dp"
android:id="@+id/prefMsgHighLedColor_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:spinnerMode="dialog"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription" />
</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"/>
<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">
<Switch
android:id="@+id/prefMsgHighEnableVibrations"
android:text="@string/str_enable_vibration"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
</ScrollView>

View File

@@ -0,0 +1,81 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.cardview.widget.CardView
android:id="@+id/card_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="@dimen/card_margin"
android:elevation="3dp"
card_view:cardCornerRadius="@dimen/card_album_radius">
<androidx.constraintlayout.widget.ConstraintLayout
android:background="#FFFFFFFF"
android:layout_margin="3dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tvTimestamp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:textSize="12sp"
android:textStyle="italic"
android:text="2018-09-11 20:22:32" />
<TextView
android:id="@+id/tvTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/tvTimestamp"
android:layout_marginEnd="4sp"
android:textSize="16sp"
android:textColor="@color/colorBlack"
android:textStyle="bold"
android:ellipsize="none"
android:maxLines="6"
android:text="Message from me"/>
<TextView
android:id="@+id/tvMessage"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/tvTitle"
app:layout_constraintRight_toLeftOf="@+id/ivPriority"
app:layout_constraintLeft_toLeftOf="parent"
android:layout_margin="4sp"
android:ellipsize="none"
android:maxLines="32"
android:scrollHorizontally="false"
android:text="asdasd asdasd asdasd a" />
<ImageView
android:id="@+id/ivPriority"
android:tint="#BBB"
android:visibility="gone"
android:layout_width="24dp"
android:layout_height="24dp"
app:layout_constraintTop_toBottomOf="@+id/tvTimestamp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_margin="4sp"
android:paddingTop="3dp"
android:contentDescription="@string/desc_priority_icon" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>

View File

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -3,4 +3,8 @@
<color name="colorPrimary">#3F51B5</color> <color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color> <color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color> <color name="colorAccent">#FF4081</color>
<color name="colorHeader">#3F51B5</color>
<color name="colorHeaderForeground">#FFFFFF</color>
<color name="colorBlack">#000</color>
</resources> </resources>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="card_margin">5dp</dimen>
<dimen name="card_album_radius">0dp</dimen>
</resources>

View File

@@ -0,0 +1,33 @@
<resources>
<string name="app_name">Simple Cloud Notifier</string>
<string name="hello_blank_fragment">Hello blank fragment</string>
<string name="ic_img_user_desc">Icon User</string>
<string name="str_userid">User ID</string>
<string name="str_userkey">Auth Key</string>
<string name="str_quota">Quota</string>
<string name="str_copy">Copy to clipboard</string>
<string name="ic_img_key_desc">Icon Key</string>
<string name="ic_img_fuel_desc">Icon Fuel</string>
<string name="str_qr_code">QR Code</string>
<string name="str_reset_account">Reset Account</string>
<string name="no_notifications">No notifications</string>
<string name="str_not_connected">not connected</string>
<string name="str_reload">reload</string>
<string name="desc_priority_icon">Priority icon</string>
<string name="str_common_settings">Common Settings</string>
<string name="str_enabled">Enabled</string>
<string name="str_localcachesize">Remember the last x notifications locally</string>
<string name="str_header_prio0">Notifications (low priority)</string>
<string name="str_header_prio1">Notifications (normal priority)</string>
<string name="str_header_prio2">Notifications (high priority)</string>
<string name="str_msg_enablesound">Enable notification sound</string>
<string name="str_notificationsound">Notification sound</string>
<string name="str_repeatnotificationsound">Repeat notification sound</string>
<string name="str_enable_led">Enable notification light</string>
<string name="str_ledcolor">Notification light color</string>
<string name="str_enable_vibration">Enable notification vibration</string>
<string name="str_upgrade_account">Upgrade account</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>
</resources>

View File

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

View File

@@ -0,0 +1,3 @@
#Sun Nov 11 19:37:57 CET 2018
VERSION_NAME=0.0.5
VERSION_CODE=5

View File

@@ -7,11 +7,8 @@ buildscript {
jcenter() jcenter()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.1.4' classpath 'com.android.tools.build:gradle:3.2.1'
classpath 'com.google.gms:google-services:4.2.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
} }
} }
@@ -19,6 +16,11 @@ allprojects {
repositories { repositories {
google() google()
jcenter() jcenter()
maven { url "https://jitpack.io" }
maven { url "https://dl.bintray.com/gericop/maven" }
maven { url "https://maven.google.com" }
} }
} }

View File

@@ -11,3 +11,6 @@ org.gradle.jvmargs=-Xmx1536m
# This option should only be used with decoupled projects. More details, visit # This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true # org.gradle.parallel=true
android.useAndroidX=true
android.enableJetifier=true

View File

@@ -1,6 +1,6 @@
#Fri Sep 21 22:14:10 CEST 2018 #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-4.4-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip

View File

View File

View File

@@ -1,28 +0,0 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 27
defaultConfig {
applicationId "com.blackforestbytes.simplecloudnotifier"
minSdkVersion 15
targetSdkVersion 27
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}

View File

@@ -1,26 +0,0 @@
package com.blackforestbytes.simplecloudnotifier;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("com.blackforestbytes.simplecloudnotifier", appContext.getPackageName());
}
}

View File

@@ -1,21 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.blackforestbytes.simplecloudnotifier">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@@ -1,13 +0,0 @@
package com.blackforestbytes.simplecloudnotifier;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}

View File

@@ -1,18 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>

View File

@@ -1,3 +0,0 @@
<resources>
<string name="app_name">SimpleCloudNotifier</string>
</resources>

View File

@@ -1,11 +0,0 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
</resources>

View File

@@ -1,17 +0,0 @@
package com.blackforestbytes.simplecloudnotifier;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() {
assertEquals(4, 2 + 2);
}
}

BIN
data/function_graphic.pdn Normal file

Binary file not shown.

BIN
data/function_graphic.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

BIN
data/icon.pdn Normal file

Binary file not shown.

BIN
data/icon_512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

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