@ -0,0 +1,13 @@ |
|||||
|
*.iml |
||||
|
.gradle |
||||
|
/local.properties |
||||
|
/.idea/caches |
||||
|
/.idea/libraries |
||||
|
/.idea/modules.xml |
||||
|
/.idea/workspace.xml |
||||
|
/.idea/navEditor.xml |
||||
|
/.idea/assetWizardSettings.xml |
||||
|
.DS_Store |
||||
|
/build |
||||
|
/captures |
||||
|
.externalNativeBuild |
@ -0,0 +1 @@ |
|||||
|
/build |
@ -0,0 +1,58 @@ |
|||||
|
apply plugin: 'com.android.application' |
||||
|
apply plugin: 'androidx.navigation.safeargs' |
||||
|
apply plugin: 'com.jaredsburrows.license' |
||||
|
|
||||
|
android { |
||||
|
compileSdkVersion 28 |
||||
|
defaultConfig { |
||||
|
applicationId "com.skulixlabs.iotcontrol" |
||||
|
minSdkVersion 21 |
||||
|
targetSdkVersion 28 |
||||
|
versionCode 1 |
||||
|
versionName "1.0" |
||||
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" |
||||
|
} |
||||
|
applicationVariants.all { variant -> |
||||
|
variant.resValue "string", "appVersion", variant.versionName |
||||
|
} |
||||
|
buildTypes { |
||||
|
release { |
||||
|
minifyEnabled false |
||||
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/*licenseReport { |
||||
|
generateHtmlReport = false |
||||
|
generateJsonReport = true |
||||
|
copyHtmlReportToAssets = true |
||||
|
copyJsonReportToAssets = false |
||||
|
}*/ |
||||
|
|
||||
|
dependencies { |
||||
|
def nav_version = "2.1.0" |
||||
|
def jackson_ver = "2.9.9" |
||||
|
|
||||
|
implementation fileTree(dir: 'libs', include: ['*.jar']) |
||||
|
implementation 'androidx.appcompat:appcompat:1.0.0' |
||||
|
implementation 'androidx.constraintlayout:constraintlayout:1.1.3' |
||||
|
//implementation 'com.google.android.material:material:1.0.0' |
||||
|
implementation 'androidx.preference:preference:1.1.0-alpha05' |
||||
|
implementation 'androidx.legacy:legacy-support-v4:1.0.0' |
||||
|
testImplementation 'junit:junit:4.12' |
||||
|
androidTestImplementation 'androidx.test:runner:1.1.0' |
||||
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0' |
||||
|
//debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.0-beta-3' |
||||
|
implementation "com.google.android.material:material:1.1.0-alpha10" |
||||
|
implementation "androidx.navigation:navigation-fragment:$nav_version" |
||||
|
implementation "androidx.navigation:navigation-ui:$nav_version" |
||||
|
implementation "com.fasterxml.jackson.core:jackson-databind:$jackson_ver" |
||||
|
implementation "com.fasterxml.jackson.core:jackson-core:$jackson_ver" |
||||
|
implementation "com.fasterxml.jackson.core:jackson-annotations:$jackson_ver" |
||||
|
implementation 'commons-io:commons-io:2.6' |
||||
|
implementation 'com.google.android:flexbox:1.1.1' |
||||
|
|
||||
|
implementation 'br.com.simplepass:loading-button-android:2.1.5' |
||||
|
implementation 'com.github.StevenDXC:DxLoadingButton:2.2' |
||||
|
} |
@ -0,0 +1,333 @@ |
|||||
|
|
||||
|
- artifact: androidx.slidingpanelayout:slidingpanelayout:+ |
||||
|
name: Android Support Library Sliding Pane Layout |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: http://developer.android.com/tools/extras/support-library.html |
||||
|
- artifact: androidx.appcompat:appcompat:+ |
||||
|
name: Android AppCompat Library v7 |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: https://developer.android.com/jetpack/androidx |
||||
|
- artifact: androidx.appcompat:appcompat-resources:+ |
||||
|
name: Android Resources Library |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: https://developer.android.com/jetpack/androidx |
||||
|
- artifact: androidx.customview:customview:+ |
||||
|
name: Android Support Library Custom View |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: http://developer.android.com/tools/extras/support-library.html |
||||
|
- artifact: org.jetbrains.kotlin:kotlin-stdlib-common:+ |
||||
|
name: org.jetbrains.kotlin:kotlin-stdlib-common |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: https://kotlinlang.org/ |
||||
|
- artifact: androidx.swiperefreshlayout:swiperefreshlayout:+ |
||||
|
name: Android Support Library Custom View |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: http://developer.android.com/tools/extras/support-library.html |
||||
|
- artifact: androidx.navigation:navigation-fragment:+ |
||||
|
name: Android Navigation Fragment |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: https://developer.android.com/topic/libraries/architecture/index.html |
||||
|
- artifact: androidx.savedstate:savedstate:+ |
||||
|
name: Activity |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: https://developer.android.com/jetpack/androidx |
||||
|
- artifact: com.google.android.material:material:+ |
||||
|
name: Material Components for Android |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: http://developer.android.com/tools/extras/support-library.html |
||||
|
- artifact: androidx.fragment:fragment:+ |
||||
|
name: Android Support Library fragment |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: https://developer.android.com/jetpack/androidx |
||||
|
- artifact: androidx.lifecycle:lifecycle-livedata:+ |
||||
|
name: Android Lifecycle LiveData |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: https://developer.android.com/topic/libraries/architecture/index.html |
||||
|
- artifact: androidx.constraintlayout:constraintlayout:+ |
||||
|
name: Android ConstraintLayout |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: http://tools.android.com |
||||
|
- artifact: androidx.localbroadcastmanager:localbroadcastmanager:+ |
||||
|
name: Android Support Library Local Broadcast Manager |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: http://developer.android.com/tools/extras/support-library.html |
||||
|
- artifact: androidx.preference:preference:+ |
||||
|
name: AndroidX Preference |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: http://developer.android.com/tools/extras/support-library.html |
||||
|
- artifact: androidx.legacy:legacy-support-core-utils:+ |
||||
|
name: Android Support Library core utils |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: http://developer.android.com/tools/extras/support-library.html |
||||
|
- artifact: androidx.coordinatorlayout:coordinatorlayout:+ |
||||
|
name: Android Support Library Coordinator Layout |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: http://developer.android.com/tools/extras/support-library.html |
||||
|
- artifact: androidx.legacy:legacy-support-v4:+ |
||||
|
name: Android Support Library v4 |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: http://developer.android.com/tools/extras/support-library.html |
||||
|
- artifact: commons-io:commons-io:+ |
||||
|
name: Apache Commons IO |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: #LICENSE# |
||||
|
url: http://commons.apache.org/proper/commons-io/ |
||||
|
- artifact: androidx.navigation:navigation-common:+ |
||||
|
name: Android Navigation Common |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: https://developer.android.com/topic/libraries/architecture/index.html |
||||
|
- artifact: androidx.versionedparcelable:versionedparcelable:+ |
||||
|
name: VersionedParcelable |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: http://developer.android.com/tools/extras/support-library.html |
||||
|
- artifact: androidx.print:print:+ |
||||
|
name: Android Support Library Print |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: http://developer.android.com/tools/extras/support-library.html |
||||
|
- artifact: androidx.drawerlayout:drawerlayout:+ |
||||
|
name: Android Support Library Drawer Layout |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: http://developer.android.com/tools/extras/support-library.html |
||||
|
- artifact: androidx.navigation:navigation-runtime:+ |
||||
|
name: Android Navigation Runtime |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: https://developer.android.com/topic/libraries/architecture/index.html |
||||
|
- artifact: androidx.core:core:+ |
||||
|
name: Android Support Library compat |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: http://developer.android.com/tools/extras/support-library.html |
||||
|
- artifact: androidx.lifecycle:lifecycle-common:+ |
||||
|
name: Android Lifecycle-Common |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: https://developer.android.com/topic/libraries/architecture/index.html |
||||
|
- artifact: androidx.interpolator:interpolator:+ |
||||
|
name: Android Support Library Interpolators |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: http://developer.android.com/tools/extras/support-library.html |
||||
|
- artifact: org.jetbrains.kotlin:kotlin-stdlib:+ |
||||
|
name: org.jetbrains.kotlin:kotlin-stdlib |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: https://kotlinlang.org/ |
||||
|
- artifact: androidx.viewpager:viewpager:+ |
||||
|
name: Android Support Library View Pager |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: http://developer.android.com/tools/extras/support-library.html |
||||
|
- artifact: androidx.lifecycle:lifecycle-runtime:+ |
||||
|
name: Android Lifecycle Runtime |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: https://developer.android.com/topic/libraries/architecture/index.html |
||||
|
- artifact: androidx.arch.core:core-common:+ |
||||
|
name: Android Arch-Common |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: https://developer.android.com/topic/libraries/architecture/index.html |
||||
|
- artifact: androidx.lifecycle:lifecycle-viewmodel:+ |
||||
|
name: Android Lifecycle ViewModel |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: https://developer.android.com/topic/libraries/architecture/index.html |
||||
|
- artifact: androidx.vectordrawable:vectordrawable:+ |
||||
|
name: Android Support VectorDrawable |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: https://developer.android.com/jetpack/androidx |
||||
|
- artifact: androidx.lifecycle:lifecycle-livedata-core:+ |
||||
|
name: Android Lifecycle LiveData Core |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: https://developer.android.com/topic/libraries/architecture/index.html |
||||
|
- artifact: androidx.media:media:+ |
||||
|
name: Android Support Library media compat |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: http://developer.android.com/tools/extras/support-library.html |
||||
|
- artifact: br.com.simplepass:loading-button-android:+ |
||||
|
name: LoadingButtonAndroid |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: https://github.com/leandroBorgesFerreira/LoadingButtonAndroid |
||||
|
- artifact: androidx.documentfile:documentfile:+ |
||||
|
name: Android Support Library Document File |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: http://developer.android.com/tools/extras/support-library.html |
||||
|
- artifact: androidx.cursoradapter:cursoradapter:+ |
||||
|
name: Android Support Library Cursor Adapter |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: http://developer.android.com/tools/extras/support-library.html |
||||
|
- artifact: org.jetbrains:annotations:+ |
||||
|
name: IntelliJ IDEA Annotations |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: http://www.jetbrains.org |
||||
|
- artifact: androidx.arch.core:core-runtime:+ |
||||
|
name: Android Arch-Runtime |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: https://developer.android.com/topic/libraries/architecture/index.html |
||||
|
- artifact: androidx.recyclerview:recyclerview:+ |
||||
|
name: Android Support RecyclerView v7 |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: https://developer.android.com/jetpack/androidx |
||||
|
- artifact: com.fasterxml.jackson.core:jackson-core:+ |
||||
|
name: Jackson-core |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: #LICENSE# |
||||
|
url: https://github.com/FasterXML/jackson-core |
||||
|
- artifact: androidx.cardview:cardview:+ |
||||
|
name: Android Support CardView v7 |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: http://developer.android.com/tools/extras/support-library.html |
||||
|
- artifact: androidx.legacy:legacy-support-core-ui:+ |
||||
|
name: Android Support Library core UI |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: http://developer.android.com/tools/extras/support-library.html |
||||
|
- artifact: com.fasterxml.jackson.core:jackson-databind:+ |
||||
|
name: jackson-databind |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: #LICENSE# |
||||
|
url: http://github.com/FasterXML/jackson |
||||
|
- artifact: androidx.vectordrawable:vectordrawable-animated:+ |
||||
|
name: Android Support AnimatedVectorDrawable |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: https://developer.android.com/jetpack/androidx |
||||
|
- artifact: androidx.annotation:annotation:+ |
||||
|
name: Android Support Library Annotations |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: http://developer.android.com/tools/extras/support-library.html |
||||
|
- artifact: androidx.transition:transition:+ |
||||
|
name: Android Transition Support Library |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: http://developer.android.com/tools/extras/support-library.html |
||||
|
- artifact: androidx.navigation:navigation-ui:+ |
||||
|
name: Android Navigation UI |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: https://developer.android.com/topic/libraries/architecture/index.html |
||||
|
- artifact: com.fasterxml.jackson.core:jackson-annotations:+ |
||||
|
name: Jackson-annotations |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: #LICENSE# |
||||
|
url: http://github.com/FasterXML/jackson |
||||
|
- artifact: androidx.constraintlayout:constraintlayout-solver:+ |
||||
|
name: Android ConstraintLayout Solver |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: http://tools.android.com |
||||
|
- artifact: androidx.viewpager2:viewpager2:+ |
||||
|
name: AndroidX Widget ViewPager2 |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: https://developer.android.com/jetpack/androidx |
||||
|
- artifact: androidx.asynclayoutinflater:asynclayoutinflater:+ |
||||
|
name: Android Support Library Async Layout Inflater |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: http://developer.android.com/tools/extras/support-library.html |
||||
|
- artifact: com.github.StevenDXC:DxLoadingButton:+ |
||||
|
name: StevenDXC/DxLoadingButton |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: Apache License 2.0 |
||||
|
licenseUrl: https://api.github.com/licenses/apache-2.0 |
||||
|
url: https://github.com/StevenDXC/DxLoadingButton |
||||
|
- artifact: androidx.loader:loader:+ |
||||
|
name: Android Support Library loader |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: http://developer.android.com/tools/extras/support-library.html |
||||
|
- artifact: androidx.activity:activity:+ |
||||
|
name: Activity |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: https://developer.android.com/jetpack/androidx |
||||
|
- artifact: androidx.collection:collection:+ |
||||
|
name: Android Support Library collections |
||||
|
copyrightHolder: #COPYRIGHT_HOLDER# |
||||
|
license: The Apache Software License, Version 2.0 |
||||
|
licenseUrl: http://www.apache.org/licenses/LICENSE-2.0.txt |
||||
|
url: http://developer.android.com/tools/extras/support-library.html |
@ -0,0 +1,21 @@ |
|||||
|
# Add project specific ProGuard rules here. |
||||
|
# You can control the set of applied configuration files using the |
||||
|
# proguardFiles setting in build.gradle. |
||||
|
# |
||||
|
# For more details, see |
||||
|
# http://developer.android.com/guide/developing/tools/proguard.html |
||||
|
|
||||
|
# If your project uses WebView with JS, uncomment the following |
||||
|
# and specify the fully qualified class name to the JavaScript interface |
||||
|
# class: |
||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview { |
||||
|
# public *; |
||||
|
#} |
||||
|
|
||||
|
# Uncomment this to preserve the line number information for |
||||
|
# debugging stack traces. |
||||
|
#-keepattributes SourceFile,LineNumberTable |
||||
|
|
||||
|
# If you keep the line number information, uncomment this to |
||||
|
# hide the original source file name. |
||||
|
#-renamesourcefileattribute SourceFile |
@ -0,0 +1,26 @@ |
|||||
|
package com.skulixlabs.iotcontrol; |
||||
|
|
||||
|
import android.content.Context; |
||||
|
import androidx.test.InstrumentationRegistry; |
||||
|
import androidx.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.example.gatecontrol", appContext.getPackageName()); |
||||
|
} |
||||
|
} |
@ -0,0 +1,26 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" |
||||
|
package="com.skulixlabs.iotcontrol"> |
||||
|
|
||||
|
<uses-permission android:name="android.permission.INTERNET" /> |
||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> |
||||
|
|
||||
|
<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="com.skulixlabs.iotcontrol.MainActivity" |
||||
|
android:label="@string/app_name"> |
||||
|
<intent-filter> |
||||
|
<action android:name="android.intent.action.MAIN" /> |
||||
|
|
||||
|
<category android:name="android.intent.category.LAUNCHER" /> |
||||
|
</intent-filter> |
||||
|
</activity> |
||||
|
</application> |
||||
|
|
||||
|
</manifest> |
@ -0,0 +1,385 @@ |
|||||
|
<html> |
||||
|
<head> |
||||
|
<style>body { font-family: sans-serif } pre { background-color: #eeeeee; padding: 1em; white-space: pre-wrap; display: inline-block }</style> |
||||
|
<title>Open source licenses</title> |
||||
|
</head> |
||||
|
<body> |
||||
|
<h3>Notice for packages:</h3> |
||||
|
<ul> |
||||
|
<li> |
||||
|
<a href="#314129783">Activity</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">Activity</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">Android AppCompat Library v7</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">Android Arch-Common</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">Android Arch-Runtime</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">Android ConstraintLayout</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">Android ConstraintLayout Solver</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">Android Lifecycle LiveData</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">Android Lifecycle LiveData Core</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">Android Lifecycle Runtime</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">Android Lifecycle ViewModel</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">Android Lifecycle-Common</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">Android Navigation Common</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">Android Navigation Fragment</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">Android Navigation Runtime</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">Android Navigation UI</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">Android Resources Library</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">Android Support AnimatedVectorDrawable</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">Android Support CardView v7</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">Android Support Library Annotations</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">Android Support Library Async Layout Inflater</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">Android Support Library collections</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">Android Support Library compat</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">Android Support Library Coordinator Layout</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">Android Support Library core UI</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">Android Support Library core utils</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">Android Support Library Cursor Adapter</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">Android Support Library Custom View</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">Android Support Library Custom View</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">Android Support Library Document File</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">Android Support Library Drawer Layout</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">Android Support Library fragment</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">Android Support Library Interpolators</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">Android Support Library loader</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">Android Support Library Local Broadcast Manager</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">Android Support Library media compat</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">Android Support Library Print</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">Android Support Library Sliding Pane Layout</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">Android Support Library v4</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">Android Support Library View Pager</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">Android Support RecyclerView v7</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">Android Support VectorDrawable</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">Android Transition Support Library</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">AndroidX Preference</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">AndroidX Widget ViewPager2</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">Apache Commons IO</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">flexbox-layout</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">IntelliJ IDEA Annotations</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">Jackson-annotations</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">Jackson-core</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">jackson-databind</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">LoadingButtonAndroid</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">Material Components for Android</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">org.jetbrains.kotlin:kotlin-stdlib</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">org.jetbrains.kotlin:kotlin-stdlib-common</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">StevenDXC/DxLoadingButton</a> |
||||
|
</li> |
||||
|
<li> |
||||
|
<a href="#314129783">VersionedParcelable</a> |
||||
|
</li> |
||||
|
<a name="314129783" /> |
||||
|
<pre> Apache License |
||||
|
Version 2.0, January 2004 |
||||
|
http://www.apache.org/licenses/ |
||||
|
|
||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION |
||||
|
|
||||
|
1. Definitions. |
||||
|
|
||||
|
"License" shall mean the terms and conditions for use, reproduction, |
||||
|
and distribution as defined by Sections 1 through 9 of this document. |
||||
|
|
||||
|
"Licensor" shall mean the copyright owner or entity authorized by |
||||
|
the copyright owner that is granting the License. |
||||
|
|
||||
|
"Legal Entity" shall mean the union of the acting entity and all |
||||
|
other entities that control, are controlled by, or are under common |
||||
|
control with that entity. For the purposes of this definition, |
||||
|
"control" means (i) the power, direct or indirect, to cause the |
||||
|
direction or management of such entity, whether by contract or |
||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the |
||||
|
outstanding shares, or (iii) beneficial ownership of such entity. |
||||
|
|
||||
|
"You" (or "Your") shall mean an individual or Legal Entity |
||||
|
exercising permissions granted by this License. |
||||
|
|
||||
|
"Source" form shall mean the preferred form for making modifications, |
||||
|
including but not limited to software source code, documentation |
||||
|
source, and configuration files. |
||||
|
|
||||
|
"Object" form shall mean any form resulting from mechanical |
||||
|
transformation or translation of a Source form, including but |
||||
|
not limited to compiled object code, generated documentation, |
||||
|
and conversions to other media types. |
||||
|
|
||||
|
"Work" shall mean the work of authorship, whether in Source or |
||||
|
Object form, made available under the License, as indicated by a |
||||
|
copyright notice that is included in or attached to the work |
||||
|
(an example is provided in the Appendix below). |
||||
|
|
||||
|
"Derivative Works" shall mean any work, whether in Source or Object |
||||
|
form, that is based on (or derived from) the Work and for which the |
||||
|
editorial revisions, annotations, elaborations, or other modifications |
||||
|
represent, as a whole, an original work of authorship. For the purposes |
||||
|
of this License, Derivative Works shall not include works that remain |
||||
|
separable from, or merely link (or bind by name) to the interfaces of, |
||||
|
the Work and Derivative Works thereof. |
||||
|
|
||||
|
"Contribution" shall mean any work of authorship, including |
||||
|
the original version of the Work and any modifications or additions |
||||
|
to that Work or Derivative Works thereof, that is intentionally |
||||
|
submitted to Licensor for inclusion in the Work by the copyright owner |
||||
|
or by an individual or Legal Entity authorized to submit on behalf of |
||||
|
the copyright owner. For the purposes of this definition, "submitted" |
||||
|
means any form of electronic, verbal, or written communication sent |
||||
|
to the Licensor or its representatives, including but not limited to |
||||
|
communication on electronic mailing lists, source code control systems, |
||||
|
and issue tracking systems that are managed by, or on behalf of, the |
||||
|
Licensor for the purpose of discussing and improving the Work, but |
||||
|
excluding communication that is conspicuously marked or otherwise |
||||
|
designated in writing by the copyright owner as "Not a Contribution." |
||||
|
|
||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity |
||||
|
on behalf of whom a Contribution has been received by Licensor and |
||||
|
subsequently incorporated within the Work. |
||||
|
|
||||
|
2. Grant of Copyright License. Subject to the terms and conditions of |
||||
|
this License, each Contributor hereby grants to You a perpetual, |
||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
||||
|
copyright license to reproduce, prepare Derivative Works of, |
||||
|
publicly display, publicly perform, sublicense, and distribute the |
||||
|
Work and such Derivative Works in Source or Object form. |
||||
|
|
||||
|
3. Grant of Patent License. Subject to the terms and conditions of |
||||
|
this License, each Contributor hereby grants to You a perpetual, |
||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
||||
|
(except as stated in this section) patent license to make, have made, |
||||
|
use, offer to sell, sell, import, and otherwise transfer the Work, |
||||
|
where such license applies only to those patent claims licensable |
||||
|
by such Contributor that are necessarily infringed by their |
||||
|
Contribution(s) alone or by combination of their Contribution(s) |
||||
|
with the Work to which such Contribution(s) was submitted. If You |
||||
|
institute patent litigation against any entity (including a |
||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work |
||||
|
or a Contribution incorporated within the Work constitutes direct |
||||
|
or contributory patent infringement, then any patent licenses |
||||
|
granted to You under this License for that Work shall terminate |
||||
|
as of the date such litigation is filed. |
||||
|
|
||||
|
4. Redistribution. You may reproduce and distribute copies of the |
||||
|
Work or Derivative Works thereof in any medium, with or without |
||||
|
modifications, and in Source or Object form, provided that You |
||||
|
meet the following conditions: |
||||
|
|
||||
|
(a) You must give any other recipients of the Work or |
||||
|
Derivative Works a copy of this License; and |
||||
|
|
||||
|
(b) You must cause any modified files to carry prominent notices |
||||
|
stating that You changed the files; and |
||||
|
|
||||
|
(c) You must retain, in the Source form of any Derivative Works |
||||
|
that You distribute, all copyright, patent, trademark, and |
||||
|
attribution notices from the Source form of the Work, |
||||
|
excluding those notices that do not pertain to any part of |
||||
|
the Derivative Works; and |
||||
|
|
||||
|
(d) If the Work includes a "NOTICE" text file as part of its |
||||
|
distribution, then any Derivative Works that You distribute must |
||||
|
include a readable copy of the attribution notices contained |
||||
|
within such NOTICE file, excluding those notices that do not |
||||
|
pertain to any part of the Derivative Works, in at least one |
||||
|
of the following places: within a NOTICE text file distributed |
||||
|
as part of the Derivative Works; within the Source form or |
||||
|
documentation, if provided along with the Derivative Works; or, |
||||
|
within a display generated by the Derivative Works, if and |
||||
|
wherever such third-party notices normally appear. The contents |
||||
|
of the NOTICE file are for informational purposes only and |
||||
|
do not modify the License. You may add Your own attribution |
||||
|
notices within Derivative Works that You distribute, alongside |
||||
|
or as an addendum to the NOTICE text from the Work, provided |
||||
|
that such additional attribution notices cannot be construed |
||||
|
as modifying the License. |
||||
|
|
||||
|
You may add Your own copyright statement to Your modifications and |
||||
|
may provide additional or different license terms and conditions |
||||
|
for use, reproduction, or distribution of Your modifications, or |
||||
|
for any such Derivative Works as a whole, provided Your use, |
||||
|
reproduction, and distribution of the Work otherwise complies with |
||||
|
the conditions stated in this License. |
||||
|
|
||||
|
5. Submission of Contributions. Unless You explicitly state otherwise, |
||||
|
any Contribution intentionally submitted for inclusion in the Work |
||||
|
by You to the Licensor shall be under the terms and conditions of |
||||
|
this License, without any additional terms or conditions. |
||||
|
Notwithstanding the above, nothing herein shall supersede or modify |
||||
|
the terms of any separate license agreement you may have executed |
||||
|
with Licensor regarding such Contributions. |
||||
|
|
||||
|
6. Trademarks. This License does not grant permission to use the trade |
||||
|
names, trademarks, service marks, or product names of the Licensor, |
||||
|
except as required for reasonable and customary use in describing the |
||||
|
origin of the Work and reproducing the content of the NOTICE file. |
||||
|
|
||||
|
7. Disclaimer of Warranty. Unless required by applicable law or |
||||
|
agreed to in writing, Licensor provides the Work (and each |
||||
|
Contributor provides its Contributions) on an "AS IS" BASIS, |
||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
||||
|
implied, including, without limitation, any warranties or conditions |
||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A |
||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the |
||||
|
appropriateness of using or redistributing the Work and assume any |
||||
|
risks associated with Your exercise of permissions under this License. |
||||
|
|
||||
|
8. Limitation of Liability. In no event and under no legal theory, |
||||
|
whether in tort (including negligence), contract, or otherwise, |
||||
|
unless required by applicable law (such as deliberate and grossly |
||||
|
negligent acts) or agreed to in writing, shall any Contributor be |
||||
|
liable to You for damages, including any direct, indirect, special, |
||||
|
incidental, or consequential damages of any character arising as a |
||||
|
result of this License or out of the use or inability to use the |
||||
|
Work (including but not limited to damages for loss of goodwill, |
||||
|
work stoppage, computer failure or malfunction, or any and all |
||||
|
other commercial damages or losses), even if such Contributor |
||||
|
has been advised of the possibility of such damages. |
||||
|
|
||||
|
9. Accepting Warranty or Additional Liability. While redistributing |
||||
|
the Work or Derivative Works thereof, You may choose to offer, |
||||
|
and charge a fee for, acceptance of support, warranty, indemnity, |
||||
|
or other liability obligations and/or rights consistent with this |
||||
|
License. However, in accepting such obligations, You may act only |
||||
|
on Your own behalf and on Your sole responsibility, not on behalf |
||||
|
of any other Contributor, and only if You agree to indemnify, |
||||
|
defend, and hold each Contributor harmless for any liability |
||||
|
incurred by, or claims asserted against, such Contributor by reason |
||||
|
of your accepting any such warranty or additional liability. |
||||
|
|
||||
|
END OF TERMS AND CONDITIONS |
||||
|
|
||||
|
APPENDIX: How to apply the Apache License to your work. |
||||
|
|
||||
|
To apply the Apache License to your work, attach the following |
||||
|
boilerplate notice, with the fields enclosed by brackets "[]" |
||||
|
replaced with your own identifying information. (Don't include |
||||
|
the brackets!) The text should be enclosed in the appropriate |
||||
|
comment syntax for the file format. We also recommend that a |
||||
|
file or class name and description of purpose be included on the |
||||
|
same "printed page" as the copyright notice for easier |
||||
|
identification within third-party archives. |
||||
|
|
||||
|
Copyright [yyyy] [name of copyright owner] |
||||
|
|
||||
|
Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
you may not use this file except in compliance with the License. |
||||
|
You may obtain a copy of the License at |
||||
|
|
||||
|
http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
|
||||
|
Unless required by applicable law or agreed to in writing, software |
||||
|
distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
See the License for the specific language governing permissions and |
||||
|
limitations under the License. |
||||
|
</pre> |
||||
|
</ul> |
||||
|
</body> |
||||
|
</html> |
@ -0,0 +1,65 @@ |
|||||
|
package com.skulixlabs.iotcontrol; |
||||
|
|
||||
|
import android.app.Dialog; |
||||
|
import android.content.DialogInterface; |
||||
|
import android.os.Bundle; |
||||
|
|
||||
|
import androidx.annotation.NonNull; |
||||
|
import androidx.annotation.Nullable; |
||||
|
import androidx.appcompat.app.AlertDialog; |
||||
|
import androidx.fragment.app.DialogFragment; |
||||
|
import androidx.fragment.app.Fragment; |
||||
|
|
||||
|
import android.view.LayoutInflater; |
||||
|
import android.view.View; |
||||
|
import android.view.ViewGroup; |
||||
|
import android.webkit.WebView; |
||||
|
|
||||
|
|
||||
|
public class AboutFragment extends Fragment { |
||||
|
@Override |
||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, |
||||
|
Bundle savedInstanceState) { |
||||
|
// Inflate the layout for this fragment
|
||||
|
return inflater.inflate(R.layout.fragment_about, container, false); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { |
||||
|
super.onViewCreated(view, savedInstanceState); |
||||
|
|
||||
|
view.findViewById(R.id.btnLicenses).setOnClickListener(new View.OnClickListener() { |
||||
|
@Override |
||||
|
public void onClick(View view) { |
||||
|
new OpenSourceLicensesDialog().newInstance().show(getChildFragmentManager(), "dialog_licenses"); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
public static class OpenSourceLicensesDialog extends DialogFragment { |
||||
|
|
||||
|
public static OpenSourceLicensesDialog newInstance() { |
||||
|
OpenSourceLicensesDialog dialog = new OpenSourceLicensesDialog(); |
||||
|
Bundle args = new Bundle(); |
||||
|
dialog.setArguments(args); |
||||
|
return dialog; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { |
||||
|
WebView webView = new WebView(requireActivity()); |
||||
|
webView.loadUrl("file:///android_asset/open_source_licenses.html"); |
||||
|
|
||||
|
return new AlertDialog.Builder(requireActivity()) |
||||
|
.setTitle("Open Source Licenses") |
||||
|
.setView(webView) |
||||
|
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { |
||||
|
@Override |
||||
|
public void onClick(DialogInterface dialog, int i) { |
||||
|
dialog.dismiss(); |
||||
|
} |
||||
|
}) |
||||
|
.create(); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,231 @@ |
|||||
|
package com.skulixlabs.iotcontrol; |
||||
|
|
||||
|
import android.graphics.Color; |
||||
|
import android.os.AsyncTask; |
||||
|
import android.os.Handler; |
||||
|
import android.text.Spannable; |
||||
|
import android.text.SpannableString; |
||||
|
import android.text.style.ForegroundColorSpan; |
||||
|
import android.util.Log; |
||||
|
|
||||
|
import com.dx.dxloadingbutton.lib.LoadingButton; |
||||
|
import com.skulixlabs.iotcontrol.adapter.ItemAdapter; |
||||
|
import com.skulixlabs.iotcontrol.model.Cmd; |
||||
|
import com.skulixlabs.iotcontrol.model.CmdResponse; |
||||
|
import com.skulixlabs.iotcontrol.util.Utils; |
||||
|
|
||||
|
import java.io.ByteArrayOutputStream; |
||||
|
import java.lang.ref.WeakReference; |
||||
|
import java.nio.ByteBuffer; |
||||
|
import java.util.Arrays; |
||||
|
|
||||
|
import javax.crypto.Mac; |
||||
|
import javax.crypto.spec.SecretKeySpec; |
||||
|
|
||||
|
import br.com.simplepass.loadingbutton.customViews.CircularProgressButton; |
||||
|
|
||||
|
public class ConnectTaskFromAdapter extends AsyncTask<Void, Spannable, ConnectTaskFromAdapter.ConnectTaskResult> { |
||||
|
private final String TAG = ConnectTaskFromAdapter.class.getName(); |
||||
|
final ConnectTaskFromAdapter.ConnectTaskResult result = new ConnectTaskFromAdapter.ConnectTaskResult(); // If we were to save to a Byte var, then a final Byte array should be initiated
|
||||
|
|
||||
|
WeakReference<ItemAdapter.ItemViewHolder> holder; |
||||
|
WeakReference<MainFragment> fragment; |
||||
|
LoadingButton actionButton; |
||||
|
TcpClient mTcpClient = null; |
||||
|
Mac mac = null; |
||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); |
||||
|
ByteBuffer resHash; |
||||
|
ByteBuffer gotBytes; |
||||
|
int gotBytesNum = 0; |
||||
|
Cmd cmd; |
||||
|
|
||||
|
public ConnectTaskFromAdapter(ItemAdapter.ItemViewHolder holder, WeakReference<MainFragment> fragment, Cmd cmd, LoadingButton actionButton) { |
||||
|
this.holder = new WeakReference<>(holder); |
||||
|
this.fragment = fragment; |
||||
|
this.cmd = cmd; |
||||
|
this.actionButton = actionButton; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void onPreExecute() { |
||||
|
try { |
||||
|
mac = Mac.getInstance("HmacSHA256"); |
||||
|
mac.init(new SecretKeySpec(cmd.getOptions().getHmacKey().getBytes(), cmd.getOptions().getHmacKey())); |
||||
|
} catch (Exception e) { |
||||
|
//onMessage(e.getMessage());
|
||||
|
cancel(true); |
||||
|
result.exception = e; |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
gotBytes = ByteBuffer.allocate(cmd.getOptions().getRandDataLength()); |
||||
|
resHash = ByteBuffer.allocate(cmd.getOptions().getHashByteLength()); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected ConnectTaskFromAdapter.ConnectTaskResult doInBackground(final Void... voids) { |
||||
|
if (isCancelled()) |
||||
|
return null; |
||||
|
Log.d(TAG, "Sending command: " + cmd.getValue()); |
||||
|
mTcpClient = new TcpClient(cmd.getOptions().getUrl(), new TcpClient.OnMessageReceived() { |
||||
|
@Override |
||||
|
public void messageReceived(byte[] data) { |
||||
|
try { |
||||
|
gotBytesNum += data.length; |
||||
|
|
||||
|
if (gotBytesNum <= cmd.getOptions().getRandDataLength()) { |
||||
|
mac.update(data); |
||||
|
for (int i = 0; i < data.length; i++) { |
||||
|
gotBytes.put(gotBytesNum - data.length + i, data[i]); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (gotBytesNum == cmd.getOptions().getRandDataLength()) { |
||||
|
Log.d(TAG, "Sending action with value: " + cmd.getValue()); |
||||
|
mac.update((byte) cmd.getValue()); |
||||
|
|
||||
|
byte[] reqHash = mac.doFinal(outputStream.toByteArray()); |
||||
|
|
||||
|
System.out.println(Utils.bytesToHex(reqHash)); |
||||
|
mTcpClient.sendMessage(reqHash); |
||||
|
} |
||||
|
|
||||
|
if (gotBytesNum > cmd.getOptions().getRandDataLength()) { |
||||
|
if (gotBytesNum > (cmd.getOptions().getRandDataLength() + cmd.getOptions().getHashByteLength())) |
||||
|
Log.d("test", "error!"); |
||||
|
|
||||
|
for (int i = 0; i < data.length; i++) |
||||
|
resHash.put(gotBytesNum - cmd.getOptions().getRandDataLength() - data.length + i, data[i]); |
||||
|
|
||||
|
if (gotBytesNum == (cmd.getOptions().getRandDataLength() + cmd.getOptions().getHashByteLength())) { |
||||
|
Object[] objects = cmd.getResponseList().keySet().toArray(); |
||||
|
Byte[] bytes = Arrays.copyOf(objects, objects.length, Byte[].class); |
||||
|
|
||||
|
byte b = Utils.analyzeResponse(cmd.getOptions().getHmacKey(), bytes, gotBytes, resHash); //cmds[0].getResponseList()
|
||||
|
|
||||
|
CmdResponse cmdResponse = Utils.matchResponse(cmd, b); |
||||
|
if (cmdResponse != null) |
||||
|
publishProgress(updateState(cmdResponse.getDescription(), cmdResponse.getColor())); |
||||
|
else |
||||
|
publishProgress(updateState("Failed!", "#FF0000")); |
||||
|
|
||||
|
Log.d("test", "Response byte: " + b); |
||||
|
result.result = b; |
||||
|
|
||||
|
mTcpClient.stopClient(); |
||||
|
} |
||||
|
} |
||||
|
} catch (Exception e) { |
||||
|
publishProgress(updateState("Error: " + e.getMessage(), "#FF0000")); |
||||
|
Log.e(TAG, "An error occurred", e); |
||||
|
mTcpClient.stopClient(); |
||||
|
result.exception = e; |
||||
|
} |
||||
|
} |
||||
|
}, new TcpClient.OnClientError() { |
||||
|
@Override |
||||
|
public void onError(Exception e) { |
||||
|
|
||||
|
if (e instanceof java.net.ConnectException) |
||||
|
publishProgress(updateState("Error: Connection impossible", "#FF0000")); |
||||
|
else if (e instanceof java.net.SocketTimeoutException) |
||||
|
publishProgress(updateState("Error: Timeout", "#FF0000")); |
||||
|
else |
||||
|
publishProgress(updateState("Error: " + e.getMessage(), "#FF0000")); |
||||
|
mTcpClient.stopClient(); |
||||
|
result.exception = e; |
||||
|
|
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
mTcpClient.run(); |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void onProgressUpdate(Spannable... values) { |
||||
|
try { |
||||
|
switch (cmd.getType()) { |
||||
|
case STATE: |
||||
|
holder.get().state.setText(values[0]); |
||||
|
break; |
||||
|
case FEEDBACK: |
||||
|
|
||||
|
} |
||||
|
} catch (NullPointerException e) { // holder may have been destroyed (null)
|
||||
|
// Just stifle the exception, preventing sudden app termination
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void onPostExecute(ConnectTaskFromAdapter.ConnectTaskResult result) { |
||||
|
super.onPostExecute(result); |
||||
|
if (actionButton!= null) { |
||||
|
if (result.exception == null) { // A response should be returned if succeeded. Otherwise use custom object including an Exception field
|
||||
|
actionSucceededAnimation(actionButton); |
||||
|
} else { |
||||
|
actionFailedAnimation(actionButton); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/*private void onMessage(String message) { |
||||
|
Toast.makeText(fragment.get().getActivity(), message, Toast.LENGTH_SHORT).show(); |
||||
|
}*/ |
||||
|
|
||||
|
private void actionFailedAnimation(LoadingButton actionButton) { |
||||
|
/*Bitmap bitmap = Utils.drawableToBitmap(ContextCompat.getDrawable(holder.get().getContext(), R.drawable.ic_close_red_24dp)); |
||||
|
actionButton.doneLoadingAnimation(SettingsFragment.getPrimaryColor(holder.get().getContext()), bitmap); |
||||
|
revertAnimation(actionButton);*/ |
||||
|
actionButton.loadingFailed(); // Auto reset is enabled
|
||||
|
} |
||||
|
|
||||
|
private void actionSucceededAnimation(final LoadingButton actionButton) { |
||||
|
/*Bitmap bitmap = Utils.drawableToBitmap(ContextCompat.getDrawable(holder.get().getContext(), R.drawable.ic_check_green_48dp)); |
||||
|
actionButton.doneLoadingAnimation(SettingsFragment.getPrimaryColor(holder.get().getContext()), bitmap); |
||||
|
revertAnimation(actionButton);*/ |
||||
|
|
||||
|
actionButton.loadingSuccessful(); |
||||
|
new Handler().postDelayed(new Runnable() { |
||||
|
@Override |
||||
|
public void run() { |
||||
|
actionButton.reset(); |
||||
|
} |
||||
|
}, 750); |
||||
|
/*actionButton.setAnimationEndAction((Function1)(new Function1() { |
||||
|
public Object invoke(Object var1) { |
||||
|
this.invoke((AnimationType)var1); |
||||
|
return Unit.INSTANCE; |
||||
|
} |
||||
|
|
||||
|
public final void invoke(@NotNull AnimationType it) { |
||||
|
Intrinsics.checkParameterIsNotNull(it, "it"); |
||||
|
Toast.makeText(holder.get().getContext().getApplicationContext(), (CharSequence)("end:" + it), Toast.LENGTH_SHORT).show(); |
||||
|
} |
||||
|
}));*/ |
||||
|
} |
||||
|
|
||||
|
private void revertAnimation(final CircularProgressButton actionButton) { |
||||
|
new Handler().postDelayed(new Runnable() { |
||||
|
@Override |
||||
|
public void run() { |
||||
|
actionButton.startMorphRevertAnimation(); |
||||
|
} |
||||
|
}, 500); |
||||
|
} |
||||
|
|
||||
|
private Spannable updateState(String str, String color) { |
||||
|
Spannable spannable = new SpannableString(str); |
||||
|
spannable.setSpan( |
||||
|
new ForegroundColorSpan(Color.parseColor(color)), |
||||
|
0, str.length(), |
||||
|
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); |
||||
|
return spannable; |
||||
|
} |
||||
|
|
||||
|
public class ConnectTaskResult { |
||||
|
public byte result; |
||||
|
public Exception exception; |
||||
|
} |
||||
|
} |
||||
|
|
@ -0,0 +1,69 @@ |
|||||
|
package com.skulixlabs.iotcontrol; |
||||
|
|
||||
|
import android.os.Bundle; |
||||
|
|
||||
|
import androidx.appcompat.app.AppCompatActivity; |
||||
|
import androidx.appcompat.app.AppCompatDelegate; |
||||
|
import androidx.appcompat.widget.Toolbar; |
||||
|
import androidx.navigation.NavController; |
||||
|
import androidx.navigation.Navigation; |
||||
|
import androidx.navigation.ui.NavigationUI; |
||||
|
import androidx.preference.PreferenceManager; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* IMPORTANT NOTES |
||||
|
* |
||||
|
* The following applies only to custom toolbars. If we use NavigationUI's default |
||||
|
* toolbar then everything works by default. ConstraintLayout overlaps its children. |
||||
|
* That means that the Toolbar will be overlapped by the fragments' content (content |
||||
|
* of all the pages in the graph). To avoid this our top destination (home |
||||
|
* page in nav graph) must have a different type of layout (linear). The other |
||||
|
* solution is to add the margin_top=?attr/actionBarSize for the fragment of the |
||||
|
* home page, but that is not recommended. |
||||
|
* |
||||
|
*/ |
||||
|
|
||||
|
// TODO: dynamic cmd and response equality check
|
||||
|
public class MainActivity extends AppCompatActivity { |
||||
|
|
||||
|
@Override |
||||
|
protected void onCreate(Bundle savedInstanceState) { |
||||
|
theme(); // Set theme here, save as SharedPreference, recreate when changed
|
||||
|
super.onCreate(savedInstanceState); |
||||
|
setContentView(R.layout.activity_main); |
||||
|
|
||||
|
NavController navController = Navigation.findNavController(this, R.id.my_nav_host_fragment); |
||||
|
Toolbar toolbar = findViewById(R.id.toolbar); |
||||
|
setSupportActionBar(toolbar); |
||||
|
NavigationUI.setupWithNavController(toolbar, navController); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public boolean onSupportNavigateUp() { |
||||
|
Navigation.findNavController(this, R.id.my_nav_host_fragment).navigateUp(); |
||||
|
return super.onSupportNavigateUp(); |
||||
|
} |
||||
|
|
||||
|
public void theme() { |
||||
|
if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean("dark_theme", false)) |
||||
|
getDelegate().setLocalNightMode(AppCompatDelegate.MODE_NIGHT_YES); |
||||
|
else { |
||||
|
getDelegate().setLocalNightMode(AppCompatDelegate.MODE_NIGHT_NO); |
||||
|
} |
||||
|
|
||||
|
/* Color even in dark mode, move to above else block if applied only when dark mode is off*/ |
||||
|
String themeColor = PreferenceManager.getDefaultSharedPreferences(this).getString("theme_color", null); |
||||
|
if (themeColor == null) themeColor = "default"; |
||||
|
switch (themeColor) { |
||||
|
case "red": |
||||
|
setTheme(R.style.AppTheme_RED); |
||||
|
break; |
||||
|
case "orange": |
||||
|
setTheme(R.style.AppTheme_ORANGE); |
||||
|
break; |
||||
|
default: |
||||
|
setTheme(R.style.AppTheme); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,124 @@ |
|||||
|
package com.skulixlabs.iotcontrol; |
||||
|
|
||||
|
|
||||
|
import android.os.Bundle; |
||||
|
import android.util.Log; |
||||
|
import android.view.LayoutInflater; |
||||
|
import android.view.Menu; |
||||
|
import android.view.MenuInflater; |
||||
|
import android.view.MenuItem; |
||||
|
import android.view.View; |
||||
|
import android.view.ViewGroup; |
||||
|
|
||||
|
import androidx.annotation.NonNull; |
||||
|
import androidx.annotation.Nullable; |
||||
|
import androidx.fragment.app.Fragment; |
||||
|
import androidx.navigation.Navigation; |
||||
|
import androidx.recyclerview.widget.LinearLayoutManager; |
||||
|
import androidx.recyclerview.widget.RecyclerView; |
||||
|
|
||||
|
import com.skulixlabs.iotcontrol.adapter.ItemAdapter; |
||||
|
import com.skulixlabs.iotcontrol.util.jackson.JsonUtils; |
||||
|
import com.google.android.material.snackbar.Snackbar; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.List; |
||||
|
|
||||
|
import com.skulixlabs.iotcontrol.model.CmdGroup; |
||||
|
|
||||
|
/** |
||||
|
* A simple {@link Fragment} subclass. |
||||
|
*/ |
||||
|
public class MainFragment extends Fragment { |
||||
|
|
||||
|
private static final String TAG = MainFragment.class.getName(); |
||||
|
ItemAdapter adapter = null; |
||||
|
|
||||
|
public MainFragment() { |
||||
|
// Required empty public constructor
|
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, |
||||
|
Bundle savedInstanceState) { |
||||
|
// Inflate the layout for this fragment
|
||||
|
setHasOptionsMenu(true); |
||||
|
return inflater.inflate(R.layout.fragment_main, container, false); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onViewCreated(@NonNull final View view, @Nullable Bundle savedInstanceState) { |
||||
|
super.onViewCreated(view, savedInstanceState); |
||||
|
|
||||
|
RecyclerView recList = (RecyclerView) view.findViewById(R.id.cardList); |
||||
|
LinearLayoutManager llm = new LinearLayoutManager(getContext()); |
||||
|
llm.setOrientation(LinearLayoutManager.VERTICAL); |
||||
|
recList.setLayoutManager(llm); |
||||
|
|
||||
|
List<CmdGroup> groups = new ArrayList<>(); |
||||
|
try { |
||||
|
groups = new JsonUtils().getCommands(getActivity()); |
||||
|
} catch (Exception e) { |
||||
|
e.printStackTrace(); |
||||
|
Snackbar.make(view, getResources().getString(R.string.invalid_commands), Snackbar.LENGTH_LONG) |
||||
|
.setAction(getResources().getString(R.string.load), new View.OnClickListener() { |
||||
|
@Override |
||||
|
public void onClick(View view) { |
||||
|
NavGraphDirections.ActionOpenSettings actionOpenSettings = SettingsFragmentDirections.actionOpenSettings(); |
||||
|
actionOpenSettings.setImportCmds(true); // Using Safe-args
|
||||
|
Navigation.findNavController(getActivity(), R.id.my_nav_host_fragment).navigate(actionOpenSettings); |
||||
|
} |
||||
|
}).show(); |
||||
|
} |
||||
|
|
||||
|
adapter = new ItemAdapter(null, groups); //this,groups
|
||||
|
recList.setAdapter(adapter); |
||||
|
|
||||
|
Log.d(TAG, "oneviewcreated"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Runs when screen is off |
||||
|
*/ |
||||
|
@Override |
||||
|
public void onResume() { |
||||
|
Log.d(TAG, "onResume"); |
||||
|
super.onResume(); |
||||
|
adapter.validateStateListeners(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onPause() { |
||||
|
Log.d(TAG, "onPause"); |
||||
|
super.onPause(); |
||||
|
adapter.invalidateStateListeners(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onDestroyView() { |
||||
|
super.onDestroyView(); |
||||
|
adapter = null; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { |
||||
|
inflater.inflate(R.menu.menu_main, menu); |
||||
|
super.onCreateOptionsMenu(menu, inflater); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public boolean onOptionsItemSelected(MenuItem item) { |
||||
|
// Handle action bar item clicks here. The action bar will
|
||||
|
// automatically handle clicks on the Home/Up button, so long
|
||||
|
// as you specify a parent activity in AndroidManifest.xml.
|
||||
|
int id = item.getItemId(); |
||||
|
|
||||
|
//noinspection SimplifiableIfStatement
|
||||
|
if (id == R.id.action_settings) { |
||||
|
Navigation.findNavController(getActivity(), R.id.my_nav_host_fragment).navigate(R.id.action_open_settings); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
return super.onOptionsItemSelected(item); |
||||
|
} |
||||
|
} |
@ -0,0 +1,262 @@ |
|||||
|
package com.skulixlabs.iotcontrol; |
||||
|
|
||||
|
import android.Manifest; |
||||
|
import android.app.Activity; |
||||
|
import android.app.AlertDialog; |
||||
|
import android.app.Dialog; |
||||
|
import android.content.ClipData; |
||||
|
import android.content.ClipboardManager; |
||||
|
import android.content.Context; |
||||
|
import android.content.DialogInterface; |
||||
|
import android.content.Intent; |
||||
|
import android.content.pm.PackageManager; |
||||
|
import android.net.Uri; |
||||
|
import android.os.Bundle; |
||||
|
import android.util.TypedValue; |
||||
|
import android.view.LayoutInflater; |
||||
|
import android.view.View; |
||||
|
import android.widget.Toast; |
||||
|
|
||||
|
import androidx.annotation.ColorInt; |
||||
|
import androidx.annotation.NonNull; |
||||
|
import androidx.annotation.Nullable; |
||||
|
import androidx.core.content.ContextCompat; |
||||
|
import androidx.fragment.app.DialogFragment; |
||||
|
import androidx.fragment.app.Fragment; |
||||
|
import androidx.navigation.Navigation; |
||||
|
import androidx.preference.ListPreference; |
||||
|
import androidx.preference.Preference; |
||||
|
import androidx.preference.PreferenceFragmentCompat; |
||||
|
import androidx.preference.SwitchPreferenceCompat; |
||||
|
|
||||
|
import com.skulixlabs.iotcontrol.util.jackson.JsonUtils; |
||||
|
|
||||
|
import java.io.InputStream; |
||||
|
|
||||
|
import static android.content.ClipDescription.MIMETYPE_TEXT_PLAIN; |
||||
|
import static com.skulixlabs.iotcontrol.util.jackson.JsonUtils.REQUEST_READ_FILE; |
||||
|
import static com.skulixlabs.iotcontrol.util.jackson.JsonUtils.REQUEST_READ_STORAGE; |
||||
|
|
||||
|
/** Preference screens does not currently support Data Binding |
||||
|
* because it is not a xml layout file |
||||
|
*/ |
||||
|
public class SettingsFragment extends PreferenceFragmentCompat { |
||||
|
@ColorInt public static Integer primaryColor = null; |
||||
|
|
||||
|
public SettingsFragment() { |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { |
||||
|
setPreferencesFromResource(R.xml.root_preferences, rootKey); |
||||
|
|
||||
|
((Preference) findPreference("load_json_commands")).setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { |
||||
|
@Override |
||||
|
public boolean onPreferenceClick(Preference preference) { |
||||
|
showImportDialog(); |
||||
|
return true; |
||||
|
}; |
||||
|
}); |
||||
|
|
||||
|
((SwitchPreferenceCompat) findPreference("dark_theme")).setOnPreferenceChangeListener(updateThemeListener(themeAction())); |
||||
|
((ListPreference) findPreference("theme_color")).setOnPreferenceChangeListener(updateThemeListener(themeAction())); |
||||
|
|
||||
|
((Preference) findPreference("version")).setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { |
||||
|
@Override |
||||
|
public boolean onPreferenceClick(Preference preference) { |
||||
|
Navigation.findNavController(getActivity(), R.id.my_nav_host_fragment).navigate(R.id.action_settingsFragment_to_aboutFragment); |
||||
|
return true; |
||||
|
}; |
||||
|
}); |
||||
|
|
||||
|
shouldShowImportDialog(); |
||||
|
} |
||||
|
|
||||
|
private void shouldShowImportDialog() { |
||||
|
if (SettingsFragmentArgs.fromBundle(getArguments()).getImportCmds()) { |
||||
|
getArguments().putBoolean("importCmds", false); |
||||
|
showImportDialog(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void showImportDialog() { |
||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); |
||||
|
LayoutInflater inflater = requireActivity().getLayoutInflater(); |
||||
|
|
||||
|
// Inflate and set the layout for the dialog
|
||||
|
// Pass null as the parent view because its going in the dialog layout
|
||||
|
builder.setView(inflater.inflate(R.layout.dialog_import_method, null)); |
||||
|
final AlertDialog alertDialog = builder.create(); |
||||
|
alertDialog.show(); |
||||
|
|
||||
|
alertDialog.findViewById(R.id.btnFromClipboard).setOnClickListener(new View.OnClickListener() { |
||||
|
@Override |
||||
|
public void onClick(View view) { |
||||
|
ClipboardManager clipboard = (ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE); |
||||
|
String json = ""; |
||||
|
|
||||
|
// If the clipboard doesn't contain data, else start the process after validation
|
||||
|
if (!(clipboard.hasPrimaryClip()) || !(clipboard.getPrimaryClipDescription().hasMimeType(MIMETYPE_TEXT_PLAIN))) { |
||||
|
Toast.makeText(getActivity(), "Not valid!", Toast.LENGTH_SHORT).show(); |
||||
|
((Preference) findPreference("load_json_commands")).setIcon(R.drawable.ic_error_outline_red_48dp); |
||||
|
} else { |
||||
|
try { |
||||
|
ClipData.Item item = clipboard.getPrimaryClip().getItemAt(0); // Since the clipboard contains plain text.
|
||||
|
json = item.getText().toString(); // Gets the clipboard as text.
|
||||
|
new JsonUtils().saveCommands(getActivity(), json); |
||||
|
((Preference) findPreference("load_json_commands")).setIcon(R.drawable.ic_check_green_48dp); |
||||
|
alertDialog.cancel(); |
||||
|
} catch (Exception e) { |
||||
|
Toast.makeText(getActivity(), "Not valid!", Toast.LENGTH_SHORT).show(); |
||||
|
((Preference) findPreference("load_json_commands")).setIcon(R.drawable.ic_error_outline_red_48dp); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
alertDialog.findViewById(R.id.btnFromFile).setOnClickListener(new View.OnClickListener() { |
||||
|
@Override |
||||
|
public void onClick(View view) { |
||||
|
selectFile(); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
public Runnable themeAction() { |
||||
|
return new Runnable() { |
||||
|
@Override |
||||
|
public void run() { |
||||
|
primaryColor = null; |
||||
|
((MainActivity) getActivity()).theme(); |
||||
|
getActivity().recreate(); |
||||
|
} |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
public static synchronized int getPrimaryColor(Context context) { |
||||
|
if (primaryColor == null) { |
||||
|
TypedValue typedValue = new TypedValue(); |
||||
|
context.getTheme().resolveAttribute(R.attr.colorPrimary, typedValue, true); |
||||
|
primaryColor = typedValue.data; |
||||
|
} |
||||
|
return primaryColor; |
||||
|
} |
||||
|
|
||||
|
private String FRAGMENT_DIALOG = "dialog"; |
||||
|
|
||||
|
public void selectFile() { |
||||
|
if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.READ_EXTERNAL_STORAGE) |
||||
|
!= PackageManager.PERMISSION_GRANTED) { |
||||
|
requestReadStoragePermission(); |
||||
|
return; |
||||
|
} |
||||
|
openFileManager(); |
||||
|
} |
||||
|
|
||||
|
private void openFileManager() { |
||||
|
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); |
||||
|
intent.addCategory(Intent.CATEGORY_OPENABLE); |
||||
|
intent.setType("*/*"); |
||||
|
startActivityForResult(intent, REQUEST_READ_FILE); |
||||
|
} |
||||
|
|
||||
|
private void requestReadStoragePermission() { |
||||
|
if (shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE)) { |
||||
|
new ConfirmationDialog().show(getChildFragmentManager(), FRAGMENT_DIALOG); |
||||
|
} else { |
||||
|
requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_READ_STORAGE); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static class ConfirmationDialog extends DialogFragment { |
||||
|
@NonNull |
||||
|
@Override |
||||
|
public Dialog onCreateDialog(Bundle savedInstanceState) { |
||||
|
final Fragment parent = getParentFragment(); |
||||
|
return new AlertDialog.Builder(getActivity()) |
||||
|
.setMessage(R.string.request_read_external_storage_permission) |
||||
|
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { |
||||
|
@Override |
||||
|
public void onClick(DialogInterface dialog, int which) { |
||||
|
parent.requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, |
||||
|
REQUEST_READ_STORAGE); |
||||
|
} |
||||
|
}) |
||||
|
.create(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static class ErrorDialog extends DialogFragment { |
||||
|
|
||||
|
private static final String ARG_MESSAGE = "message"; |
||||
|
|
||||
|
public static ErrorDialog newInstance(String message) { |
||||
|
ErrorDialog dialog = new ErrorDialog(); |
||||
|
Bundle args = new Bundle(); |
||||
|
args.putString(ARG_MESSAGE, message); |
||||
|
dialog.setArguments(args); |
||||
|
return dialog; |
||||
|
} |
||||
|
|
||||
|
@NonNull |
||||
|
@Override |
||||
|
public Dialog onCreateDialog(Bundle savedInstanceState) { |
||||
|
final Activity activity = getActivity(); |
||||
|
return new AlertDialog.Builder(activity) |
||||
|
.setMessage(getArguments().getString(ARG_MESSAGE)) |
||||
|
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { |
||||
|
@Override |
||||
|
public void onClick(DialogInterface dialog, int i) { |
||||
|
getDialog().cancel(); |
||||
|
} |
||||
|
}) |
||||
|
.create(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, |
||||
|
@NonNull int[] grantResults) { |
||||
|
if (requestCode == REQUEST_READ_STORAGE) { |
||||
|
if (grantResults.length != 1 || grantResults[0] != PackageManager.PERMISSION_GRANTED) { // one requested permission, one result expected (in results array)
|
||||
|
ErrorDialog.newInstance(getString(R.string.request_read_external_storage_permission)) |
||||
|
.show(getChildFragmentManager(), FRAGMENT_DIALOG); |
||||
|
} else { |
||||
|
openFileManager(); |
||||
|
} |
||||
|
} else { |
||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { |
||||
|
if (requestCode == REQUEST_READ_FILE && resultCode == Activity.RESULT_OK) { |
||||
|
if (data != null) { |
||||
|
Uri uri = data.getData(); |
||||
|
|
||||
|
try { |
||||
|
InputStream is = getActivity().getContentResolver().openInputStream(uri); |
||||
|
JsonUtils jsonUtils = new JsonUtils(); |
||||
|
jsonUtils.saveCommands(getActivity(), is); |
||||
|
((Preference) findPreference("load_json_commands")).setIcon(R.drawable.ic_check_green_48dp); |
||||
|
System.out.println(); |
||||
|
} catch (Exception e) { |
||||
|
((Preference) findPreference("load_json_commands")).setIcon(R.drawable.ic_error_outline_red_48dp); |
||||
|
e.printStackTrace(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static Preference.OnPreferenceChangeListener updateThemeListener(final Runnable action) { |
||||
|
return new Preference.OnPreferenceChangeListener() { |
||||
|
@Override |
||||
|
public boolean onPreferenceChange(Preference preference, Object newValue) { |
||||
|
action.run(); |
||||
|
return true; |
||||
|
} |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
|
|
@ -0,0 +1,163 @@ |
|||||
|
package com.skulixlabs.iotcontrol; |
||||
|
|
||||
|
import android.util.Log; |
||||
|
|
||||
|
import java.io.DataInputStream; |
||||
|
import java.io.DataOutputStream; |
||||
|
import java.io.IOException; |
||||
|
import java.io.InputStream; |
||||
|
import java.net.InetSocketAddress; |
||||
|
import java.net.Socket; |
||||
|
|
||||
|
public class TcpClient { |
||||
|
public static final String TAG = TcpClient.class.getName(); |
||||
|
public static final int TIMEOUT = 1500; |
||||
|
public String SERVER_IP; |
||||
|
public int SERVER_PORT; |
||||
|
|
||||
|
// sends message received notifications
|
||||
|
private OnMessageReceived mMessageListener = null; |
||||
|
private OnClientError mErrorListener = null; |
||||
|
// while this is true, the server will continue running/listening
|
||||
|
private boolean mRun = false; |
||||
|
// used to send data
|
||||
|
DataOutputStream mBufferOut; |
||||
|
|
||||
|
/** |
||||
|
* Constructor of the class. OnMessagedReceived listens for the messages received from server |
||||
|
*/ |
||||
|
public TcpClient(String ipAddressWithPort, OnMessageReceived listener, OnClientError errorListener) { |
||||
|
String[] ipAddressPort = ipAddressWithPort.split(":"); |
||||
|
SERVER_IP = ipAddressPort[0]; |
||||
|
SERVER_PORT = Integer.valueOf(ipAddressPort[1]); |
||||
|
mMessageListener = listener; |
||||
|
mErrorListener = errorListener; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Sends the message entered by client to the server |
||||
|
* |
||||
|
* @param message text entered by client |
||||
|
*/ |
||||
|
public void sendMessage(final byte[] message) { |
||||
|
Runnable runnable = new Runnable() { |
||||
|
@Override |
||||
|
public void run() { |
||||
|
try { |
||||
|
if (mBufferOut != null) { |
||||
|
mBufferOut.write(message); |
||||
|
Log.d(TAG, "Sending data\n"); |
||||
|
mBufferOut.flush(); |
||||
|
} |
||||
|
} catch (Exception e) { |
||||
|
e.printStackTrace(); |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
Thread thread = new Thread(runnable); |
||||
|
thread.start(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Close the connection and release the members |
||||
|
*/ |
||||
|
public void stopClient() { |
||||
|
mRun = false; |
||||
|
|
||||
|
if (mBufferOut != null) { |
||||
|
try { |
||||
|
mBufferOut.flush(); |
||||
|
mBufferOut.close(); |
||||
|
} catch (IOException e) { |
||||
|
e.printStackTrace(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
unregisterListeners(); |
||||
|
//mBufferIn = null;
|
||||
|
mBufferOut = null; |
||||
|
|
||||
|
Log.d(TAG, "Client stopped successfully"); |
||||
|
} |
||||
|
|
||||
|
public void run() { |
||||
|
|
||||
|
mRun = true; |
||||
|
|
||||
|
try { |
||||
|
Log.d("TCP Client", "C: Connecting..."); |
||||
|
|
||||
|
//here you must put your computer's IP address.
|
||||
|
/*InetAddress serverAddr = InetAddress.getByName(SERVER_IP); |
||||
|
|
||||
|
//create a socket to make the connection with the server
|
||||
|
Socket socket = new Socket(serverAddr, SERVER_PORT);*/ |
||||
|
|
||||
|
InetSocketAddress sockAdr = new InetSocketAddress(SERVER_IP, SERVER_PORT); |
||||
|
Socket socket = new Socket(); |
||||
|
socket.setSoTimeout(TIMEOUT); |
||||
|
socket.connect(sockAdr, TIMEOUT); |
||||
|
|
||||
|
try { |
||||
|
|
||||
|
//sends the message to the server
|
||||
|
mBufferOut = new DataOutputStream(socket.getOutputStream()); |
||||
|
|
||||
|
//receives the message which the server sends back
|
||||
|
InputStream is = socket.getInputStream(); |
||||
|
DataInputStream dis = new DataInputStream(is); |
||||
|
|
||||
|
Log.d("test", "Client ready"); |
||||
|
sendMessage("X".getBytes()); // Send X and wait for response, then send command
|
||||
|
|
||||
|
//in this while the client listens for the messages sent by the server
|
||||
|
while (mRun) { |
||||
|
|
||||
|
// count the available bytes form the input stream
|
||||
|
int count = is.available(); |
||||
|
|
||||
|
// create buffer
|
||||
|
byte[] bs = new byte[count]; |
||||
|
|
||||
|
int read = dis.read(bs); |
||||
|
|
||||
|
if (count > 0 && mMessageListener != null) |
||||
|
mMessageListener.messageReceived(bs); |
||||
|
} |
||||
|
|
||||
|
Log.d("RESPONSE FROM SERVER", "S: Received Message"); |
||||
|
|
||||
|
} catch (Exception e) { |
||||
|
mErrorListener.onError(e); |
||||
|
Log.e(TAG, "S: Error", e); |
||||
|
} finally { |
||||
|
//the socket must be closed. It is not possible to reconnect to this socket
|
||||
|
// after it is closed, which means a new socket instance has to be created.
|
||||
|
socket.close(); |
||||
|
} |
||||
|
|
||||
|
} catch (Exception e) { |
||||
|
mErrorListener.onError(e); |
||||
|
Log.e(TAG, "C: Error", e); |
||||
|
} finally { |
||||
|
unregisterListeners(); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
private void unregisterListeners() { |
||||
|
mMessageListener = null; |
||||
|
mErrorListener = null; |
||||
|
} |
||||
|
|
||||
|
//Declare the interface. The method messageReceived(String message) will must be implemented in the Activity
|
||||
|
//class at on AsyncTask doInBackground
|
||||
|
public interface OnMessageReceived { |
||||
|
public void messageReceived(byte[] message); |
||||
|
} |
||||
|
|
||||
|
public interface OnClientError { |
||||
|
public void onError(Exception e); |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,6 @@ |
|||||
|
package com.skulixlabs.iotcontrol; |
||||
|
|
||||
|
public enum Type { |
||||
|
FEEDBACK, |
||||
|
STATE |
||||
|
} |
@ -0,0 +1,168 @@ |
|||||
|
package com.skulixlabs.iotcontrol.adapter; |
||||
|
|
||||
|
import android.content.Context; |
||||
|
import android.os.Handler; |
||||
|
import android.view.LayoutInflater; |
||||
|
import android.view.View; |
||||
|
import android.view.ViewGroup; |
||||
|
import android.widget.TextView; |
||||
|
|
||||
|
import androidx.annotation.NonNull; |
||||
|
import androidx.recyclerview.widget.RecyclerView; |
||||
|
|
||||
|
import com.dx.dxloadingbutton.lib.LoadingButton; |
||||
|
import com.google.android.flexbox.FlexboxLayout; |
||||
|
import com.skulixlabs.iotcontrol.ConnectTaskFromAdapter; |
||||
|
import com.skulixlabs.iotcontrol.MainFragment; |
||||
|
import com.skulixlabs.iotcontrol.R; |
||||
|
import com.skulixlabs.iotcontrol.bean.RunnableWithState; |
||||
|
import com.skulixlabs.iotcontrol.model.Cmd; |
||||
|
import com.skulixlabs.iotcontrol.model.CmdGroup; |
||||
|
import com.skulixlabs.iotcontrol.util.Utils; |
||||
|
|
||||
|
import java.lang.ref.WeakReference; |
||||
|
import java.util.HashMap; |
||||
|
import java.util.List; |
||||
|
import java.util.Map; |
||||
|
|
||||
|
import br.com.simplepass.loadingbutton.customViews.CircularProgressButton; |
||||
|
|
||||
|
|
||||
|
public class ItemAdapter extends RecyclerView.Adapter<ItemAdapter.ItemViewHolder> { |
||||
|
private List<CmdGroup> groupList; |
||||
|
private WeakReference<MainFragment> fragment; |
||||
|
private Map<Handler, RunnableWithState> stateHandlers = new HashMap<>(); |
||||
|
|
||||
|
public class ItemViewHolder extends RecyclerView.ViewHolder { |
||||
|
public Integer stateIndex; // Holds position (index) of state action
|
||||
|
|
||||
|
public TextView title; |
||||
|
public TextView state; |
||||
|
public FlexboxLayout buttonLayout; |
||||
|
|
||||
|
public ItemViewHolder(@NonNull View itemView) { |
||||
|
super(itemView); |
||||
|
title = (TextView) itemView.findViewById(R.id.tvTitle); |
||||
|
state = (TextView) itemView.findViewById(R.id.tvState); |
||||
|
buttonLayout = (FlexboxLayout) itemView.findViewById(R.id.btnLayout); |
||||
|
|
||||
|
state.setAllCaps(false); |
||||
|
} |
||||
|
|
||||
|
public LoadingButton getStateButton() { |
||||
|
return stateIndex != null ? (LoadingButton) buttonLayout.getChildAt(stateIndex) : null; |
||||
|
} |
||||
|
|
||||
|
public Context getContext() { |
||||
|
return itemView.getContext(); |
||||
|
} |
||||
|
|
||||
|
private LoadingButton addButton() { |
||||
|
int viewId = View.generateViewId(); |
||||
|
LayoutInflater inflater = LayoutInflater.from(getContext()); |
||||
|
LoadingButton button = (LoadingButton) inflater.inflate(R.layout.material_morph_button_2, null, false); |
||||
|
FlexboxLayout.LayoutParams linearLayoutParams = new FlexboxLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); |
||||
|
linearLayoutParams.setMargins(0, 0, Utils.calculatePixelsFromDp2(8, getContext()), 0); |
||||
|
button.setId(viewId); |
||||
|
buttonLayout.addView(button, linearLayoutParams); |
||||
|
return button; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public ItemAdapter(MainFragment fragment, List<CmdGroup> groupList) { |
||||
|
this.fragment = new WeakReference<>(fragment); |
||||
|
this.groupList = groupList; |
||||
|
} |
||||
|
|
||||
|
@NonNull |
||||
|
@Override |
||||
|
public ItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { |
||||
|
View itemView = LayoutInflater. |
||||
|
from(parent.getContext()). |
||||
|
inflate(R.layout.row_item, parent, false); |
||||
|
|
||||
|
return new ItemViewHolder(itemView); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onBindViewHolder(@NonNull final ItemViewHolder holder, int position) { |
||||
|
CmdGroup group = groupList.get(position); |
||||
|
holder.title.setText(group.getName()); |
||||
|
|
||||
|
for (final Cmd cmd : group.getCmdList()) { |
||||
|
|
||||
|
switch (cmd.getType()) { |
||||
|
case STATE: |
||||
|
// State actions are not shown if repeat is enabled
|
||||
|
if (cmd.getRepeat() != null) { |
||||
|
break; // Else continue to next statement to create a button
|
||||
|
} else { |
||||
|
holder.stateIndex = holder.buttonLayout.getChildCount(); |
||||
|
// And continue to next statement to create a button
|
||||
|
} |
||||
|
case FEEDBACK: |
||||
|
final LoadingButton actionButton = holder.addButton(); |
||||
|
|
||||
|
actionButton.setText(cmd.getAction()); |
||||
|
actionButton.setOnClickListener(new View.OnClickListener() { |
||||
|
@Override |
||||
|
public void onClick(View view) { |
||||
|
//actionButton.startMorphAnimation();
|
||||
|
actionButton.startLoading(); |
||||
|
new ConnectTaskFromAdapter(holder, fragment, cmd, actionButton).execute(); |
||||
|
} |
||||
|
}); |
||||
|
default: |
||||
|
|
||||
|
} |
||||
|
|
||||
|
if (cmd.getRepeat() != null) { |
||||
|
Handler handler = addStateListener(holder, cmd); |
||||
|
validateStateListener(handler); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public int getItemCount() { |
||||
|
return groupList.size(); |
||||
|
} |
||||
|
|
||||
|
public List<CmdGroup> getCollection() { |
||||
|
return groupList; |
||||
|
} |
||||
|
|
||||
|
public void validateStateListeners() { |
||||
|
for (Map.Entry<Handler, RunnableWithState> entry : stateHandlers.entrySet()) { |
||||
|
validateStateListener(entry.getKey()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void validateStateListener(Handler handler) { |
||||
|
RunnableWithState runnableWithState = stateHandlers.get(handler); |
||||
|
runnableWithState.isRunning = true; |
||||
|
handler.post(runnableWithState.runnable); |
||||
|
} |
||||
|
|
||||
|
public void invalidateStateListeners() { |
||||
|
for (Map.Entry<Handler, RunnableWithState> entry : stateHandlers.entrySet()) { |
||||
|
//entry.getKey().removeCallbacks(entry.getValue());
|
||||
|
entry.getValue().isRunning = false; |
||||
|
entry.getKey().removeCallbacksAndMessages(null); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private Handler addStateListener(final ItemViewHolder holder, @NonNull final Cmd cmd) { |
||||
|
final Handler handler = new Handler(); |
||||
|
Runnable runnable = new Runnable() { |
||||
|
@Override |
||||
|
public void run() { |
||||
|
new ConnectTaskFromAdapter(holder, fragment, cmd, null).execute(); |
||||
|
if (stateHandlers.get(handler).isRunning) |
||||
|
handler.postDelayed(this, cmd.getRepeat()); |
||||
|
} |
||||
|
}; |
||||
|
stateHandlers.put(handler, new RunnableWithState(runnable, false)); |
||||
|
return handler; |
||||
|
} |
||||
|
} |
@ -0,0 +1,11 @@ |
|||||
|
package com.skulixlabs.iotcontrol.bean; |
||||
|
|
||||
|
public class RunnableWithState { |
||||
|
public Runnable runnable; |
||||
|
public boolean isRunning; |
||||
|
|
||||
|
public RunnableWithState(Runnable runnable, boolean isRunning) { |
||||
|
this.runnable = runnable; |
||||
|
this.isRunning = isRunning; |
||||
|
} |
||||
|
} |
@ -0,0 +1,70 @@ |
|||||
|
package com.skulixlabs.iotcontrol.model; |
||||
|
|
||||
|
import com.skulixlabs.iotcontrol.Type; |
||||
|
import com.skulixlabs.iotcontrol.util.jackson.CmdsToMapDeserializer; |
||||
|
import com.fasterxml.jackson.annotation.JsonProperty; |
||||
|
import com.fasterxml.jackson.annotation.JsonUnwrapped; |
||||
|
import com.fasterxml.jackson.databind.annotation.JsonDeserialize; |
||||
|
|
||||
|
import java.util.HashMap; |
||||
|
|
||||
|
public class Cmd { |
||||
|
private String action; |
||||
|
@JsonProperty("cmd") |
||||
|
private int value; |
||||
|
@JsonUnwrapped |
||||
|
private Options options; |
||||
|
@JsonDeserialize(using = CmdsToMapDeserializer.class) |
||||
|
@JsonProperty("responses") |
||||
|
private HashMap<Byte, CmdResponse> responseList; |
||||
|
private Integer repeat; |
||||
|
private Type type; |
||||
|
|
||||
|
public String getAction() { |
||||
|
return action; |
||||
|
} |
||||
|
|
||||
|
public void setAction(String action) { |
||||
|
this.action = action; |
||||
|
} |
||||
|
|
||||
|
public int getValue() { |
||||
|
return value; |
||||
|
} |
||||
|
|
||||
|
public void setValue(int value) { |
||||
|
this.value = value; |
||||
|
} |
||||
|
|
||||
|
public Options getOptions() { |
||||
|
return options; |
||||
|
} |
||||
|
|
||||
|
public void setOptions(Options options) { |
||||
|
this.options = options; |
||||
|
} |
||||
|
|
||||
|
public HashMap<Byte, CmdResponse> getResponseList() { |
||||
|
return responseList; |
||||
|
} |
||||
|
|
||||
|
public void setResponseList(HashMap<Byte, CmdResponse> responseList) { |
||||
|
this.responseList = responseList; |
||||
|
} |
||||
|
|
||||
|
public Integer getRepeat() { |
||||
|
return repeat; |
||||
|
} |
||||
|
|
||||
|
public void setRepeat(Integer repeat) { |
||||
|
this.repeat = repeat; |
||||
|
} |
||||
|
|
||||
|
public Type getType() { |
||||
|
return type; |
||||
|
} |
||||
|
|
||||
|
public void setType(Type type) { |
||||
|
this.type = type; |
||||
|
} |
||||
|
} |
@ -0,0 +1,28 @@ |
|||||
|
package com.skulixlabs.iotcontrol.model; |
||||
|
|
||||
|
import com.fasterxml.jackson.annotation.JsonProperty; |
||||
|
|
||||
|
import java.util.List; |
||||
|
|
||||
|
public class CmdGroup { |
||||
|
@JsonProperty("group") |
||||
|
private String name; |
||||
|
@JsonProperty("actions") |
||||
|
private List<Cmd> cmdList; |
||||
|
|
||||
|
public String getName() { |
||||
|
return name; |
||||
|
} |
||||
|
|
||||
|
public void setName(String name) { |
||||
|
this.name = name; |
||||
|
} |
||||
|
|
||||
|
public List<Cmd> getCmdList() { |
||||
|
return cmdList; |
||||
|
} |
||||
|
|
||||
|
public void setCmdList(List<Cmd> cmdList) { |
||||
|
this.cmdList = cmdList; |
||||
|
} |
||||
|
} |
@ -0,0 +1,40 @@ |
|||||
|
package com.skulixlabs.iotcontrol.model; |
||||
|
|
||||
|
public class CmdResponse { |
||||
|
private byte value; |
||||
|
private String description; |
||||
|
private String color; |
||||
|
|
||||
|
public CmdResponse() { |
||||
|
} |
||||
|
|
||||
|
public CmdResponse(byte value, String description, String color) { |
||||
|
this.description = description; |
||||
|
this.value = value; |
||||
|
this.color = color; |
||||
|
} |
||||
|
|
||||
|
public byte getValue() { |
||||
|
return value; |
||||
|
} |
||||
|
|
||||
|
public void setValue(byte value) { |
||||
|
this.value = value; |
||||
|
} |
||||
|
|
||||
|
public String getDescription() { |
||||
|
return description; |
||||
|
} |
||||
|
|
||||
|
public void setDescription(String description) { |
||||
|
this.description = description; |
||||
|
} |
||||
|
|
||||
|
public String getColor() { |
||||
|
return color; |
||||
|
} |
||||
|
|
||||
|
public void setColor(String color) { |
||||
|
this.color = color; |
||||
|
} |
||||
|
} |
@ -0,0 +1,43 @@ |
|||||
|
package com.skulixlabs.iotcontrol.model; |
||||
|
|
||||
|
public class Options { |
||||
|
private String url; |
||||
|
private String hmacKey; |
||||
|
private int hashByteLength; |
||||
|
private int randDataLength; |
||||
|
|
||||
|
public Options() { |
||||
|
} |
||||
|
|
||||
|
public String getUrl() { |
||||
|
return url; |
||||
|
} |
||||
|
|
||||
|
public void setUrl(String url) { |
||||
|
this.url = url; |
||||
|
} |
||||
|
|
||||
|
public String getHmacKey() { |
||||
|
return hmacKey; |
||||
|
} |
||||
|
|
||||
|
public void setHmacKey(String hmacKey) { |
||||
|
this.hmacKey = hmacKey; |
||||
|
} |
||||
|
|
||||
|
public int getHashByteLength() { |
||||
|
return hashByteLength; |
||||
|
} |
||||
|
|
||||
|
public void setHashByteLength(int hashByteLength) { |
||||
|
this.hashByteLength = hashByteLength; |
||||
|
} |
||||
|
|
||||
|
public int getRandDataLength() { |
||||
|
return randDataLength; |
||||
|
} |
||||
|
|
||||
|
public void setRandDataLength(int randDataLength) { |
||||
|
this.randDataLength = randDataLength; |
||||
|
} |
||||
|
} |
@ -0,0 +1,66 @@ |
|||||
|
package com.skulixlabs.iotcontrol.util; |
||||
|
|
||||
|
import android.annotation.TargetApi; |
||||
|
import android.content.Context; |
||||
|
import android.content.res.Configuration; |
||||
|
import android.content.res.Resources; |
||||
|
import android.os.Build; |
||||
|
|
||||
|
import androidx.preference.PreferenceManager; |
||||
|
|
||||
|
import java.util.Locale; |
||||
|
|
||||
|
public class AppUtils { |
||||
|
public static String lang = null; |
||||
|
|
||||
|
/** |
||||
|
* Override {@link android.app.Activity#attachBaseContext(Context)} and pass this |
||||
|
* method as the Context parameter in order to change language in runtime. This |
||||
|
* will not work if dark mode is enabled, because the Context is rebuild by the |
||||
|
* Android Framework using default parameters (passed Context is ignored). |
||||
|
* |
||||
|
* @param context |
||||
|
* @return |
||||
|
*/ |
||||
|
public static Context updateBaseContextLocale(Context context) { // The context probably always is the default one, even if previously modified
|
||||
|
if (lang == null || "".equals(lang)) { |
||||
|
lang = PreferenceManager |
||||
|
.getDefaultSharedPreferences(context).getString("language", "system_default"); |
||||
|
} |
||||
|
|
||||
|
if (!lang.equals("system_default")) { |
||||
|
|
||||
|
String[] langSplit = lang.split("_"); |
||||
|
|
||||
|
Locale locale = new Locale(langSplit[0], langSplit[1]); |
||||
|
Locale.setDefault(locale); |
||||
|
|
||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { |
||||
|
return updateResourcesLocale(context, locale); |
||||
|
} |
||||
|
|
||||
|
return updateResourcesLocaleLegacy(context, locale); |
||||
|
} |
||||
|
return context; |
||||
|
} |
||||
|
|
||||
|
@TargetApi(Build.VERSION_CODES.N) |
||||
|
private static Context updateResourcesLocale(Context context, Locale locale) { |
||||
|
/*if (context.getResources().getConfiguration().getLocales().get(0).equals(locale)) // Even though locale=en
|
||||
|
return context;*/ |
||||
|
Configuration configuration = context.getResources().getConfiguration(); |
||||
|
configuration.setLocale(locale); |
||||
|
return context.createConfigurationContext(configuration); |
||||
|
} |
||||
|
|
||||
|
@SuppressWarnings("deprecation") |
||||
|
private static Context updateResourcesLocaleLegacy(Context context, Locale locale) { |
||||
|
if (context.getResources().getConfiguration().locale.equals(locale)) |
||||
|
return context; |
||||
|
Resources resources = context.getResources(); |
||||
|
Configuration configuration = resources.getConfiguration(); |
||||
|
configuration.locale = locale; |
||||
|
resources.updateConfiguration(configuration, resources.getDisplayMetrics()); |
||||
|
return context; |
||||
|
} |
||||
|
} |
@ -0,0 +1,170 @@ |
|||||
|
package com.skulixlabs.iotcontrol.util; |
||||
|
|
||||
|
import android.content.Context; |
||||
|
import android.graphics.Bitmap; |
||||
|
import android.graphics.Canvas; |
||||
|
import android.graphics.drawable.BitmapDrawable; |
||||
|
import android.graphics.drawable.Drawable; |
||||
|
import android.text.InputType; |
||||
|
import android.util.TypedValue; |
||||
|
import android.widget.EditText; |
||||
|
|
||||
|
import androidx.annotation.NonNull; |
||||
|
import androidx.preference.EditTextPreference; |
||||
|
|
||||
|
import com.skulixlabs.iotcontrol.model.Cmd; |
||||
|
import com.skulixlabs.iotcontrol.model.CmdResponse; |
||||
|
|
||||
|
import java.io.ByteArrayOutputStream; |
||||
|
import java.io.IOException; |
||||
|
import java.nio.ByteBuffer; |
||||
|
import java.security.InvalidKeyException; |
||||
|
import java.security.NoSuchAlgorithmException; |
||||
|
|
||||
|
import javax.crypto.Mac; |
||||
|
import javax.crypto.spec.SecretKeySpec; |
||||
|
|
||||
|
public class Utils { |
||||
|
private static final String TAG = Utils.class.getName(); |
||||
|
|
||||
|
public static byte[] generateHashWithHmac256ByByteValue(String hmacKey, byte[] data) { |
||||
|
try { |
||||
|
final String hashingAlgorithm = "HmacSHA256"; |
||||
|
|
||||
|
byte[] bytes = hmac(hashingAlgorithm, hmacKey.getBytes(), data); |
||||
|
|
||||
|
//Log.i(TAG, "message digest: " + bytes);
|
||||
|
return bytes; |
||||
|
|
||||
|
} catch (Exception e) { |
||||
|
e.printStackTrace(); |
||||
|
} |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
public static byte[] generateHashWithHmac256ByValue(String hmacKey, String message) { |
||||
|
try { |
||||
|
final String hashingAlgorithm = "HmacSHA256"; |
||||
|
|
||||
|
byte[] bytes = hmac(hashingAlgorithm, hmacKey.getBytes(), message.getBytes()); |
||||
|
|
||||
|
//Log.i(TAG, "message digest: " + bytesToHex(bytes));
|
||||
|
return bytes; |
||||
|
|
||||
|
} catch (Exception e) { |
||||
|
e.printStackTrace(); |
||||
|
} |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
public static byte[] hmac(String algorithm, byte[] key, byte[] message) throws NoSuchAlgorithmException, InvalidKeyException { |
||||
|
Mac mac = Mac.getInstance(algorithm); |
||||
|
mac.init(new SecretKeySpec(key, algorithm)); |
||||
|
return mac.doFinal(message); |
||||
|
} |
||||
|
|
||||
|
public static String bytesToHex(byte[] bytes) { |
||||
|
final char[] hexArray = "0123456789abcdef".toCharArray(); |
||||
|
char[] hexChars = new char[bytes.length * 2]; |
||||
|
for (int j = 0, v; j < bytes.length; j++) { |
||||
|
v = bytes[j] & 0xFF; |
||||
|
hexChars[j * 2] = hexArray[v >>> 4]; |
||||
|
hexChars[j * 2 + 1] = hexArray[v & 0x0F]; |
||||
|
} |
||||
|
return new String(hexChars); |
||||
|
} |
||||
|
|
||||
|
public static byte[] hexStringToByteArray(String s) { |
||||
|
int len = s.length(); |
||||
|
byte[] data = new byte[len / 2]; |
||||
|
for (int i = 0; i < len; i += 2) { |
||||
|
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) |
||||
|
+ Character.digit(s.charAt(i+1), 16)); |
||||
|
} |
||||
|
return data; |
||||
|
} |
||||
|
|
||||
|
public static byte oneDigitHexToByte(String s) { |
||||
|
String[] split = s.split("x"); |
||||
|
int i = Integer.parseInt(split[1], 16); |
||||
|
return (byte) i; |
||||
|
} |
||||
|
|
||||
|
public static byte analyzeResponse(String hmacKey, Byte[] cmdSend, ByteBuffer gotBytes, ByteBuffer resHash) throws IOException { |
||||
|
gotBytes.position(0); |
||||
|
resHash.position(0); |
||||
|
|
||||
|
byte[] gotBytesArr = new byte[gotBytes.remaining()]; |
||||
|
gotBytes.get(gotBytesArr); |
||||
|
|
||||
|
for(int i=0; i<cmdSend.length; i++){ |
||||
|
ByteBuffer testHash = getHash(hmacKey, gotBytesArr, new byte[] {cmdSend[i]}); |
||||
|
if (hashesAreTheSame(resHash, testHash)){ |
||||
|
return cmdSend[i]; |
||||
|
} |
||||
|
} |
||||
|
throw new IllegalArgumentException("Illegal response"); // Illegal response
|
||||
|
} |
||||
|
|
||||
|
private static ByteBuffer getHash(String hmacKey, byte[] data, byte[] additionalData) throws IOException { |
||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream( ); |
||||
|
outputStream.write(data); |
||||
|
outputStream.write(additionalData); |
||||
|
return ByteBuffer.wrap(Utils.generateHashWithHmac256ByByteValue(hmacKey, outputStream.toByteArray())); |
||||
|
} |
||||
|
|
||||
|
private static boolean hashesAreTheSame(ByteBuffer hash1, ByteBuffer hash2){ |
||||
|
//Log.d(TAG, "hash1: " + printHash(hash1));
|
||||
|
//Log.d(TAG, "hash2: " + printHash(hash2));
|
||||
|
|
||||
|
if (hash1.capacity() != hash2.capacity()) return false; |
||||
|
for (int j=0; j<hash1.capacity(); j++) if(hash1.get(j) != hash2.get(j)) return false; |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
public static EditTextPreference.OnBindEditTextListener editTextPreferenceNumericOnly() { |
||||
|
return new EditTextPreference.OnBindEditTextListener() { |
||||
|
@Override |
||||
|
public void onBindEditText(@NonNull EditText editText) { |
||||
|
editText.setInputType(InputType.TYPE_NUMBER_FLAG_DECIMAL); |
||||
|
} |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
public static String printHash(ByteBuffer hash) { |
||||
|
byte[] bytes = new byte[hash.remaining()]; |
||||
|
hash.get(bytes); |
||||
|
return printHash(bytes); |
||||
|
} |
||||
|
|
||||
|
public static String printHash(byte[] bytes) { |
||||
|
return bytesToHex(bytes); |
||||
|
} |
||||
|
|
||||
|
public static boolean stringNullOrEmpty(String value) { |
||||
|
if (value == null || value.equalsIgnoreCase("")) |
||||
|
return true; |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public static CmdResponse matchResponse(Cmd cmd, byte b) { |
||||
|
return cmd.getResponseList().get(b); |
||||
|
} |
||||
|
|
||||
|
public static Integer calculatePixelsFromDp2(Integer dps, Context context) { |
||||
|
return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dps, context.getResources().getDisplayMetrics())); // Or cast to int
|
||||
|
} |
||||
|
|
||||
|
public static Bitmap drawableToBitmap (Drawable drawable) { |
||||
|
if (drawable instanceof BitmapDrawable) { |
||||
|
return ((BitmapDrawable)drawable).getBitmap(); |
||||
|
} |
||||
|
|
||||
|
Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); |
||||
|
Canvas canvas = new Canvas(bitmap); |
||||
|
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); |
||||
|
drawable.draw(canvas); |
||||
|
|
||||
|
return bitmap; |
||||
|
} |
||||
|
} |
@ -0,0 +1,34 @@ |
|||||
|
package com.skulixlabs.iotcontrol.util.jackson; |
||||
|
|
||||
|
import com.skulixlabs.iotcontrol.model.CmdResponse; |
||||
|
import com.fasterxml.jackson.core.JsonParser; |
||||
|
import com.fasterxml.jackson.core.JsonProcessingException; |
||||
|
import com.fasterxml.jackson.databind.DeserializationContext; |
||||
|
import com.fasterxml.jackson.databind.JsonDeserializer; |
||||
|
import com.fasterxml.jackson.databind.JsonNode; |
||||
|
|
||||
|
import java.io.IOException; |
||||
|
import java.util.HashMap; |
||||
|
import java.util.Map; |
||||
|
|
||||
|
import static com.skulixlabs.iotcontrol.util.Utils.oneDigitHexToByte; |
||||
|
|
||||
|
public class CmdsToMapDeserializer extends JsonDeserializer { |
||||
|
|
||||
|
@Override |
||||
|
public Map<Byte, CmdResponse> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { |
||||
|
JsonNode node = p.getCodec().readTree(p); |
||||
|
|
||||
|
Map<Byte, CmdResponse> map = new HashMap<>(); |
||||
|
|
||||
|
for (JsonNode objectNode : node) { |
||||
|
byte value = oneDigitHexToByte(objectNode.get("value").asText()); |
||||
|
String description = objectNode.get("description").asText(); |
||||
|
String color = objectNode.get("color").asText(); |
||||
|
|
||||
|
map.put(value, new CmdResponse(value, description, color)); |
||||
|
} |
||||
|
|
||||
|
return map; |
||||
|
} |
||||
|
} |
@ -0,0 +1,53 @@ |
|||||
|
package com.skulixlabs.iotcontrol.util.jackson; |
||||
|
|
||||
|
import android.content.Context; |
||||
|
|
||||
|
import com.skulixlabs.iotcontrol.model.CmdGroup; |
||||
|
import com.fasterxml.jackson.core.type.TypeReference; |
||||
|
import com.fasterxml.jackson.databind.MapperFeature; |
||||
|
import com.fasterxml.jackson.databind.ObjectMapper; |
||||
|
|
||||
|
import org.apache.commons.io.IOUtils; |
||||
|
|
||||
|
import java.io.IOException; |
||||
|
import java.io.InputStream; |
||||
|
import java.util.List; |
||||
|
|
||||
|
import static android.content.Context.MODE_PRIVATE; |
||||
|
|
||||
|
public class JsonUtils { |
||||
|
public static final int REQUEST_READ_STORAGE = 7; |
||||
|
public static final int REQUEST_READ_FILE = 8; |
||||
|
|
||||
|
public boolean saveCommands(Context context, InputStream is) throws IOException { |
||||
|
String json = IOUtils.toString(is, "UTF-8"); |
||||
|
validateJson(json); |
||||
|
return context.getSharedPreferences("commands", MODE_PRIVATE).edit().putString("commands", json).commit(); |
||||
|
} |
||||
|
|
||||
|
public boolean saveCommands(Context context, String json) throws IOException { |
||||
|
validateJson(json); |
||||
|
return context.getSharedPreferences("commands", MODE_PRIVATE).edit().putString("commands", json).commit(); |
||||
|
} |
||||
|
|
||||
|
public void validateJson(String json) throws IOException { |
||||
|
ObjectMapper objectMapper = new ObjectMapper(); |
||||
|
objectMapper.readTree(json); |
||||
|
} |
||||
|
|
||||
|
public List<CmdGroup> getCommands(Context context) throws IOException { |
||||
|
List<CmdGroup> cmdGroups = parseCommands(context.getSharedPreferences("commands", MODE_PRIVATE).getString("commands", null)); |
||||
|
return cmdGroups; |
||||
|
} |
||||
|
|
||||
|
public List<CmdGroup> parseCommands(String json) throws IOException { |
||||
|
if (json == null) throw new IllegalArgumentException("Content not valid"); |
||||
|
return new ObjectMapper() |
||||
|
.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS) |
||||
|
.readValue(json, new TypeReference<List<CmdGroup>>(){}); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
} |
@ -0,0 +1,127 @@ |
|||||
|
package com.skulixlabs.iotcontrol.util.legacy; |
||||
|
|
||||
|
import android.util.Log; |
||||
|
|
||||
|
import java.io.BufferedReader; |
||||
|
import java.io.BufferedWriter; |
||||
|
import java.io.InputStreamReader; |
||||
|
import java.io.OutputStreamWriter; |
||||
|
import java.io.PrintWriter; |
||||
|
import java.net.InetAddress; |
||||
|
import java.net.Socket; |
||||
|
|
||||
|
public class TcpClient_original { |
||||
|
public static final String TAG = TcpClient_original.class.getSimpleName(); |
||||
|
public static final String SERVER_IP = "192.168.1.170"; //server IP address
|
||||
|
public static final int SERVER_PORT = 3000; |
||||
|
|
||||
|
// message to send to the server
|
||||
|
private String mServerMessage; |
||||
|
// sends message received notifications
|
||||
|
private OnMessageReceived mMessageListener = null; |
||||
|
// while this is true, the server will continue running
|
||||
|
private boolean mRun = false; |
||||
|
// used to send messages
|
||||
|
private PrintWriter mBufferOut; |
||||
|
// used to read messages from the server
|
||||
|
private BufferedReader mBufferIn; |
||||
|
|
||||
|
/** |
||||
|
* Constructor of the class. OnMessagedReceived listens for the messages received from server |
||||
|
*/ |
||||
|
public TcpClient_original(OnMessageReceived listener) { |
||||
|
mMessageListener = listener; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Sends the message entered by client to the server |
||||
|
* |
||||
|
* @param message text entered by client |
||||
|
*/ |
||||
|
public void sendMessage(final String message) { |
||||
|
Runnable runnable = new Runnable() { |
||||
|
@Override |
||||
|
public void run() { |
||||
|
if (mBufferOut != null) { |
||||
|
Log.d(TAG, "Sending: " + message); |
||||
|
mBufferOut.println(message); |
||||
|
mBufferOut.flush(); |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
Thread thread = new Thread(runnable); |
||||
|
thread.start(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Close the connection and release the members |
||||
|
*/ |
||||
|
public void stopClient() { |
||||
|
|
||||
|
mRun = false; |
||||
|
|
||||
|
if (mBufferOut != null) { |
||||
|
mBufferOut.flush(); |
||||
|
mBufferOut.close(); |
||||
|
} |
||||
|
|
||||
|
mMessageListener = null; |
||||
|
mBufferIn = null; |
||||
|
mBufferOut = null; |
||||
|
mServerMessage = null; |
||||
|
} |
||||
|
|
||||
|
public void run() { |
||||
|
|
||||
|
mRun = true; |
||||
|
|
||||
|
try { |
||||
|
//here you must put your computer's IP address.
|
||||
|
InetAddress serverAddr = InetAddress.getByName(SERVER_IP); |
||||
|
|
||||
|
Log.d("TCP Client", "C: Connecting..."); |
||||
|
|
||||
|
//create a socket to make the connection with the server
|
||||
|
Socket socket = new Socket(serverAddr, SERVER_PORT); |
||||
|
|
||||
|
try { |
||||
|
|
||||
|
//sends the message to the server
|
||||
|
mBufferOut = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true); |
||||
|
|
||||
|
//receives the message which the server sends back
|
||||
|
mBufferIn = new BufferedReader(new InputStreamReader(socket.getInputStream())); |
||||
|
|
||||
|
|
||||
|
//in this while the client listens for the messages sent by the server
|
||||
|
while (mRun) { |
||||
|
mServerMessage = mBufferIn.readLine(); |
||||
|
if (mServerMessage != null && mMessageListener != null) { |
||||
|
//sendAction the method messageReceived from MyActivity class
|
||||
|
mMessageListener.messageReceived(mServerMessage); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
Log.d("RESPONSE FROM SERVER", "S: Received Message: '" + mServerMessage + "'"); |
||||
|
|
||||
|
} catch (Exception e) { |
||||
|
Log.e("TCP", "S: Error", e); |
||||
|
} finally { |
||||
|
//the socket must be closed. It is not possible to reconnect to this socket
|
||||
|
// after it is closed, which means a new socket instance has to be created.
|
||||
|
socket.close(); |
||||
|
} |
||||
|
|
||||
|
} catch (Exception e) { |
||||
|
Log.e("TCP", "C: Error", e); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
//Declare the interface. The method messageReceived(String message) will must be implemented in the Activity
|
||||
|
//class at on AsyncTask doInBackground
|
||||
|
public interface OnMessageReceived { |
||||
|
public void messageReceived(String message); |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,34 @@ |
|||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" |
||||
|
xmlns:aapt="http://schemas.android.com/aapt" |
||||
|
android:width="108dp" |
||||
|
android:height="108dp" |
||||
|
android:viewportWidth="108" |
||||
|
android:viewportHeight="108"> |
||||
|
<path |
||||
|
android:fillType="evenOdd" |
||||
|
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z" |
||||
|
android:strokeWidth="1" |
||||
|
android:strokeColor="#00000000"> |
||||
|
<aapt:attr name="android:fillColor"> |
||||
|
<gradient |
||||
|
android:endX="78.5885" |
||||
|
android:endY="90.9159" |
||||
|
android:startX="48.7653" |
||||
|
android:startY="61.0927" |
||||
|
android:type="linear"> |
||||
|
<item |
||||
|
android:color="#44000000" |
||||
|
android:offset="0.0" /> |
||||
|
<item |
||||
|
android:color="#00000000" |
||||
|
android:offset="1.0" /> |
||||
|
</gradient> |
||||
|
</aapt:attr> |
||||
|
</path> |
||||
|
<path |
||||
|
android:fillColor="#FFFFFF" |
||||
|
android:fillType="nonZero" |
||||
|
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z" |
||||
|
android:strokeWidth="1" |
||||
|
android:strokeColor="#00000000" /> |
||||
|
</vector> |
@ -0,0 +1,5 @@ |
|||||
|
<vector android:height="48dp" android:tint="#00F91A" |
||||
|
android:viewportHeight="24.0" android:viewportWidth="24.0" |
||||
|
android:width="48dp" xmlns:android="http://schemas.android.com/apk/res/android"> |
||||
|
<path android:fillColor="#FF000000" android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/> |
||||
|
</vector> |
@ -0,0 +1,5 @@ |
|||||
|
<vector android:height="24dp" android:tint="#FF0D00" |
||||
|
android:viewportHeight="24.0" android:viewportWidth="24.0" |
||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> |
||||
|
<path android:fillColor="#FF000000" android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/> |
||||
|
</vector> |
@ -0,0 +1,5 @@ |
|||||
|
<vector android:height="48dp" android:tint="#FF0000" |
||||
|
android:viewportHeight="24.0" android:viewportWidth="24.0" |
||||
|
android:width="48dp" xmlns:android="http://schemas.android.com/apk/res/android"> |
||||
|
<path android:fillColor="#FF000000" android:pathData="M11,15h2v2h-2zM11,7h2v6h-2zM11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z"/> |
||||
|
</vector> |
@ -0,0 +1,170 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" |
||||
|
android:width="108dp" |
||||
|
android:height="108dp" |
||||
|
android:viewportWidth="108" |
||||
|
android:viewportHeight="108"> |
||||
|
<path |
||||
|
android:fillColor="#008577" |
||||
|
android:pathData="M0,0h108v108h-108z" /> |
||||
|
<path |
||||
|
android:fillColor="#00000000" |
||||
|
android:pathData="M9,0L9,108" |
||||
|
android:strokeWidth="0.8" |
||||
|
android:strokeColor="#33FFFFFF" /> |
||||
|
<path |
||||
|
android:fillColor="#00000000" |
||||
|
android:pathData="M19,0L19,108" |
||||
|
android:strokeWidth="0.8" |
||||
|
android:strokeColor="#33FFFFFF" /> |
||||
|
<path |
||||
|
android:fillColor="#00000000" |
||||
|
android:pathData="M29,0L29,108" |
||||
|
android:strokeWidth="0.8" |
||||
|
android:strokeColor="#33FFFFFF" /> |
||||
|
<path |
||||
|
android:fillColor="#00000000" |
||||
|
android:pathData="M39,0L39,108" |
||||
|
android:strokeWidth="0.8" |
||||
|
android:strokeColor="#33FFFFFF" /> |
||||
|
<path |
||||
|
android:fillColor="#00000000" |
||||
|
android:pathData="M49,0L49,108" |
||||
|
android:strokeWidth="0.8" |
||||
|
android:strokeColor="#33FFFFFF" /> |
||||
|
<path |
||||
|
android:fillColor="#00000000" |
||||
|
android:pathData="M59,0L59,108" |
||||
|
android:strokeWidth="0.8" |
||||
|
android:strokeColor="#33FFFFFF" /> |
||||
|
<path |
||||
|
android:fillColor="#00000000" |
||||
|
android:pathData="M69,0L69,108" |
||||
|
android:strokeWidth="0.8" |
||||
|
android:strokeColor="#33FFFFFF" /> |
||||
|
<path |
||||
|
android:fillColor="#00000000" |
||||
|
android:pathData="M79,0L79,108" |
||||
|
android:strokeWidth="0.8" |
||||
|
android:strokeColor="#33FFFFFF" /> |
||||
|
<path |
||||
|
android:fillColor="#00000000" |
||||
|
android:pathData="M89,0L89,108" |
||||
|
android:strokeWidth="0.8" |
||||
|
android:strokeColor="#33FFFFFF" /> |
||||
|
<path |
||||
|
android:fillColor="#00000000" |
||||
|
android:pathData="M99,0L99,108" |
||||
|
android:strokeWidth="0.8" |
||||
|
android:strokeColor="#33FFFFFF" /> |
||||
|
<path |
||||
|
android:fillColor="#00000000" |
||||
|
android:pathData="M0,9L108,9" |
||||
|
android:strokeWidth="0.8" |
||||
|
android:strokeColor="#33FFFFFF" /> |
||||
|
<path |
||||
|
android:fillColor="#00000000" |
||||
|
android:pathData="M0,19L108,19" |
||||
|
android:strokeWidth="0.8" |
||||
|
android:strokeColor="#33FFFFFF" /> |
||||
|
<path |
||||
|
android:fillColor="#00000000" |
||||
|
android:pathData="M0,29L108,29" |
||||
|
android:strokeWidth="0.8" |
||||
|
android:strokeColor="#33FFFFFF" /> |
||||
|
<path |
||||
|
android:fillColor="#00000000" |
||||
|
android:pathData="M0,39L108,39" |
||||
|
android:strokeWidth="0.8" |
||||
|
android:strokeColor="#33FFFFFF" /> |
||||
|
<path |
||||
|
android:fillColor="#00000000" |
||||
|
android:pathData="M0,49L108,49" |
||||
|
android:strokeWidth="0.8" |
||||
|
android:strokeColor="#33FFFFFF" /> |
||||
|
<path |
||||
|
android:fillColor="#00000000" |
||||
|
android:pathData="M0,59L108,59" |
||||
|
android:strokeWidth="0.8" |
||||
|
android:strokeColor="#33FFFFFF" /> |
||||
|
<path |
||||
|
android:fillColor="#00000000" |
||||
|
android:pathData="M0,69L108,69" |
||||
|
android:strokeWidth="0.8" |
||||
|
android:strokeColor="#33FFFFFF" /> |
||||
|
<path |
||||
|
android:fillColor="#00000000" |
||||
|
android:pathData="M0,79L108,79" |
||||
|
android:strokeWidth="0.8" |
||||
|
android:strokeColor="#33FFFFFF" /> |
||||
|
<path |
||||
|
android:fillColor="#00000000" |
||||
|
android:pathData="M0,89L108,89" |
||||
|
android:strokeWidth="0.8" |
||||
|
android:strokeColor="#33FFFFFF" /> |
||||
|
<path |
||||
|
android:fillColor="#00000000" |
||||
|
android:pathData="M0,99L108,99" |
||||
|
android:strokeWidth="0.8" |
||||
|
android:strokeColor="#33FFFFFF" /> |
||||
|
<path |
||||
|
android:fillColor="#00000000" |
||||
|
android:pathData="M19,29L89,29" |
||||
|
android:strokeWidth="0.8" |
||||
|
android:strokeColor="#33FFFFFF" /> |
||||
|
<path |
||||
|
android:fillColor="#00000000" |
||||
|
android:pathData="M19,39L89,39" |
||||
|
android:strokeWidth="0.8" |
||||
|
android:strokeColor="#33FFFFFF" /> |
||||
|
<path |
||||
|
android:fillColor="#00000000" |
||||
|
android:pathData="M19,49L89,49" |
||||
|
android:strokeWidth="0.8" |
||||
|
android:strokeColor="#33FFFFFF" /> |
||||
|
<path |
||||
|
android:fillColor="#00000000" |
||||
|
android:pathData="M19,59L89,59" |
||||
|
android:strokeWidth="0.8" |
||||
|
android:strokeColor="#33FFFFFF" /> |
||||
|
<path |
||||
|
android:fillColor="#00000000" |
||||
|
android:pathData="M19,69L89,69" |
||||
|
android:strokeWidth="0.8" |
||||
|
android:strokeColor="#33FFFFFF" /> |
||||
|
<path |
||||
|
android:fillColor="#00000000" |
||||
|
android:pathData="M19,79L89,79" |
||||
|
android:strokeWidth="0.8" |
||||
|
android:strokeColor="#33FFFFFF" /> |
||||
|
<path |
||||
|
android:fillColor="#00000000" |
||||
|
android:pathData="M29,19L29,89" |
||||
|
android:strokeWidth="0.8" |
||||
|
android:strokeColor="#33FFFFFF" /> |
||||
|
<path |
||||
|
android:fillColor="#00000000" |
||||
|
android:pathData="M39,19L39,89" |
||||
|
android:strokeWidth="0.8" |
||||
|
android:strokeColor="#33FFFFFF" /> |
||||
|
<path |
||||
|
android:fillColor="#00000000" |
||||
|
android:pathData="M49,19L49,89" |
||||
|
android:strokeWidth="0.8" |
||||
|
android:strokeColor="#33FFFFFF" /> |
||||
|
<path |
||||
|
android:fillColor="#00000000" |
||||
|
android:pathData="M59,19L59,89" |
||||
|
android:strokeWidth="0.8" |
||||
|
android:strokeColor="#33FFFFFF" /> |
||||
|
<path |
||||
|
android:fillColor="#00000000" |
||||
|
android:pathData="M69,19L69,89" |
||||
|
android:strokeWidth="0.8" |
||||
|
android:strokeColor="#33FFFFFF" /> |
||||
|
<path |
||||
|
android:fillColor="#00000000" |
||||
|
android:pathData="M79,19L79,89" |
||||
|
android:strokeWidth="0.8" |
||||
|
android:strokeColor="#33FFFFFF" /> |
||||
|
</vector> |
@ -0,0 +1,34 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<LinearLayout |
||||
|
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" |
||||
|
android:orientation="vertical"> |
||||
|
|
||||
|
|
||||
|
<com.google.android.material.appbar.AppBarLayout |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:theme="@style/AppTheme.AppBarOverlay"> |
||||
|
|
||||
|
<androidx.appcompat.widget.Toolbar |
||||
|
android:id="@+id/toolbar" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="?attr/actionBarSize" |
||||
|
app:popupTheme="@style/AppTheme.PopupOverlay" |
||||
|
android:theme="@style/ToolbarTheme"/> |
||||
|
|
||||
|
</com.google.android.material.appbar.AppBarLayout> |
||||
|
|
||||
|
<fragment |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="match_parent" |
||||
|
android:id="@+id/my_nav_host_fragment" |
||||
|
android:name="androidx.navigation.fragment.NavHostFragment" |
||||
|
app:navGraph="@navigation/nav_graph" |
||||
|
app:defaultNavHost="true" /> |
||||
|
|
||||
|
</LinearLayout> |
@ -0,0 +1,29 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
|
xmlns:app="http://schemas.android.com/apk/res-auto" |
||||
|
android:orientation="vertical" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="match_parent" |
||||
|
android:padding="15dp"> |
||||
|
|
||||
|
|
||||
|
<Button |
||||
|
android:id="@+id/btnFromClipboard" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
app:cornerRadius="@dimen/material_button_rounded_radius" |
||||
|
android:text="@string/paste_from_clipboard" /> |
||||
|
|
||||
|
<include |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
layout="@layout/divider_or_horizontal" /> |
||||
|
|
||||
|
<Button |
||||
|
android:id="@+id/btnFromFile" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
style="@style/Widget.MaterialComponents.Button.OutlinedButton" |
||||
|
app:cornerRadius="@dimen/material_button_rounded_radius" |
||||
|
android:text="@string/import_from_file" /> |
||||
|
</LinearLayout> |
@ -0,0 +1,31 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
|
android:orientation="horizontal" |
||||
|
android:layout_width="wrap_content" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:weightSum="1" |
||||
|
android:gravity="center"> |
||||
|
|
||||
|
<View |
||||
|
android:layout_width="0dp" |
||||
|
android:layout_height="1dp" |
||||
|
android:layout_weight="0.45" |
||||
|
android:background="@android:color/darker_gray" |
||||
|
android:layout_marginVertical="15dp"/> |
||||
|
|
||||
|
<TextView |
||||
|
android:layout_width="0dp" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_weight="0.1" |
||||
|
android:gravity="center" |
||||
|
android:text="@string/or_header"> |
||||
|
</TextView> |
||||
|
|
||||
|
<View |
||||
|
android:layout_width="0dp" |
||||
|
android:layout_height="1dp" |
||||
|
android:layout_weight="0.45" |
||||
|
android:background="@android:color/darker_gray" |
||||
|
android:layout_marginVertical="15dp"/> |
||||
|
|
||||
|
</LinearLayout> |
@ -0,0 +1,54 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<androidx.constraintlayout.widget.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=".AboutFragment" |
||||
|
android:id="@+id/aboutLayout"> |
||||
|
|
||||
|
<!-- TODO: Update blank fragment layout --> |
||||
|
<TextView |
||||
|
android:id="@+id/tvAppName" |
||||
|
android:layout_width="wrap_content" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_marginStart="16dp" |
||||
|
android:layout_marginTop="16dp" |
||||
|
android:text="@string/app_name" |
||||
|
android:textSize="30sp" |
||||
|
app:layout_constraintStart_toStartOf="parent" |
||||
|
app:layout_constraintTop_toTopOf="parent" |
||||
|
android:gravity="bottom"/> |
||||
|
|
||||
|
<TextView |
||||
|
android:id="@+id/tvTeamName" |
||||
|
android:layout_width="wrap_content" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_marginTop="8dp" |
||||
|
android:text="by SkulixLabs" |
||||
|
app:layout_constraintStart_toStartOf="@+id/tvAppName" |
||||
|
app:layout_constraintTop_toBottomOf="@+id/tvAppName" |
||||
|
tools:text="by SkulixLabs" /> |
||||
|
|
||||
|
<TextView |
||||
|
android:id="@+id/tvAppVersion" |
||||
|
android:layout_width="wrap_content" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_marginTop="16dp" |
||||
|
android:text="@string/appVersion" |
||||
|
app:layout_constraintStart_toStartOf="@+id/tvTeamName" |
||||
|
app:layout_constraintTop_toBottomOf="@+id/tvTeamName" |
||||
|
tools:text="@string/appVersion" /> |
||||
|
|
||||
|
<Button |
||||
|
android:id="@+id/btnLicenses" |
||||
|
style="@style/Widget.MaterialComponents.Button.TextButton" |
||||
|
android:layout_width="wrap_content" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_marginTop="64dp" |
||||
|
android:text="@string/open_source_liceses" |
||||
|
app:layout_constraintEnd_toEndOf="parent" |
||||
|
app:layout_constraintStart_toStartOf="parent" |
||||
|
app:layout_constraintTop_toBottomOf="@+id/tvAppVersion" /> |
||||
|
|
||||
|
</androidx.constraintlayout.widget.ConstraintLayout> |
@ -0,0 +1,53 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<androidx.constraintlayout.widget.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" |
||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior" |
||||
|
tools:context=".MainFragment" |
||||
|
android:id="@+id/mainLayout"> |
||||
|
<!--tools:showIn="@layout/activity_main"--> |
||||
|
|
||||
|
<!--<Button |
||||
|
android:id="@+id/send" |
||||
|
android:layout_width="wrap_content" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_marginStart="8dp" |
||||
|
android:layout_marginTop="8dp" |
||||
|
android:layout_marginEnd="8dp" |
||||
|
android:paddingHorizontal="64dp" |
||||
|
android:paddingVertical="48dp" |
||||
|
android:text="@string/btn_send" |
||||
|
android:textSize="20sp" |
||||
|
app:layout_constraintBottom_toBottomOf="parent" |
||||
|
app:layout_constraintEnd_toEndOf="parent" |
||||
|
app:layout_constraintStart_toStartOf="parent" |
||||
|
app:layout_constraintTop_toTopOf="parent" |
||||
|
app:layout_constraintVertical_bias="0.916" />--> |
||||
|
|
||||
|
<!--<br.com.simplepass.loadingbutton.customViews.CircularProgressButton |
||||
|
android:id="@+id/imgBtnTest0" |
||||
|
android:layout_width="wrap_content" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:text="Test 0" |
||||
|
android:theme="@style/mybtn" |
||||
|
app:finalCornerAngle="50dp" |
||||
|
app:initialCornerAngle="0dp" |
||||
|
app:layout_constraintStart_toStartOf="parent" |
||||
|
app:layout_constraintTop_toTopOf="parent" |
||||
|
app:spinning_bar_color="#FFFFFF" |
||||
|
app:spinning_bar_width="3dp" />--> |
||||
|
|
||||
|
<androidx.recyclerview.widget.RecyclerView |
||||
|
android:id="@+id/cardList" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="0dp" |
||||
|
app:layout_constraintBottom_toBottomOf="parent" |
||||
|
app:layout_constraintEnd_toEndOf="parent" |
||||
|
app:layout_constraintHorizontal_bias="1.0" |
||||
|
app:layout_constraintStart_toStartOf="parent" |
||||
|
app:layout_constraintTop_toTopOf="parent" |
||||
|
app:layout_constraintVertical_bias="1.0" /> |
||||
|
|
||||
|
</androidx.constraintlayout.widget.ConstraintLayout> |
@ -0,0 +1,18 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<androidx.constraintlayout.widget.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=".SettingsFragment"> |
||||
|
|
||||
|
<fragment |
||||
|
android:id="@+id/fragment" |
||||
|
android:name="com.skulixlabs.iotcontrol.SettingsFragment" |
||||
|
android:layout_width="wrap_content" |
||||
|
android:layout_height="wrap_content" |
||||
|
app:layout_constraintBottom_toBottomOf="parent" |
||||
|
app:layout_constraintEnd_toEndOf="parent" |
||||
|
app:layout_constraintStart_toStartOf="parent" |
||||
|
app:layout_constraintTop_toTopOf="parent" /> |
||||
|
</androidx.constraintlayout.widget.ConstraintLayout> |
@ -0,0 +1,24 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
|
||||
|
<br.com.simplepass.loadingbutton.customViews.CircularProgressButton |
||||
|
xmlns:android="http://schemas.android.com/apk/res/android" |
||||
|
xmlns:app="http://schemas.android.com/apk/res-auto" |
||||
|
style="@style/mybtn" |
||||
|
android:id="@+id/btn" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="85dp" |
||||
|
android:layout_marginLeft="18dp" |
||||
|
android:layout_marginRight="18dp" |
||||
|
android:layout_marginBottom="10dp" |
||||
|
android:text="Test" |
||||
|
app:finalCornerAngle="50dp" |
||||
|
app:initialCornerAngle="0dp" |
||||
|
app:spinning_bar_color="#FFFFFF" |
||||
|
app:spinning_bar_padding="0dp" |
||||
|
app:spinning_bar_width="3dp" |
||||
|
|
||||
|
app:layout_constraintBottom_toBottomOf="parent" |
||||
|
app:layout_constraintLeft_toLeftOf="parent" |
||||
|
app:layout_constraintRight_toRightOf="parent" |
||||
|
app:layout_constraintTop_toTopOf="parent"> |
||||
|
</br.com.simplepass.loadingbutton.customViews.CircularProgressButton> |
@ -0,0 +1,20 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
|
||||
|
<com.dx.dxloadingbutton.lib.LoadingButton |
||||
|
xmlns:android="http://schemas.android.com/apk/res/android" |
||||
|
xmlns:app="http://schemas.android.com/apk/res-auto" |
||||
|
android:id="@+id/btn" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_marginLeft="18dp" |
||||
|
android:layout_marginRight="18dp" |
||||
|
android:layout_marginBottom="10dp" |
||||
|
app:layout_constraintBottom_toBottomOf="parent" |
||||
|
app:layout_constraintLeft_toLeftOf="parent" |
||||
|
app:layout_constraintRight_toRightOf="parent" |
||||
|
app:layout_constraintTop_toTopOf="parent" |
||||
|
app:lb_btnText="Test" |
||||
|
app:lb_resetAfterFailed="true" |
||||
|
app:lb_btnRippleColor="#000000" |
||||
|
app:lb_btnColor="?attr/colorPrimary" > |
||||
|
</com.dx.dxloadingbutton.lib.LoadingButton> |
@ -0,0 +1,81 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<androidx.cardview.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto" |
||||
|
xmlns:android="http://schemas.android.com/apk/res/android" |
||||
|
xmlns:tools="http://schemas.android.com/tools" |
||||
|
xmlns:app="http://schemas.android.com/apk/res-auto" |
||||
|
android:id="@+id/card_view" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
card_view:cardCornerRadius="4dp" |
||||
|
android:layout_margin="5dp" |
||||
|
card_view:contentPadding="5dp" |
||||
|
tools:context=".MainFragment"> |
||||
|
|
||||
|
<LinearLayout |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="match_parent" |
||||
|
android:orientation="horizontal" |
||||
|
android:weightSum="1"> |
||||
|
|
||||
|
<androidx.constraintlayout.widget.ConstraintLayout |
||||
|
android:layout_width="0dp" |
||||
|
android:layout_height="match_parent" |
||||
|
android:layout_weight="0.45" |
||||
|
android:padding="8dp" > |
||||
|
|
||||
|
<TextView |
||||
|
android:id="@+id/tvTitle" |
||||
|
android:layout_width="wrap_content" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_marginEnd="8dp" |
||||
|
android:text="Title test" |
||||
|
android:textSize="18sp" |
||||
|
card_view:layout_constraintEnd_toEndOf="parent" |
||||
|
card_view:layout_constraintHorizontal_bias="0.0" |
||||
|
card_view:layout_constraintStart_toStartOf="parent" |
||||
|
card_view:layout_constraintTop_toTopOf="parent" /> |
||||
|
|
||||
|
<TextView |
||||
|
android:id="@+id/tvState" |
||||
|
android:layout_width="wrap_content" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_marginTop="8dp" |
||||
|
android:layout_marginEnd="8dp" |
||||
|
android:layout_marginBottom="8dp" |
||||
|
android:text="" |
||||
|
android:textSize="15sp" |
||||
|
card_view:layout_constraintBottom_toBottomOf="parent" |
||||
|
card_view:layout_constraintEnd_toEndOf="parent" |
||||
|
card_view:layout_constraintHorizontal_bias="0.0" |
||||
|
card_view:layout_constraintStart_toStartOf="parent" |
||||
|
card_view:layout_constraintTop_toBottomOf="@+id/tvTitle" /> |
||||
|
|
||||
|
</androidx.constraintlayout.widget.ConstraintLayout> |
||||
|
|
||||
|
<!--<LinearLayout |
||||
|
android:id="@+id/btnLayout" |
||||
|
style="@style/Theme.MaterialComponents.DayNight" |
||||
|
android:layout_width="0dp" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_weight="0.5" |
||||
|
android:paddingTop="20dp" |
||||
|
android:orientation="horizontal" |
||||
|
android:gravity="right|bottom" |
||||
|
android:background="@color/colorAccent"/>--> |
||||
|
|
||||
|
<com.google.android.flexbox.FlexboxLayout |
||||
|
xmlns:android="http://schemas.android.com/apk/res/android" |
||||
|
xmlns:app="http://schemas.android.com/apk/res-auto" |
||||
|
android:id="@+id/btnLayout" |
||||
|
style="@style/Theme.MaterialComponents.DayNight" |
||||
|
android:layout_width="0dp" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_weight="0.55" |
||||
|
android:paddingTop="20dp" |
||||
|
app:flexWrap="wrap" |
||||
|
app:alignItems="flex_start" |
||||
|
app:alignContent="flex_start"> |
||||
|
</com.google.android.flexbox.FlexboxLayout> |
||||
|
|
||||
|
</LinearLayout> |
||||
|
</androidx.cardview.widget.CardView> |
@ -0,0 +1,33 @@ |
|||||
|
<?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" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="90dp" |
||||
|
android:orientation="vertical"> |
||||
|
|
||||
|
<ImageView |
||||
|
android:id="@+id/imvColor" |
||||
|
android:layout_width="55dp" |
||||
|
android:layout_height="match_parent" |
||||
|
android:layout_alignParentTop="true" |
||||
|
android:layout_alignParentStart="true" |
||||
|
android:layout_alignParentBottom="true" |
||||
|
android:layout_centerVertical="true" |
||||
|
android:layout_marginTop="1dp" |
||||
|
android:layout_marginEnd="1dp" |
||||
|
android:layout_marginBottom="1dp" |
||||
|
android:padding="5dp" |
||||
|
android:src="@color/secondaryColorRed" /> |
||||
|
|
||||
|
<TextView |
||||
|
android:id="@+id/name" |
||||
|
android:layout_width="wrap_content" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_alignParentTop="true" |
||||
|
android:layout_toRightOf="@+id/imvColor" |
||||
|
android:layout_marginLeft="10dp" |
||||
|
android:layout_marginTop="24dp" |
||||
|
android:text="@string/color_header" |
||||
|
android:textSize="22dp" /> |
||||
|
|
||||
|
</RelativeLayout> |
@ -0,0 +1,10 @@ |
|||||
|
<menu 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:context="com.skulixlabs.iotcontrol.MainActivity"> |
||||
|
<item |
||||
|
android:id="@+id/action_settings" |
||||
|
android:orderInCategory="100" |
||||
|
android:title="@string/settings_header" |
||||
|
app:showAsAction="never" /> |
||||
|
</menu> |
@ -0,0 +1,5 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> |
||||
|
<background android:drawable="@drawable/ic_launcher_background" /> |
||||
|
<foreground android:drawable="@drawable/ic_launcher_foreground" /> |
||||
|
</adaptive-icon> |
@ -0,0 +1,5 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> |
||||
|
<background android:drawable="@drawable/ic_launcher_background" /> |
||||
|
<foreground android:drawable="@drawable/ic_launcher_foreground" /> |
||||
|
</adaptive-icon> |
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 4.8 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 6.7 KiB |
After Width: | Height: | Size: 6.2 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 8.9 KiB |
After Width: | Height: | Size: 15 KiB |
@ -0,0 +1,41 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<navigation 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:id="@+id/nav_graph" |
||||
|
app:startDestination="@+id/mainFragment"> |
||||
|
|
||||
|
<fragment |
||||
|
android:id="@+id/mainFragment" |
||||
|
android:name="com.skulixlabs.iotcontrol.MainFragment" |
||||
|
android:label="@string/app_name" |
||||
|
tools:layout="@layout/fragment_main"></fragment> |
||||
|
|
||||
|
<!--Settings fragment--> |
||||
|
<fragment |
||||
|
android:id="@+id/settingsFragment" |
||||
|
android:name="com.skulixlabs.iotcontrol.SettingsFragment" |
||||
|
android:label="@string/settings_header" |
||||
|
tools:layout="@layout/fragment_settings"> |
||||
|
<argument |
||||
|
android:name="importCmds" |
||||
|
app:argType="boolean" |
||||
|
android:defaultValue="false" /> |
||||
|
<action |
||||
|
android:id="@+id/action_settingsFragment_to_aboutFragment" |
||||
|
app:destination="@id/aboutFragment" /> |
||||
|
</fragment> |
||||
|
|
||||
|
<!--Global action--> |
||||
|
<action |
||||
|
android:id="@+id/action_open_settings" |
||||
|
app:destination="@id/settingsFragment" /> |
||||
|
|
||||
|
<!--About Fragment--> |
||||
|
<fragment |
||||
|
android:id="@+id/aboutFragment" |
||||
|
android:name="com.skulixlabs.iotcontrol.AboutFragment" |
||||
|
android:label="@string/about_header" |
||||
|
tools:layout="@layout/fragment_about" /> |
||||
|
|
||||
|
</navigation> |
@ -0,0 +1,24 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<resources> |
||||
|
<string name="about_header">Σχετικά με</string> |
||||
|
<string name="theme_header">Θέμα</string> |
||||
|
<string name="version">Έκδοση</string> |
||||
|
<string name="import_from_file">Εισαγωγή από αρχείο</string> |
||||
|
<string name="load_json_commands">Φόρτωση εντολών</string> |
||||
|
<string name="open_source_liceses">Άδειες ανοιχτού λογισμικού</string> |
||||
|
<string name="options_header">Επιλογές</string> |
||||
|
<string name="or_header">Ή</string> |
||||
|
<string name="paste_from_clipboard">Επικόλληση από πρόχειρο</string> |
||||
|
<string name="color_header">Χρώμα</string> |
||||
|
<string name="dark_theme_title">Σκούρο θέμα</string> |
||||
|
<string name="settings_header">Ρυθμίσεις</string> |
||||
|
<string name="request_read_external_storage_permission">Η εφαρμογή χρειάζεται άδεια πρόσβασης στα αρχεία της συσκευής.</string> |
||||
|
<string name="language_header">Γλώσσα</string> |
||||
|
<string name="option_default">Προεπιλογή</string> |
||||
|
<string name="orange">Πορτοκαλί</string> |
||||
|
<string name="red">Κόκκινο</string> |
||||
|
<string name="system_default">Προεπιλογή συστήματος</string> |
||||
|
<string name="select_commands_load_method">Επιλογή μεθόδου φόρτωσης</string> |
||||
|
<string name="load">Φόρτωση</string> |
||||
|
<string name="invalid_commands">Μη έγκυρες εντολές</string> |
||||
|
</resources> |
@ -0,0 +1,86 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<resources> |
||||
|
|
||||
|
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar"> |
||||
|
<item name="colorPrimary">@color/colorPrimaryNight</item> |
||||
|
<!--<item name="colorPrimaryDark">@color/colorPrimaryDarkNight</item>--> |
||||
|
<item name="colorAccent">@color/colorAccentNight</item> |
||||
|
|
||||
|
<!--<item name="actionBarStyle">@style/AppTheme.MyActionBar</item>--> |
||||
|
|
||||
|
<!-- Primary color is applied as text color of modal buttons. |
||||
|
Overriding dark mode colors for better user experience --> |
||||
|
<!-- <item name="alertDialogTheme">@style/AlertDialogTheme</item>--> |
||||
|
</style> |
||||
|
|
||||
|
<style name="AppTheme.RED" parent="AppTheme.NoActionBar"> |
||||
|
<item name="colorPrimary">@color/primaryColorRedNight</item> |
||||
|
<!--<item name="colorPrimaryDark">@color/primaryDarkColorRedNight</item>--> |
||||
|
<item name="colorAccent">@color/secondaryColorRedNight</item> |
||||
|
</style> |
||||
|
|
||||
|
<style name="AppTheme.ORANGE" parent="AppTheme.NoActionBar"> |
||||
|
<item name="colorPrimary">@color/primaryColorOrangeNight</item> |
||||
|
<!--<item name="colorPrimaryDark">@color/primaryDarkColorOrangeNight</item>--> |
||||
|
<item name="colorAccent">@color/secondaryColorOrangeNight</item> |
||||
|
</style> |
||||
|
|
||||
|
<style name="AlertDialogTheme" parent="ThemeOverlay.MaterialComponents.Dialog.Alert"> |
||||
|
<item name="buttonBarNegativeButtonStyle">@style/NegativeButtonStyle</item> |
||||
|
<item name="buttonBarPositiveButtonStyle">@style/PositiveButtonStyle</item> |
||||
|
</style> |
||||
|
|
||||
|
<style name="NegativeButtonStyle" parent="Widget.MaterialComponents.Button.TextButton.Dialog"> |
||||
|
<item name="android:textColor">#ef9a9a</item> |
||||
|
</style> |
||||
|
|
||||
|
<style name="PositiveButtonStyle" parent="Widget.MaterialComponents.Button.TextButton.Dialog"> |
||||
|
<item name="android:textColor">#b0bec5</item> |
||||
|
</style> |
||||
|
|
||||
|
<style name="ToolbarTheme" parent="@style/ThemeOverlay.MaterialComponents.Dark.ActionBar"> |
||||
|
<!-- android:textColorPrimary is the color of the title text in the Toolbar --> |
||||
|
<!--<item name="android:textColorPrimary">@android:color/holo_blue_light</item>--> |
||||
|
<!-- actionMenuTextColor/textColor is the color of the text of action (menu) items --> |
||||
|
<!--<item name="actionMenuTextColor">@android:color/holo_green_light</item>--> |
||||
|
<item name="android:textColor">@color/white</item> |
||||
|
<!-- Tints the input fields like checkboxes and text fields --> |
||||
|
<!--<item name="colorAccent">#000000</item>--> |
||||
|
<!-- Applies to views in their normal state. --> |
||||
|
<!--<item name="colorControlNormal">#000000</item>--> |
||||
|
<!-- Applies to views in their activated state (i.e checked or switches) --> |
||||
|
<!--<item name="colorControlActivated">#000000</item>--> |
||||
|
<!-- Applied to framework control highlights (i.e ripples or list selectors) --> |
||||
|
<!--<item name="colorControlHighlight">#000000</item>--> |
||||
|
|
||||
|
<!-- Enable these below if you want clicking icons to trigger a ripple effect --> |
||||
|
<!-- |
||||
|
<item name="selectableItemBackground">?android:selectableItemBackground</item> |
||||
|
<item name="selectableItemBackgroundBorderless">?android:selectableItemBackground</item> |
||||
|
--> |
||||
|
</style> |
||||
|
|
||||
|
<!--<style name="AppTheme." parent="ThemeOverlay.MaterialComponents.Dialog.Alert"> |
||||
|
<item name="" |
||||
|
</style>--> |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
<style name="AppTheme.MyActionBar" |
||||
|
parent="@style/Widget.AppCompat.Light.ActionBar.Solid.Inverse"> |
||||
|
<item name="android:background">@color/colorPrimaryNight</item> |
||||
|
<item name="android:textColor">#000</item> |
||||
|
<!-- Support library compatibility --> |
||||
|
<item name="background">@color/colorPrimaryNight</item> |
||||
|
</style> |
||||
|
|
||||
|
|
||||
|
<!--<style name="AppTheme.MyActionBar" |
||||
|
parent="Theme.MaterialComponents.DayNight.DarkActionBar"> |
||||
|
<item name="android:background">@color/colorPrimary</item> |
||||
|
<!– Support library compatibility –> |
||||
|
<item name="background">@color/colorPrimary</item> |
||||
|
</style>--> |
||||
|
|
||||
|
</resources> |
@ -0,0 +1,27 @@ |
|||||
|
<resources> |
||||
|
<!-- Settings Language Preference --> |
||||
|
<string-array name="language_list"> |
||||
|
<item>@string/system_default</item> |
||||
|
<item>English</item> |
||||
|
<item>Ελληνικά</item> |
||||
|
</string-array> |
||||
|
|
||||
|
<string-array name="language_list_values"> |
||||
|
<item>system_default</item> |
||||
|
<item>en_US</item> |
||||
|
<item>el_GR</item> |
||||
|
</string-array> |
||||
|
|
||||
|
<!-- Theme Color Preference --> |
||||
|
<string-array name="theme_color_list"> |
||||
|
<item>@string/option_default</item> |
||||
|
<item>@string/red</item> |
||||
|
<item>@string/orange</item> |
||||
|
</string-array> |
||||
|
|
||||
|
<string-array name="theme_color_list_values"> |
||||
|
<item>default</item> |
||||
|
<item>red</item> |
||||
|
<item>orange</item> |
||||
|
</string-array> |
||||
|
</resources> |
@ -0,0 +1,27 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<resources> |
||||
|
<color name="colorPrimary">#008577</color> |
||||
|
<color name="colorPrimaryDark">#00574B</color> |
||||
|
<color name="colorAccent">#D81B60</color> |
||||
|
|
||||
|
<color name="colorPrimaryNight">#616161</color> |
||||
|
<color name="colorPrimaryDarkNight">#000000</color> |
||||
|
<color name="colorAccentNight">#c1d5e0</color> |
||||
|
|
||||
|
<color name="primaryColorRed">#d32f2f</color> |
||||
|
<color name="primaryDarkColorRed">#9a0007</color> |
||||
|
<color name="secondaryColorRed">#ff6659</color> |
||||
|
<color name="primaryColorRedNight">#ef9a9a</color> |
||||
|
<color name="primaryDarkColorRedNight">#ba6b6c</color> |
||||
|
<color name="secondaryColorRedNight">#ffcccb</color> |
||||
|
|
||||
|
<color name="primaryColorOrange">#f57c00</color> |
||||
|
<color name="primaryDarkColorOrange">#bb4d00</color> |
||||
|
<color name="secondaryColorOrange">#ffad42</color> |
||||
|
<color name="primaryColorOrangeNight">#ffcc80</color> |
||||
|
<color name="primaryDarkColorOrangeNight">#ca9b52</color> |
||||
|
<color name="secondaryColorOrangeNight">#ffffb0</color> |
||||
|
|
||||
|
<color name="white">#FFFFFF</color> |
||||
|
<color name="transparent">#80000000</color> |
||||
|
</resources> |
@ -0,0 +1,5 @@ |
|||||
|
<resources> |
||||
|
<dimen name="fab_margin">16dp</dimen> |
||||
|
|
||||
|
<dimen name="material_button_rounded_radius">10dp</dimen> |
||||
|
</resources> |
@ -0,0 +1,46 @@ |
|||||
|
<resources> |
||||
|
<string name="app_name" translatable="false">IoT Control</string> |
||||
|
<string name="settings_header">Settings</string> |
||||
|
|
||||
|
<!-- Preference Titles --> |
||||
|
<string name="options_header">Options</string> |
||||
|
<string name="theme_header">Theme</string> |
||||
|
<string name="color_header">Color</string> |
||||
|
|
||||
|
<!-- Options Preferences --> |
||||
|
<!--<string name="ip_address_port_title">IP Address and port</string> |
||||
|
<string name="hmac_key">Secret key</string> |
||||
|
<string name="hash_byte_length_key">Byte Length</string> |
||||
|
<string name="rand_data_length_key">Random Data Length</string>--> |
||||
|
<string name="load_json_commands">Load Commands</string> |
||||
|
|
||||
|
<!-- Theme Preferences --> |
||||
|
<string name="dark_theme_title">Dark theme</string> |
||||
|
|
||||
|
<!-- Sync Preferences --> |
||||
|
<!--<string name="sync_title">Sync email periodically</string> |
||||
|
<string name="attachment_title">Download incoming attachments</string> |
||||
|
<string name="attachment_summary_on">Automatically download attachments for incoming emails</string> |
||||
|
<string name="attachment_summary_off">Only download attachments when manually requested</string>--> |
||||
|
|
||||
|
<!-- JSON Dialog --> |
||||
|
<string name="request_read_external_storage_permission">This application needs permission to access files in your phone.</string> |
||||
|
<string name="version">Version</string> |
||||
|
<string name="about_header">About</string> |
||||
|
<string name="skulix_labs" translatable="false">Skulix Labs</string> |
||||
|
<string name="open_source_liceses">Open Source Licenses</string> |
||||
|
<string name="invalid_commands">Commands were not valid</string> |
||||
|
<string name="load">Load</string> |
||||
|
<string name="select_commands_load_method">Select load method</string> |
||||
|
<string name="import_from_file">Import from file</string> |
||||
|
<string name="paste_from_clipboard">Paste from clipboard</string> |
||||
|
<string name="or_header">OR</string> |
||||
|
<string name="language_header">Language</string> |
||||
|
|
||||
|
<!-- Option dialogs - Sparse arrays --> |
||||
|
<string name="system_default">System default</string> |
||||
|
<string name="option_default">Default</string> |
||||
|
<string name="red">Red</string> |
||||
|
<string name="orange">Orange</string> |
||||
|
|
||||
|
</resources> |
@ -0,0 +1,46 @@ |
|||||
|
<resources> |
||||
|
|
||||
|
<!-- Base application theme. --> |
||||
|
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar"> |
||||
|
<!-- Customize your theme here. --> |
||||
|
<item name="colorPrimary">@color/colorPrimary</item> |
||||
|
<item name="colorPrimaryDark">@color/colorPrimaryDark</item> |
||||
|
<item name="colorAccent">@color/colorAccent</item> |
||||
|
</style> |
||||
|
|
||||
|
<style name="ToolbarTheme" parent="@style/ThemeOverlay.MaterialComponents.ActionBar"> |
||||
|
|
||||
|
</style> |
||||
|
|
||||
|
<!-- App theme without action bar --> |
||||
|
<style name="AppTheme.NoActionBar"> |
||||
|
<item name="windowActionBar">false</item> |
||||
|
<item name="windowNoTitle">true</item> |
||||
|
</style> |
||||
|
|
||||
|
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" /> |
||||
|
|
||||
|
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" /> |
||||
|
|
||||
|
<style name="AppTheme.RED" parent="AppTheme.NoActionBar"> |
||||
|
<item name="colorPrimary">@color/primaryColorRed</item> |
||||
|
<item name="colorPrimaryDark">@color/primaryDarkColorRed</item> |
||||
|
<item name="colorAccent">@color/secondaryColorRed</item> |
||||
|
</style> |
||||
|
|
||||
|
<style name="AppTheme.ORANGE" parent="AppTheme.NoActionBar"> |
||||
|
<item name="colorPrimary">@color/primaryColorOrange</item> |
||||
|
<item name="colorPrimaryDark">@color/primaryDarkColorOrange</item> |
||||
|
<item name="colorAccent">@color/secondaryColorOrange</item> |
||||
|
</style> |
||||
|
|
||||
|
<!-- <style name="mybtn" parent="@style/Widget.AppCompat.Button.Colored"> |
||||
|
<item name="android:colorButtonNormal">@color/primaryColorOrange</item> |
||||
|
<item name="android:textColor">@color/primaryColorOrange</item> |
||||
|
</style>--> |
||||
|
|
||||
|
<style name="mybtn" parent="Widget.MaterialComponents.Button"> |
||||
|
<item name="backgroundTint">?attr/colorPrimary</item> |
||||
|
</style> |
||||
|
|
||||
|
</resources> |
@ -0,0 +1,62 @@ |
|||||
|
<!-- |
||||
|
~ Copyright 2018 The app Open Source Project |
||||
|
~ |
||||
|
~ Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
~ you may not use this file except in compliance with the License. |
||||
|
~ You may obtain a copy of the License at |
||||
|
~ |
||||
|
~ http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
~ |
||||
|
~ Unless required by applicable law or agreed to in writing, software |
||||
|
~ distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
~ See the License for the specific language governing permissions and |
||||
|
~ limitations under the License. |
||||
|
--> |
||||
|
|
||||
|
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto" |
||||
|
xmlns:android="http://schemas.android.com/apk/res/android"> |
||||
|
|
||||
|
<PreferenceCategory app:title="@string/options_header"> |
||||
|
|
||||
|
<Preference |
||||
|
app:key="load_json_commands" |
||||
|
app:title="@string/load_json_commands" |
||||
|
app:summary="@string/select_commands_load_method"/> |
||||
|
|
||||
|
<!--<ListPreference |
||||
|
app:defaultValue="system_default" |
||||
|
app:entries="@array/language_list" |
||||
|
app:entryValues="@array/language_list_values" |
||||
|
app:key="language" |
||||
|
app:title="@string/language_header" |
||||
|
app:useSimpleSummaryProvider="true" />--> |
||||
|
|
||||
|
</PreferenceCategory> |
||||
|
|
||||
|
<PreferenceCategory app:title="@string/theme_header"> |
||||
|
|
||||
|
<SwitchPreferenceCompat |
||||
|
app:key="dark_theme" |
||||
|
app:title="@string/dark_theme_title" /> |
||||
|
|
||||
|
<ListPreference |
||||
|
app:defaultValue="default" |
||||
|
app:entries="@array/theme_color_list" |
||||
|
app:entryValues="@array/theme_color_list_values" |
||||
|
app:key="theme_color" |
||||
|
app:title="@string/color_header" |
||||
|
app:useSimpleSummaryProvider="true" /> |
||||
|
|
||||
|
</PreferenceCategory> |
||||
|
|
||||
|
<PreferenceCategory app:title="@string/about_header"> |
||||
|
|
||||
|
<Preference |
||||
|
app:key="version" |
||||
|
app:title="@string/version" |
||||
|
app:summary="@string/appVersion"/> |
||||
|
|
||||
|
</PreferenceCategory> |
||||
|
|
||||
|
</PreferenceScreen> |
@ -0,0 +1,17 @@ |
|||||
|
package com.skulixlabs.iotcontrol; |
||||
|
|
||||
|
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); |
||||
|
} |
||||
|
} |
@ -0,0 +1,31 @@ |
|||||
|
// Top-level build file where you can add configuration options common to all sub-projects/modules. |
||||
|
|
||||
|
buildscript { |
||||
|
repositories { |
||||
|
google() |
||||
|
jcenter() |
||||
|
|
||||
|
} |
||||
|
dependencies { |
||||
|
def nav_version = "2.1.0" |
||||
|
|
||||
|
classpath 'com.android.tools.build:gradle:3.5.1' |
||||
|
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version" |
||||
|
classpath 'com.jaredsburrows:gradle-license-plugin:0.8.5' |
||||
|
|
||||
|
// NOTE: Do not place your application dependencies here; they belong |
||||
|
// in the individual module build.gradle files |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
allprojects { |
||||
|
repositories { |
||||
|
google() |
||||
|
jcenter() |
||||
|
maven { url 'https://jitpack.io' } |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
task clean(type: Delete) { |
||||
|
delete rootProject.buildDir |
||||
|
} |
@ -0,0 +1,17 @@ |
|||||
|
# Project-wide Gradle settings. |
||||
|
# IDE (e.g. Android Studio) users: |
||||
|
# Gradle settings configured through the IDE *will override* |
||||
|
# any settings specified in this file. |
||||
|
# For more details on how to configure your build environment visit |
||||
|
# http://www.gradle.org/docs/current/userguide/build_environment.html |
||||
|
# Specifies the JVM arguments used for the daemon process. |
||||
|
# The setting is particularly useful for tweaking memory settings. |
||||
|
android.enableJetifier=true |
||||
|
android.useAndroidX=true |
||||
|
org.gradle.jvmargs=-Xmx1536m |
||||
|
# When configured, Gradle will run in incubating parallel mode. |
||||
|
# 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 |
||||
|
# org.gradle.parallel=true |
||||
|
|
||||
|
|
@ -0,0 +1,6 @@ |
|||||
|
#Wed Sep 25 21:02:35 EEST 2019 |
||||
|
distributionBase=GRADLE_USER_HOME |
||||
|
distributionPath=wrapper/dists |
||||
|
zipStoreBase=GRADLE_USER_HOME |
||||
|
zipStorePath=wrapper/dists |
||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip |
@ -0,0 +1,172 @@ |
|||||
|
#!/usr/bin/env sh |
||||
|
|
||||
|
############################################################################## |
||||
|
## |
||||
|
## Gradle start up script for UN*X |
||||
|
## |
||||
|
############################################################################## |
||||
|
|
||||
|
# Attempt to set APP_HOME |
||||
|
# Resolve links: $0 may be a link |
||||
|
PRG="$0" |
||||
|
# Need this for relative symlinks. |
||||
|
while [ -h "$PRG" ] ; do |
||||
|
ls=`ls -ld "$PRG"` |
||||
|
link=`expr "$ls" : '.*-> \(.*\)$'` |
||||
|
if expr "$link" : '/.*' > /dev/null; then |
||||
|
PRG="$link" |
||||
|
else |
||||
|
PRG=`dirname "$PRG"`"/$link" |
||||
|
fi |
||||
|
done |
||||
|
SAVED="`pwd`" |
||||
|
cd "`dirname \"$PRG\"`/" >/dev/null |
||||
|
APP_HOME="`pwd -P`" |
||||
|
cd "$SAVED" >/dev/null |
||||
|
|
||||
|
APP_NAME="Gradle" |
||||
|
APP_BASE_NAME=`basename "$0"` |
||||
|
|
||||
|
# Add default JVM optionsOld here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM optionsOld to this script. |
||||
|
DEFAULT_JVM_OPTS="" |
||||
|
|
||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value. |
||||
|
MAX_FD="maximum" |
||||
|
|
||||
|
warn () { |
||||
|
echo "$*" |
||||
|
} |
||||
|
|
||||
|
die () { |
||||
|
echo |
||||
|
echo "$*" |
||||
|
echo |
||||
|
exit 1 |
||||
|
} |
||||
|
|
||||
|
# OS specific support (must be 'true' or 'false'). |
||||
|
cygwin=false |
||||
|
msys=false |
||||
|
darwin=false |
||||
|
nonstop=false |
||||
|
case "`uname`" in |
||||
|
CYGWIN* ) |
||||
|
cygwin=true |
||||
|
;; |
||||
|
Darwin* ) |
||||
|
darwin=true |
||||
|
;; |
||||
|
MINGW* ) |
||||
|
msys=true |
||||
|
;; |
||||
|
NONSTOP* ) |
||||
|
nonstop=true |
||||
|
;; |
||||
|
esac |
||||
|
|
||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar |
||||
|
|
||||
|
# Determine the Java command to use to start the JVM. |
||||
|
if [ -n "$JAVA_HOME" ] ; then |
||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then |
||||
|
# IBM's JDK on AIX uses strange locations for the executables |
||||
|
JAVACMD="$JAVA_HOME/jre/sh/java" |
||||
|
else |
||||
|
JAVACMD="$JAVA_HOME/bin/java" |
||||
|
fi |
||||
|
if [ ! -x "$JAVACMD" ] ; then |
||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME |
||||
|
|
||||
|
Please set the JAVA_HOME variable in your environment to match the |
||||
|
location of your Java installation." |
||||
|
fi |
||||
|
else |
||||
|
JAVACMD="java" |
||||
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. |
||||
|
|
||||
|
Please set the JAVA_HOME variable in your environment to match the |
||||
|
location of your Java installation." |
||||
|
fi |
||||
|
|
||||
|
# Increase the maximum file descriptors if we can. |
||||
|
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then |
||||
|
MAX_FD_LIMIT=`ulimit -H -n` |
||||
|
if [ $? -eq 0 ] ; then |
||||
|
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then |
||||
|
MAX_FD="$MAX_FD_LIMIT" |
||||
|
fi |
||||
|
ulimit -n $MAX_FD |
||||
|
if [ $? -ne 0 ] ; then |
||||
|
warn "Could not set maximum file descriptor limit: $MAX_FD" |
||||
|
fi |
||||
|
else |
||||
|
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" |
||||
|
fi |
||||
|
fi |
||||
|
|
||||
|
# For Darwin, add optionsOld to specify how the application appears in the dock |
||||
|
if $darwin; then |
||||
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" |
||||
|
fi |
||||
|
|
||||
|
# For Cygwin, switch paths to Windows format before running java |
||||
|
if $cygwin ; then |
||||
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"` |
||||
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` |
||||
|
JAVACMD=`cygpath --unix "$JAVACMD"` |
||||
|
|
||||
|
# We build the pattern for arguments to be converted via cygpath |
||||
|
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` |
||||
|
SEP="" |
||||
|
for dir in $ROOTDIRSRAW ; do |
||||
|
ROOTDIRS="$ROOTDIRS$SEP$dir" |
||||
|
SEP="|" |
||||
|
done |
||||
|
OURCYGPATTERN="(^($ROOTDIRS))" |
||||
|
# Add a user-defined pattern to the cygpath arguments |
||||
|
if [ "$GRADLE_CYGPATTERN" != "" ] ; then |
||||
|
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" |
||||
|
fi |
||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh |
||||
|
i=0 |
||||
|
for arg in "$@" ; do |
||||
|
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` |
||||
|
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option |
||||
|
|
||||
|
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition |
||||
|
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` |
||||
|
else |
||||
|
eval `echo args$i`="\"$arg\"" |
||||
|
fi |
||||
|
i=$((i+1)) |
||||
|
done |
||||
|
case $i in |
||||
|
(0) set -- ;; |
||||
|
(1) set -- "$args0" ;; |
||||
|
(2) set -- "$args0" "$args1" ;; |
||||
|
(3) set -- "$args0" "$args1" "$args2" ;; |
||||
|
(4) set -- "$args0" "$args1" "$args2" "$args3" ;; |
||||
|
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; |
||||
|
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; |
||||
|
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; |
||||
|
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; |
||||
|
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; |
||||
|
esac |
||||
|
fi |
||||
|
|
||||
|
# Escape application args |
||||
|
save () { |
||||
|
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done |
||||
|
echo " " |
||||
|
} |
||||
|
APP_ARGS=$(save "$@") |
||||
|
|
||||
|
# Collect all arguments for the java command, following the shell quoting and substitution rules |
||||
|
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" |
||||
|
|
||||
|
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong |
||||
|
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then |
||||
|
cd "$(dirname "$0")" |
||||
|
fi |
||||
|
|
||||
|
exec "$JAVACMD" "$@" |
@ -0,0 +1,84 @@ |
|||||
|
@if "%DEBUG%" == "" @echo off |
||||
|
@rem ########################################################################## |
||||
|
@rem |
||||
|
@rem Gradle startup script for Windows |
||||
|
@rem |
||||
|
@rem ########################################################################## |
||||
|
|
||||
|
@rem Set local scope for the variables with windows NT shell |
||||
|
if "%OS%"=="Windows_NT" setlocal |
||||
|
|
||||
|
set DIRNAME=%~dp0 |
||||
|
if "%DIRNAME%" == "" set DIRNAME=. |
||||
|
set APP_BASE_NAME=%~n0 |
||||
|
set APP_HOME=%DIRNAME% |
||||
|
|
||||
|
@rem Add default JVM optionsOld here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM optionsOld to this script. |
||||
|
set DEFAULT_JVM_OPTS= |
||||
|
|
||||
|
@rem Find java.exe |
||||
|
if defined JAVA_HOME goto findJavaFromJavaHome |
||||
|
|
||||
|
set JAVA_EXE=java.exe |
||||
|
%JAVA_EXE% -version >NUL 2>&1 |
||||
|
if "%ERRORLEVEL%" == "0" goto init |
||||
|
|
||||
|
echo. |
||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. |
||||
|
echo. |
||||
|
echo Please set the JAVA_HOME variable in your environment to match the |
||||
|
echo location of your Java installation. |
||||
|
|
||||
|
goto fail |
||||
|
|
||||
|
:findJavaFromJavaHome |
||||
|
set JAVA_HOME=%JAVA_HOME:"=% |
||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe |
||||
|
|
||||
|
if exist "%JAVA_EXE%" goto init |
||||
|
|
||||
|
echo. |
||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% |
||||
|
echo. |
||||
|
echo Please set the JAVA_HOME variable in your environment to match the |
||||
|
echo location of your Java installation. |
||||
|
|
||||
|
goto fail |
||||
|
|
||||
|
:init |
||||
|
@rem Get command-line arguments, handling Windows variants |
||||
|
|
||||
|
if not "%OS%" == "Windows_NT" goto win9xME_args |
||||
|
|
||||
|
:win9xME_args |
||||
|
@rem Slurp the command line arguments. |
||||
|
set CMD_LINE_ARGS= |
||||
|
set _SKIP=2 |
||||
|
|
||||
|
:win9xME_args_slurp |
||||
|
if "x%~1" == "x" goto execute |
||||
|
|
||||
|
set CMD_LINE_ARGS=%* |
||||
|
|
||||
|
:execute |
||||
|
@rem Setup the command line |
||||
|
|
||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar |
||||
|
|
||||
|
@rem Execute Gradle |
||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% |
||||
|
|
||||
|
:end |
||||
|
@rem End local scope for the variables with windows NT shell |
||||
|
if "%ERRORLEVEL%"=="0" goto mainEnd |
||||
|
|
||||
|
:fail |
||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of |
||||
|
rem the _cmd.exe /c_ return code! |
||||
|
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 |
||||
|
exit /b 1 |
||||
|
|
||||
|
:mainEnd |
||||
|
if "%OS%"=="Windows_NT" endlocal |
||||
|
|
||||
|
:omega |
@ -0,0 +1 @@ |
|||||
|
include ':app' |