add project adins

This commit is contained in:
Alfrid Sanjaya Leo Putra 2024-07-25 14:44:22 +07:00
commit f8f85d679d
5299 changed files with 625430 additions and 0 deletions

View file

@ -0,0 +1 @@
/build

View file

@ -0,0 +1,88 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
android {
compileSdkVersion 30
buildToolsVersion '29.0.2'
android.packageBuildConfig = false
defaultConfig {
minSdkVersion 19
targetSdkVersion 30
versionCode 2
versionName "1.0"
useLibrary 'org.apache.http.legacy'
multiDexEnabled true
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
}
dataBinding {
enabled = true
}
buildTypes {
release {
shrinkResources false
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
minifyEnabled false
shrinkResources false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
dexOptions {
javaMaxHeapSize "5g"
}
testOptions {
unitTests.returnDefaultValues = true
}
}
dependencies {
api fileTree(include: '*.jar', dir: 'libs')
api group: 'com.zebra', name: 'android-sdk', version: '1.0'
api 'com.google.android.gms:play-services-location:17.1.0'
api 'com.google.android.gms:play-services-maps:17.0.0'
api 'com.google.firebase:firebase-messaging:20.2.4'
api 'com.google.firebase:firebase-core:17.5.0'
api 'com.google.firebase:firebase-storage:19.2.0'
api 'com.google.firebase:firebase-database:19.4.0'
api 'com.google.firebase:firebase-perf:19.1.1'
api 'com.google.firebase:firebase-analytics:17.5.0'
api 'com.google.firebase:firebase-crashlytics:17.2.1'
api 'androidx.appcompat:appcompat:1.0.0'
// implementation 'com.android.support:support-annotations:28.0.0'
api 'com.google.android.material:material:1.0.0'
api 'androidx.recyclerview:recyclerview:1.0.0'
api 'androidx.cardview:cardview:1.0.0'
api 'androidx.legacy:legacy-support-v4:1.0.0'
api 'androidx.constraintlayout:constraintlayout:1.1.3'
api 'jp.wasabeef:recyclerview-animators:2.2.7'
api 'androidx.multidex:multidex:2.0.0'
api 'de.greenrobot:eventbus:2.4.0'
api 'org.greenrobot:greendao-encryption:2.2.2'
api 'net.zetetic:android-database-sqlcipher:4.2.0'
api 'de.hdodenhof:circleimageview:2.2.0'
api files('libs/PaxNeptuneLiteApi_V2.01.00_20171025.jar')
api files('libs/PaxNeptuneApi_V1.00.01_20161130.jar')
api "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
testImplementation 'junit:junit:4.12'
api(name:'material-showcase', ext:'aar')
// Espresso UI Testing
// androidtestImplementation "com.android.support.test.espresso:espresso-core:3.0.2"
// androidtestImplementation 'com.android.support.test:rules:1.0.2'
// androidtestImplementation 'com.android.support.test:runner:1.0.2'
testImplementation 'org.mockito:mockito-core:2.7.22'
testImplementation 'org.powermock:powermock-core:1.7.0RC2'
testImplementation 'org.powermock:powermock-module-junit4:1.7.0RC2'
testImplementation 'org.powermock:powermock-api-mockito2:1.7.0RC2'
androidTestImplementation 'org.mockito:mockito-android:2.7.22'
implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0'
}
apply plugin: 'com.google.gms.google-services'
apply plugin: 'kotlin-android-extensions'

View file

@ -0,0 +1,585 @@
{
"project_info": {
"project_number": "814888014770",
"firebase_url": "https://aitmss-1fd04.firebaseio.com",
"project_id": "aitmss-1fd04",
"storage_bucket": "aitmss-1fd04.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:814888014770:android:30c443717ea63b1d",
"android_client_info": {
"package_name": "com.adins.mss.coll"
}
},
"oauth_client": [
{
"client_id": "814888014770-3mnsl7ja8ddiuv5sl5gqfvij9cm973na.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "com.adins.mss.coll",
"certificate_hash": "b07630f358413826df1dceeff13dec3cc7642e36"
}
},
{
"client_id": "814888014770-o9drphjl20de1nbt9bd1tbuj08musiis.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "com.adins.mss.coll",
"certificate_hash": "edf00b52879e51d97f85961fd196240bbd316220"
}
},
{
"client_id": "814888014770-1crpacj0ttvkjeimakn6qe6pmhu3qeq7.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "com.adins.mss.coll",
"certificate_hash": "e831f754571d0287b1577c64c2f9da80dca8a28d"
}
},
{
"client_id": "814888014770-lqni1uhtl2omhj2ghl8ennrlpdtgchgi.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "com.adins.mss.coll",
"certificate_hash": "781f237f99355c079785379c9bb39d0c6f036fba"
}
},
{
"client_id": "814888014770-qgllsrlpktvi162r6uqae4on3qtg0ap2.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyC6ybt31nRknv-l0dzR69vZZGPoSeHkAm8"
},
{
"current_key": "AIzaSyDmV5RbC4xQFgzhTUAQMHUSbtFdl8wduII"
}
],
"services": {
"analytics_service": {
"status": 1
},
"appinvite_service": {
"status": 2,
"other_platform_oauth_client": [
{
"client_id": "814888014770-qgllsrlpktvi162r6uqae4on3qtg0ap2.apps.googleusercontent.com",
"client_type": 3
}
]
},
"ads_service": {
"status": 2
}
}
},
{
"client_info": {
"mobilesdk_app_id": "1:814888014770:android:5c334278ae3d4dbd",
"android_client_info": {
"package_name": "com.adins.mss.coll.dev"
}
},
"oauth_client": [
{
"client_id": "814888014770-qgllsrlpktvi162r6uqae4on3qtg0ap2.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyC6ybt31nRknv-l0dzR69vZZGPoSeHkAm8"
},
{
"current_key": "AIzaSyDmV5RbC4xQFgzhTUAQMHUSbtFdl8wduII"
}
],
"services": {
"analytics_service": {
"status": 1
},
"appinvite_service": {
"status": 1,
"other_platform_oauth_client": []
},
"ads_service": {
"status": 2
}
}
},
{
"client_info": {
"mobilesdk_app_id": "1:814888014770:android:62ea46ede52e350b",
"android_client_info": {
"package_name": "com.adins.mss.coll.trial"
}
},
"oauth_client": [
{
"client_id": "814888014770-qgllsrlpktvi162r6uqae4on3qtg0ap2.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyC6ybt31nRknv-l0dzR69vZZGPoSeHkAm8"
},
{
"current_key": "AIzaSyDmV5RbC4xQFgzhTUAQMHUSbtFdl8wduII"
}
],
"services": {
"analytics_service": {
"status": 1
},
"appinvite_service": {
"status": 1,
"other_platform_oauth_client": []
},
"ads_service": {
"status": 2
}
}
},
{
"client_info": {
"mobilesdk_app_id": "1:814888014770:android:6168d59a3ce39f25",
"android_client_info": {
"package_name": "com.adins.mss.svy"
}
},
"oauth_client": [
{
"client_id": "814888014770-5f1768ika3hs4ttrek8jmkmf5rahfi1l.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "com.adins.mss.svy",
"certificate_hash": "b07630f358413826df1dceeff13dec3cc7642e36"
}
},
{
"client_id": "814888014770-6jbu06k8ntt610ah06be2q8fmf8g49u6.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "com.adins.mss.svy",
"certificate_hash": "edf00b52879e51d97f85961fd196240bbd316220"
}
},
{
"client_id": "814888014770-nr69qi0ku9cgok5co187apdpgs0at6ie.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "com.adins.mss.svy",
"certificate_hash": "e831f754571d0287b1577c64c2f9da80dca8a28d"
}
},
{
"client_id": "814888014770-34mdd9bo2goldi5a5hoeh4iup8nvcdvk.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "com.adins.mss.svy",
"certificate_hash": "781f237f99355c079785379c9bb39d0c6f036fba"
}
},
{
"client_id": "814888014770-qgllsrlpktvi162r6uqae4on3qtg0ap2.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyC6ybt31nRknv-l0dzR69vZZGPoSeHkAm8"
},
{
"current_key": "AIzaSyDmV5RbC4xQFgzhTUAQMHUSbtFdl8wduII"
}
],
"services": {
"analytics_service": {
"status": 1
},
"appinvite_service": {
"status": 2,
"other_platform_oauth_client": [
{
"client_id": "814888014770-qgllsrlpktvi162r6uqae4on3qtg0ap2.apps.googleusercontent.com",
"client_type": 3
}
]
},
"ads_service": {
"status": 2
}
}
},
{
"client_info": {
"mobilesdk_app_id": "1:814888014770:android:4c9a50b93d56ebef",
"android_client_info": {
"package_name": "com.adins.mss.svy.dev"
}
},
"oauth_client": [
{
"client_id": "814888014770-qgllsrlpktvi162r6uqae4on3qtg0ap2.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyC6ybt31nRknv-l0dzR69vZZGPoSeHkAm8"
},
{
"current_key": "AIzaSyDmV5RbC4xQFgzhTUAQMHUSbtFdl8wduII"
}
],
"services": {
"analytics_service": {
"status": 1
},
"appinvite_service": {
"status": 1,
"other_platform_oauth_client": []
},
"ads_service": {
"status": 2
}
}
},
{
"client_info": {
"mobilesdk_app_id": "1:814888014770:android:48590bba4365ec2d",
"android_client_info": {
"package_name": "com.adins.mss.odr"
}
},
"oauth_client": [
{
"client_id": "814888014770-itt09df2ovn47an5rearlf1kh8ljp5tt.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "com.adins.mss.odr",
"certificate_hash": "b07630f358413826df1dceeff13dec3cc7642e36"
}
},
{
"client_id": "814888014770-09lnve4i7o6ml5436v128stk0jfu5vug.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "com.adins.mss.odr",
"certificate_hash": "edf00b52879e51d97f85961fd196240bbd316220"
}
},
{
"client_id": "814888014770-52el593n7dc31cgu431kiqes2qijklie.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "com.adins.mss.odr",
"certificate_hash": "e831f754571d0287b1577c64c2f9da80dca8a28d"
}
},
{
"client_id": "814888014770-76sm9sf0veuhc96gtiqkdinju9b4al6d.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "com.adins.mss.odr",
"certificate_hash": "781f237f99355c079785379c9bb39d0c6f036fba"
}
},
{
"client_id": "814888014770-qgllsrlpktvi162r6uqae4on3qtg0ap2.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyC6ybt31nRknv-l0dzR69vZZGPoSeHkAm8"
},
{
"current_key": "AIzaSyDmV5RbC4xQFgzhTUAQMHUSbtFdl8wduII"
}
],
"services": {
"analytics_service": {
"status": 1
},
"appinvite_service": {
"status": 2,
"other_platform_oauth_client": [
{
"client_id": "814888014770-qgllsrlpktvi162r6uqae4on3qtg0ap2.apps.googleusercontent.com",
"client_type": 3
}
]
},
"ads_service": {
"status": 2
}
}
},
{
"client_info": {
"mobilesdk_app_id": "1:814888014770:android:75bff1874bc58298",
"android_client_info": {
"package_name": "com.adins.mss.odr.dev"
}
},
"oauth_client": [
{
"client_id": "814888014770-qgllsrlpktvi162r6uqae4on3qtg0ap2.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyC6ybt31nRknv-l0dzR69vZZGPoSeHkAm8"
},
{
"current_key": "AIzaSyDmV5RbC4xQFgzhTUAQMHUSbtFdl8wduII"
}
],
"services": {
"analytics_service": {
"status": 1
},
"appinvite_service": {
"status": 1,
"other_platform_oauth_client": []
},
"ads_service": {
"status": 2
}
}
},
{
"client_info": {
"mobilesdk_app_id": "1:814888014770:android:41ac49e3a1067b63",
"android_client_info": {
"package_name": "com.adins.mss.svy.trial"
}
},
"oauth_client": [
{
"client_id": "814888014770-qgllsrlpktvi162r6uqae4on3qtg0ap2.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyC6ybt31nRknv-l0dzR69vZZGPoSeHkAm8"
},
{
"current_key": "AIzaSyDmV5RbC4xQFgzhTUAQMHUSbtFdl8wduII"
}
],
"services": {
"analytics_service": {
"status": 1
},
"appinvite_service": {
"status": 1,
"other_platform_oauth_client": []
},
"ads_service": {
"status": 2
}
}
},
{
"client_info": {
"mobilesdk_app_id": "1:814888014770:android:0056a18df222e269",
"android_client_info": {
"package_name": "com.adins.mss.odr.trial"
}
},
"oauth_client": [
{
"client_id": "814888014770-qgllsrlpktvi162r6uqae4on3qtg0ap2.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyC6ybt31nRknv-l0dzR69vZZGPoSeHkAm8"
},
{
"current_key": "AIzaSyDmV5RbC4xQFgzhTUAQMHUSbtFdl8wduII"
}
],
"services": {
"analytics_service": {
"status": 1
},
"appinvite_service": {
"status": 1,
"other_platform_oauth_client": []
},
"ads_service": {
"status": 2
}
}
},
{
"client_info": {
"mobilesdk_app_id": "1:814888014770:android:2ce43e2402f184a7",
"android_client_info": {
"package_name": "com.adins.mss.mma"
}
},
"oauth_client": [
{
"client_id": "814888014770-qgllsrlpktvi162r6uqae4on3qtg0ap2.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyC6ybt31nRknv-l0dzR69vZZGPoSeHkAm8"
},
{
"current_key": "AIzaSyDmV5RbC4xQFgzhTUAQMHUSbtFdl8wduII"
}
],
"services": {
"analytics_service": {
"status": 1
},
"appinvite_service": {
"status": 1,
"other_platform_oauth_client": []
},
"ads_service": {
"status": 2
}
}
},
{
"client_info": {
"mobilesdk_app_id": "1:814888014770:android:81af48a318e62233",
"android_client_info": {
"package_name": "com.adins.mss.mma.dev"
}
},
"oauth_client": [
{
"client_id": "814888014770-qgllsrlpktvi162r6uqae4on3qtg0ap2.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyC6ybt31nRknv-l0dzR69vZZGPoSeHkAm8"
},
{
"current_key": "AIzaSyDmV5RbC4xQFgzhTUAQMHUSbtFdl8wduII"
}
],
"services": {
"analytics_service": {
"status": 1
},
"appinvite_service": {
"status": 1,
"other_platform_oauth_client": []
},
"ads_service": {
"status": 2
}
}
},
{
"client_info": {
"mobilesdk_app_id": "1:814888014770:android:4394bdce086fcfcc",
"android_client_info": {
"package_name": "com.adins.mss.mma.trial"
}
},
"oauth_client": [
{
"client_id": "814888014770-qgllsrlpktvi162r6uqae4on3qtg0ap2.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyC6ybt31nRknv-l0dzR69vZZGPoSeHkAm8"
},
{
"current_key": "AIzaSyDmV5RbC4xQFgzhTUAQMHUSbtFdl8wduII"
}
],
"services": {
"analytics_service": {
"status": 1
},
"appinvite_service": {
"status": 1,
"other_platform_oauth_client": []
},
"ads_service": {
"status": 2
}
}
},
{
"client_info": {
"mobilesdk_app_id": "1:814888014770:android:7314503471868713",
"android_client_info": {
"package_name": "com.adins.mss.base"
}
},
"oauth_client": [
{
"client_id": "814888014770-51c5dehkiu9o90j7rdq9ke6fvlvaomtc.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "com.adins.mss.base",
"certificate_hash": "781f237f99355c079785379c9bb39d0c6f036fba"
}
},
{
"client_id": "814888014770-aqe9bq2rc57suvnangenhpfai77f4j2u.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "com.adins.mss.base",
"certificate_hash": "e831f754571d0287b1577c64c2f9da80dca8a28d"
}
},
{
"client_id": "814888014770-qgllsrlpktvi162r6uqae4on3qtg0ap2.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyC6ybt31nRknv-l0dzR69vZZGPoSeHkAm8"
},
{
"current_key": "AIzaSyDmV5RbC4xQFgzhTUAQMHUSbtFdl8wduII"
}
],
"services": {
"analytics_service": {
"status": 1
},
"appinvite_service": {
"status": 2,
"other_platform_oauth_client": [
{
"client_id": "814888014770-qgllsrlpktvi162r6uqae4on3qtg0ap2.apps.googleusercontent.com",
"client_type": 3
}
]
},
"ads_service": {
"status": 2
}
}
}
],
"configuration_version": "1"
}

View file

@ -0,0 +1,691 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in C:\Users\gigin.ginanjar\AppData\Local\Android\sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# 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 *;
#}
#-injars bin/classes
#-injars libs
##-outjars bin/classes-processed.jar
##-libraryjars "C:\Users\gigin.ginanjar\AppData\Local\Android\sdk/platforms\android-23/android.jar"
#
-dontpreverify
#-repackageclasses ''
-allowaccessmodification
-optimizations !code/simplification/arithmetic,!field
-keepattributes Signature
-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
-keep public class * extends android.app.Activity
-keep public class * extends androidx.fragment.app.Fragment
-keep public class * extends androidx.fragment.app.FragmentActivity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends androidx.appcompat.app.AppCompatActivity
-keepclassmembers class * extends android.content.Context {
public void *(android.view.View);
public void *(android.view.NewMenuItem);
}
-keepclassmembers class * implements android.os.Parcelable {
static ** CREATOR;
}
#-keepclassmembers class **.R$* {
# public static <fields>;
#}
-keep class com.adins.mss.base.R$* {
public static <fields>;
}
-keepclassmembers class ** {
public static *** parse(***);
}
-keepclassmembers class ** {
public static <fields>;
}
-keep public class com.adins.mss.logger.Logger{
public <methods>;
}
### greenDAO 3
-keepclassmembers class * extends org.greenrobot.greendao.AbstractDao {
public static java.lang.String TABLENAME;
}
-keep class **$Properties
# If you do not use SQLCipher:
-dontwarn org.greenrobot.greendao.database.**
# If you do not use RxJava:
-dontwarn rx.**
### greenDAO 2
-keepclassmembers class * extends de.greenrobot.dao.AbstractDao {
public static java.lang.String TABLENAME;
}
-keep class **$Properties
-keepclassmembers class * {
@android.webkit.JavascriptInterface <methods>;
}
-keepattributes InnerClasses
-dontwarn okio.**
-dontwarn com.androidquery.**
-dontwarn org.apache.commons.jexl2.**
-dontwarn com.fasterxml.uuid.**
-dontwarn org.bouncycastle.**
-dontwarn java.lang.**
-dontwarn fr.castorflex.android.smoothprogressbar.**
-dontwarn lib.gegemobile.**
-dontwarn uk.co.senab.actionbarpulltorefresh.library.**
-dontwarn zj.com.**
-dontwarn org.apache.**
-dontwarn com.pax.**
-dontwarn android.databinding.**
-keep class android.databinding.** { *; }
-keep class androidx.databinding.** { *; }
-keep class * extends androidx.databinding.DataBinderMapper { *; }
-keep class com.adins.mss.base.databinding.** { *; }
#Keep classes that are referenced on the AndroidManifest
-keep public class * extends androidx.appcompat.app.AppCompatActivity
-keep public class * extends com.adins.mss.base.MssFragmentActivity
-keep public class * extends androidx.fragment.app.Fragment
-keep public class * extends androidx.fragment.app.FragmentActivity
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keepclassmembers class * extends androidx.fragment.app.Fragment{
public void *(android.view.View);
}
-keep class com.androidquery.AQuery {
public protected <methods>;
public protected <fields>;
}
-keep class android.** { *; }
-keep class com.google.** { *; }
-keep class com.google.gson.JsonSyntaxException {public protected static *; }
-keep class org.acra.sender.HttpSender {
public protected <methods>;
public protected <fields>;
}
-keep class androidx.fragment.app.FragmentManager{
public protected <methods>;
public protected <fields>;
}
-keep class org.acra.annotation.** {*; }
-keep class org.acra.ReportField {*; }
-keep class org.acra.ReportingInteractionMode {*; }
-keep class org.acra.annotation.ReportsCrashes {public protected static *; }
-keep class org.acra.ErrorReporter {
public protected <methods>;
public protected <fields>;
}
-keep class java.lang.String {
public protected <methods>;
public protected <fields>;
}
-keep public enum org.acra.sender.HttpSender.Type$** {
*;
}
-keep public enum org.acra.sender.HttpSender.Method$** {
*;
}
-keepclassmembers,allowoptimization enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keep class org.acra.annotation.** {*; }
-keep class org.acra.ReportField {*; }
-keep class org.acra.ReportingInteractionMode {*; }
-keep class org.acra.annotation.ReportsCrashes {public protected static *; }
-keep class org.acra.ErrorReporter {
public protected <methods>;
public protected <fields>;
}
-keep class java.lang.String {
public protected <methods>;
public protected <fields>;
}
-keepclassmembers,allowoptimization enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
#keep class for mss
#-keep class com.adins.foundation.**
-keep class com.adins.mss.dao.** {*;}
-keep class com.adins.mss.constant.** {*;}
-keep class com.adins.mss.base.crashlytics.** {*;}
-keep class com.adins.mss.foundation.** {*;}
-keep class com.adins.mss.foundation.db.dataaccess.** {*;}
-keep class com.adins.mss.base.login.DefaultLoginModel
-keep class com.squareup.okhttp.** {*;}
-keep class com.androidquery.** {*;}
-keep class android.view.animation.** {*;}
-keep class com.adins.mss.base.todolist.todayplanrepository.** {*;}
#Nendi: 17.12.2020
-keep class com.adins.mss.foundation.security.storepreferences.ObscuredSharedPreferences {*;}
-keepclasseswithmembernames class com.adins.mss.base.commons.SecondHelper
-keep public class com.gadberry.** {*;}
-keep public class * extends android.view.View {
public <init>(android.content.Context);
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
public void set*(...);
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet);
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keep public class com.adins.mss.base.util.Utility{
public <methods>;
}
-keep class com.adins.mss.base.dynamictheme.DynamicTheme{
public <methods>;
}
-keep class com.soundcloud.android.crop.Crop{
public <methods>;
public public <fields>;
}
-keep class com.adins.mss.base.dynamictheme.ThemeLoader{
public <methods>;
public public <fields>;
}
-keep public interface com.adins.mss.base.dynamictheme.ThemeLoader$ColorSetLoaderCallback {*;}
-keep class com.adins.mss.base.dynamictheme.ThemeUtility{
public <methods>;
}
-keep public class com.adins.mss.base.util.EventBusHelper{
public <methods>;
}
-keep public class com.adins.mss.base.util.LocaleHelper{
public <methods>;
}
-keep public class com.adins.mss.base.checkin.CheckInManager{
public <methods>;
}
-keep public class com.adins.mss.base.errorhandler.ErrorMessageHandler{
public <methods>;
}
-keep public class com.adins.mss.base.errorhandler.IShowError{
public <methods>;
}
-keep class net.sqlcipher.** {
*;
}
-keep class com.zebra.** { *; }
-keep interface com.zebra.** { *; }
-keep class com.fasterxml.** { *; }
-keep interface com.fasterxml.** { *; }
-keep interface com.adins.libs.** { *; }
-keep class uk.co.deanwild.materialshowcaseview.** { *; }
-keep interface uk.co.deanwild.materialshowcaseview.** { *; }
-keep class com.adins.mss.base.commons.CommonImpl{
public <methods>;
}
-keep class com.adins.mss.base.tasklog.TaskLogImpl{
public <methods>;
}
-keep class com.adins.mss.base.commons.ViewImpl{
public <methods>;
}
-keep class com.adins.mss.base.commons.TaskListener{
public <methods>;
}
-keep class com.adins.mss.base.depositreport.TaskLogHelper{
public <methods>;
}
-keep class com.adins.mss.base.dynamicform.form.questions.viewholder.ImageQuestionViewHolder{
public <methods>;
}
-keep class com.adins.mss.base.timeline.Constants{
public <methods>;
}
-keep class com.adins.mss.base.dynamicform.TaskDBean{
public <methods>;
}
-keep class org.acra.ACRAConfiguration{
public <methods>;
}
-keep class com.services.ForegroundServiceNotification{
public <methods>;
}
-keep class com.services.SurveyAssignmentThread{
public <methods>;
}
-keep class com.adins.mss.base.commons.BackupManager{
public <methods>;
}
-keep class com.adins.mss.base.mainmenu.NewMenuItem{
public <methods>;
}
-keep class com.adins.mss.base.NewMainActivity{
public static <fields>;
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.base.AppContext {
public protected <methods>;
public protected <fields>;
}
-keep class org.acra.annotation.ReportsCrashes {
public protected <methods>;
public protected <fields>;
}
-keep class org.acra.ReportField {
public protected <methods>;
public protected <fields>;
}
-keep class org.acra.ReportingInteractionMode {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.base.GlobalData {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.base.util.GsonHelper {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.base.util.ExcludeFromGson{
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.foundation.dialog.NiftyDialogBuilder {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.foundation.http.HttpConnectionResult {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.foundation.http.HttpCryptedConnection {
public protected <methods>;
public protected <fields>;
}
-keep class org.acra.ACRA {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.foundation.http.KeyValue {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.foundation.http.MssResponseType {
public protected <methods>;
public protected <fields>;
}
-keep class androidx.fragment.app.Fragment {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.dao.TaskH {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.base.timeline.MapsViewer {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.foundation.dialog.DialogManager {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.foundation.formatter.Tool {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.foundation.image.Utils {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.base.mainmenu.MainMenuActivity {
public protected <methods>;
public protected <fields>;
}
-keep class androidx.fragment.app.FragmentActivity {
public protected <methods>;
public protected <fields>;
}
-keep class androidx.fragment.app.FragmentTransaction {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.base.dynamicform.JsonResponseSubmitTask {
public protected <methods>;
public protected <fields>;
}
-keep class com.mikepenz.aboutlibraries.entity.Library {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.base.dynamicform.CustomerFragment {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.base.dynamicform.SurveyHeaderBean {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.base.tasklog.TaskLogArrayAdapter {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.base.timeline.TimelineManager {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.base.todolist.ToDoList {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.base.todolist.form.JsonResponseTaskList {
public protected <methods>;
}
-keep class com.adins.mss.dao.Scheme {
public protected <methods>;
}
-keep class com.adins.mss.dao.User {
public protected <methods>;
}
-keep class com.adins.mss.foundation.db.dataaccess.SchemeDataAccess {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.foundation.db.dataaccess.TaskHDataAccess {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.foundation.db.dataaccess.TimelineDataAccess {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.foundation.db.dataaccess.TimelineTypeDataAccess {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.foundation.http.MssRequestType {
public protected <methods>;
}
-keep class com.mikepenz.aboutlibraries.entity.License {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.base.about.activity.AboutActivity {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.base.login.DefaultLoginModel {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.base.LoginActivity {
public protected <methods>;
public protected <fields>;
}
-keep class androidx.core.app.NotificationCompat {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.base.dynamicform.JsonRequestTaskD {
public protected <methods>;
}
-keep class com.adins.mss.base.dynamicform.JsonResponseTaskD {
public protected <methods>;
}
-keep class com.adins.mss.base.timeline.activity.Timeline_Activity {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.dao.TaskD {
public protected <methods>;
}
-keep class com.adins.mss.foundation.db.dataaccess.GeneralParameterDataAccess {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.foundation.db.dataaccess.TaskDDataAccess {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.foundation.notification.Notification {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.base.ChangePasswordFragment {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.base.mainmenu.MainMenuHelper {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.base.tasklog.TaskLogImplImpl {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.base.timeline.MenuAdapter {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.base.timeline.MenuModel {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.base.todo.form.GetSchemeTask {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.base.todolist.form.StatusSectionFragment {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.base.todolist.form.TaskListTask {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.base.todolist.form.TaskList_Fragment {
public protected <methods>;
public protected <fields>;
}
-keep class com.services.NotificationThread {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.base.todo.form.NewTaskActivity {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.base.todo.form.NewTaskAdapter {
public protected <methods>;
}
-keep class com.adins.mss.base.todolist.DoList {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.base.SynchronizeActivity {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.foundation.formatter.Formatter {
public protected <methods>;
public protected <fields>;
}
-keep class androidx.fragment.app.DialogFragment {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.base.dynamicform.Constant {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.base.dynamicform.DynamicFormActivity {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.base.dynamicform.SendResultActivity {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.base.dynamicform.TaskManager {
public protected <methods>;
public protected <fields>;
}
-keep class com.adins.mss.foundation.questiongenerator.QuestionBean {
public protected <methods>;
public protected <fields>;
}
-keep class com.google.gson.JsonSyntaxException {public protected static *; }
-keep class com.adins.mss.foundation.db.DaoOpenHelper {public protected static *; }
-keep class com.adins.mss.foundation.image.JsonResponseImage {public protected static *; }
-keep class com.adins.mss.svy.models.SurveyorSearchResponse {public protected static *;}
-keep class com.adins.mss.svy.reassignment.JsonResponseServer {
public protected <methods>;
public protected <fields>;
}
-keep public class com.adins.mss.base.dynamicform.form.questions.OnQuestionClickListener{
public protected <methods>;
}
-keep public class com.adins.mss.base.dynamicform.form.questions.viewholder.ExpandableRecyclerView$GroupViewHolder{
public protected <methods>;
public protected <fields>;
}
-keep public class com.adins.mss.base.dynamicform.QuestionSetTask{
public protected <methods>;
public protected <fields>;
}
-keep public class com.adins.libs.nineoldandroids.view.ViewHelper{
public protected <methods>;
public protected <fields>;
}
-keep public class com.adins.mss.base.dynamicform.form.questions.viewholder.ExpandableRecyclerView$Adapter{
public protected <methods>;
public protected <fields>;
}
-keep public class com.adins.mss.base.dialogfragments.NewTaskDialog{
public protected <methods>;
public protected <fields>;
}
-keep public class com.adins.mss.base.todolist.form.OnTaskListClickListener{
public protected <methods>;
public protected <fields>;
}
-keep public class com.adins.mss.base.authentication.Authentication{
public protected <methods>;
public protected <fields>;
}
-keep public class com.adins.mss.base.todolist.form.TasklistListener{
public protected <methods>;
public protected <fields>;
}
-keep public class com.adins.mss.dummy.userhelp_dummy.Adapter.NewTaskLogDummyAdapter{
public protected <methods>;
public protected <fields>;
}
-keep public class com.adins.mss.base.dynamicform.TaskManager$ApproveTaskOnBackground{
public protected <methods>;
public protected <fields>;
}
-keep public class com.adins.mss.base.dynamicform.TaskManager$VerifTaskOnBackground{
public protected <methods>;
public protected <fields>;
}
#Uncomment if using Serializable
-keepclassmembers class * implements java.io.Serializable {
private static final java.io.ObjectStreamField[] serialPersistentFields;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
-keep class * implements java.io.Serializable {
public protected <fields>;
}
#Keep fields for Gson transactions
-keep public class * extends com.adins.mss.foundation.http.MssRequestType{
<fields>;
<methods>;
}
-keep public class * extends RecyclerView.Adapter{
<fields>;
<methods>;
}
-keep public class * extends com.adins.mss.foundation.http.MssResponseType{
<fields>;
<methods>;
}
-keep public class com.adins.mss.foundation.UserHelp.** {*;}

View file

@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.adins.mss.base">
<!-- <uses-permission android:name="android.permission.RECORD_AUDIO" /> -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-feature android:name="android.hardware.camera" />
<uses-permission android:name="android.permission.ACCESS_GPS" />
<uses-permission android:name="android.permission.ACCESS_ASSISTED_GPS" />
<uses-feature
android:name="android.hardware.camera.front"
android:required="false" />
<uses-feature
android:name="android.hardware.camera.flash"
android:required="false" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.FLASHLIGHT" />
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" /> <!-- External storage for caching. -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /><!-- My Location -->
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" /> <!-- agar aplikasi tidak dikill -->
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<application>
<receiver
android:name="com.receivers.BackupReceiver"
android:enabled="true"
android:exported="false"></receiver>
<service
android:name="com.services.MainServices"
android:foregroundServiceType="location"
android:exported="false" /> <!-- broadcast receiver -->
<receiver
android:name="com.services.ServiceAutoRestart"
android:directBootAware="true"
android:exported="true"
android:process=":remote">
<intent-filter>
<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<activity
android:name=".dynamicform.form.DynamicQuestionActivity"
android:configChanges="orientation|keyboard|keyboardHidden|screenSize"
android:windowSoftInputMode="adjustResize" />
<activity
android:name=".dynamicform.form.questions.DrawingCanvasActivity"
android:configChanges="orientation|keyboard|keyboardHidden|screenSize" />
<activity android:name=".dynamicform.form.questions.ImageViewerActivity" />
<activity
android:name="com.adins.mss.printer.main_winson"
android:label="@string/title_activity_main_winson" />
<activity
android:name="zj.com.cn.bluetooth.sdk.Main_Activity1"
android:configChanges="orientation|keyboardHidden"
android:screenOrientation="portrait" />
<activity
android:name="zj.com.cn.bluetooth.sdk.DeviceListActivity"
android:configChanges="orientation|keyboardHidden"
android:label="@string/select_device"
android:theme="@android:style/Theme.Dialog" /> <!-- LOOKUP ONLINE -->
<activity
android:name=".dynamicform.form.questions.viewholder.LookupFilterActivity"
android:configChanges="orientation|keyboard|keyboardHidden|screenSize"
android:windowSoftInputMode="adjustResize" />
<activity
android:name=".dynamicform.form.questions.viewholder.LookupCriteriaOnlineActivity"
android:configChanges="orientation|keyboard|keyboardHidden|screenSize" /> <!-- <activity android:name=".NewMainActivity" /> -->
<activity android:name=".NewChangePasswordActivity" />
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>
<service
android:name="com.services.MssJobScheduler"
android:permission="android.permission.BIND_JOB_SERVICE">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</service>
<service
android:name="com.services.plantask.StartVisitJob"
android:exported="false"/>
<service
android:name="com.services.plantask.ChangePlanService"
android:exported="false"/>
<uses-library
android:name="org.apache.http.legacy"
android:required="false" />
</application>
</manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

View file

@ -0,0 +1,276 @@
/*
* Copyright (C) 2010 The Android 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.
*/
package com.adins.libs.nineoldandroids.animation;
import android.view.animation.Interpolator;
import java.util.ArrayList;
/**
* This is the superclass for classes which provide basic support for animations which can be
* started, ended, and have <code>AnimatorListeners</code> added to them.
*/
public abstract class Animator implements Cloneable {
/**
* The set of listeners to be sent events through the life of an animation.
*/
ArrayList<AnimatorListener> mListeners = null;
/**
* Starts this animation. If the animation has a nonzero startDelay, the animation will start
* running after that delay elapses. A non-delayed animation will have its initial
* value(s) set immediately, followed by calls to
* {@link AnimatorListener#onAnimationStart(Animator)} for any listeners of this animator.
* <p>
* <p>The animation started by calling this method will be run on the thread that called
* this method. This thread should have a Looper on it (a runtime exception will be thrown if
* this is not the case). Also, if the animation will animate
* properties of objects in the view hierarchy, then the calling thread should be the UI
* thread for that view hierarchy.</p>
*/
public void start() {
}
/**
* Cancels the animation. Unlike {@link #end()}, <code>cancel()</code> causes the animation to
* stop in its tracks, sending an
* {@link android.animation.Animator.AnimatorListener#onAnimationCancel(Animator)} to
* its listeners, followed by an
* {@link android.animation.Animator.AnimatorListener#onAnimationEnd(Animator)} message.
* <p>
* <p>This method must be called on the thread that is running the animation.</p>
*/
public void cancel() {
}
/**
* Ends the animation. This causes the animation to assign the end value of the property being
* animated, then calling the
* {@link android.animation.Animator.AnimatorListener#onAnimationEnd(Animator)} method on
* its listeners.
* <p>
* <p>This method must be called on the thread that is running the animation.</p>
*/
public void end() {
}
/**
* The amount of time, in milliseconds, to delay starting the animation after
* {@link #start()} is called.
*
* @return the number of milliseconds to delay running the animation
*/
public abstract long getStartDelay();
/**
* The amount of time, in milliseconds, to delay starting the animation after
* {@link #start()} is called.
*
* @param startDelay The amount of the delay, in milliseconds
*/
public abstract void setStartDelay(long startDelay);
/**
* Gets the length of the animation.
*
* @return The length of the animation, in milliseconds.
*/
public abstract long getDuration();
/**
* Sets the length of the animation.
*
* @param duration The length of the animation, in milliseconds.
*/
public abstract Animator setDuration(long duration);
/**
* The time interpolator used in calculating the elapsed fraction of this animation. The
* interpolator determines whether the animation runs with linear or non-linear motion,
* such as acceleration and deceleration. The default value is
* {@link android.view.animation.AccelerateDecelerateInterpolator}
*
* @param value the interpolator to be used by this animation
*/
public abstract void setInterpolator(/*Time*/Interpolator value);
/**
* Returns whether this Animator is currently running (having been started and gone past any
* initial startDelay period and not yet ended).
*
* @return Whether the Animator is running.
*/
public abstract boolean isRunning();
/**
* Returns whether this Animator has been started and not yet ended. This state is a superset
* of the state of {@link #isRunning()}, because an Animator with a nonzero
* {@link #getStartDelay() startDelay} will return true for {@link #isStarted()} during the
* delay phase, whereas {@link #isRunning()} will return true only after the delay phase
* is complete.
*
* @return Whether the Animator has been started and not yet ended.
*/
public boolean isStarted() {
// Default method returns value for isRunning(). Subclasses should override to return a
// real value.
return isRunning();
}
/**
* Adds a listener to the set of listeners that are sent events through the life of an
* animation, such as start, repeat, and end.
*
* @param listener the listener to be added to the current set of listeners for this animation.
*/
public void addListener(AnimatorListener listener) {
if (mListeners == null) {
mListeners = new ArrayList<AnimatorListener>();
}
mListeners.add(listener);
}
/**
* Removes a listener from the set listening to this animation.
*
* @param listener the listener to be removed from the current set of listeners for this
* animation.
*/
public void removeListener(AnimatorListener listener) {
if (mListeners == null) {
return;
}
mListeners.remove(listener);
if (mListeners.size() == 0) {
mListeners = null;
}
}
/**
* Gets the set of {@link android.animation.Animator.AnimatorListener} objects that are currently
* listening for events on this <code>Animator</code> object.
*
* @return ArrayList<AnimatorListener> The set of listeners.
*/
public ArrayList<AnimatorListener> getListeners() {
return mListeners;
}
/**
* Removes all listeners from this object. This is equivalent to calling
* <code>getListeners()</code> followed by calling <code>clear()</code> on the
* returned list of listeners.
*/
public void removeAllListeners() {
if (mListeners != null) {
mListeners.clear();
mListeners = null;
}
}
@Override
public Animator clone() {
try {
final Animator anim = (Animator) super.clone();
if (mListeners != null) {
ArrayList<AnimatorListener> oldListeners = mListeners;
anim.mListeners = new ArrayList<AnimatorListener>();
int numListeners = oldListeners.size();
for (int i = 0; i < numListeners; ++i) {
anim.mListeners.add(oldListeners.get(i));
}
}
return anim;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
/**
* This method tells the object to use appropriate information to extract
* starting values for the animation. For example, a AnimatorSet object will pass
* this call to its child objects to tell them to set up the values. A
* ObjectAnimator object will use the information it has about its target object
* and PropertyValuesHolder objects to get the start values for its properties.
* An ValueAnimator object will ignore the request since it does not have enough
* information (such as a target object) to gather these values.
*/
public void setupStartValues() {
}
/**
* This method tells the object to use appropriate information to extract
* ending values for the animation. For example, a AnimatorSet object will pass
* this call to its child objects to tell them to set up the values. A
* ObjectAnimator object will use the information it has about its target object
* and PropertyValuesHolder objects to get the start values for its properties.
* An ValueAnimator object will ignore the request since it does not have enough
* information (such as a target object) to gather these values.
*/
public void setupEndValues() {
}
/**
* Sets the target object whose property will be animated by this animation. Not all subclasses
* operate on target objects (for example, {@link ValueAnimator}, but this method
* is on the superclass for the convenience of dealing generically with those subclasses
* that do handle targets.
*
* @param target The object being animated
*/
public void setTarget(Object target) {
}
/**
* <p>An animation listener receives notifications from an animation.
* Notifications indicate animation related events, such as the end or the
* repetition of the animation.</p>
*/
public static interface AnimatorListener {
/**
* <p>Notifies the start of the animation.</p>
*
* @param animation The started animation.
*/
void onAnimationStart(Animator animation);
/**
* <p>Notifies the end of the animation. This callback is not invoked
* for animations with repeat count set to INFINITE.</p>
*
* @param animation The animation which reached its end.
*/
void onAnimationEnd(Animator animation);
/**
* <p>Notifies the cancellation of the animation. This callback is not invoked
* for animations with repeat count set to INFINITE.</p>
*
* @param animation The animation which was canceled.
*/
void onAnimationCancel(Animator animation);
/**
* <p>Notifies the repetition of the animation.</p>
*
* @param animation The animation which was repeated.
*/
void onAnimationRepeat(Animator animation);
}
}

View file

@ -0,0 +1,345 @@
/*
* Copyright (C) 2010 The Android 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.
*/
package com.adins.libs.nineoldandroids.animation;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.util.Xml;
import android.view.animation.AnimationUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.util.ArrayList;
/**
* This class is used to instantiate animator XML files into Animator objects.
* <p>
* For performance reasons, inflation relies heavily on pre-processing of
* XML files that is done at build time. Therefore, it is not currently possible
* to use this inflater with an XmlPullParser over a plain XML file at runtime;
* it only works with an XmlPullParser returned from a compiled resource (R.
* <em>something</em> file.)
*/
public class AnimatorInflater {
private static final int[] AnimatorSet = new int[]{
/* 0 */ android.R.attr.ordering,
};
private static final int AnimatorSet_ordering = 0;
private static final int[] PropertyAnimator = new int[]{
/* 0 */ android.R.attr.propertyName,
};
private static final int PropertyAnimator_propertyName = 0;
private static final int[] Animator = new int[]{
/* 0 */ android.R.attr.interpolator,
/* 1 */ android.R.attr.duration,
/* 2 */ android.R.attr.startOffset,
/* 3 */ android.R.attr.repeatCount,
/* 4 */ android.R.attr.repeatMode,
/* 5 */ android.R.attr.valueFrom,
/* 6 */ android.R.attr.valueTo,
/* 7 */ android.R.attr.valueType,
};
private static final int Animator_interpolator = 0;
private static final int Animator_duration = 1;
private static final int Animator_startOffset = 2;
private static final int Animator_repeatCount = 3;
private static final int Animator_repeatMode = 4;
private static final int Animator_valueFrom = 5;
private static final int Animator_valueTo = 6;
private static final int Animator_valueType = 7;
/**
* These flags are used when parsing AnimatorSet objects
*/
private static final int TOGETHER = 0;
//private static final int SEQUENTIALLY = 1;
/**
* Enum values used in XML attributes to indicate the value for mValueType
*/
private static final int VALUE_TYPE_FLOAT = 0;
//private static final int VALUE_TYPE_INT = 1;
//private static final int VALUE_TYPE_COLOR = 4;
//private static final int VALUE_TYPE_CUSTOM = 5;
/**
* Loads an {@link Animator} object from a resource
*
* @param context Application context used to access resources
* @param id The resource id of the animation to load
* @return The animator object reference by the specified id
* @throws android.content.res.Resources.NotFoundException when the animation cannot be loaded
*/
public static Animator loadAnimator(Context context, int id)
throws NotFoundException {
XmlResourceParser parser = null;
try {
parser = context.getResources().getAnimation(id);
return createAnimatorFromXml(context, parser);
} catch (XmlPullParserException ex) {
Resources.NotFoundException rnf =
new Resources.NotFoundException("Can't load animation resource ID #0x" +
Integer.toHexString(id));
rnf.initCause(ex);
throw rnf;
} catch (IOException ex) {
Resources.NotFoundException rnf =
new Resources.NotFoundException("Can't load animation resource ID #0x" +
Integer.toHexString(id));
rnf.initCause(ex);
throw rnf;
} finally {
if (parser != null) parser.close();
}
}
private static Animator createAnimatorFromXml(Context c, XmlPullParser parser)
throws XmlPullParserException, IOException {
return createAnimatorFromXml(c, parser, Xml.asAttributeSet(parser), null, 0);
}
private static Animator createAnimatorFromXml(Context c, XmlPullParser parser,
AttributeSet attrs, AnimatorSet parent, int sequenceOrdering)
throws XmlPullParserException, IOException {
Animator anim = null;
ArrayList<Animator> childAnims = null;
// Make sure we are on a start tag.
int type;
int depth = parser.getDepth();
while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
&& type != XmlPullParser.END_DOCUMENT) {
if (type != XmlPullParser.START_TAG) {
continue;
}
String name = parser.getName();
if (name.equals("objectAnimator")) {
anim = loadObjectAnimator(c, attrs);
} else if (name.equals("animator")) {
anim = loadAnimator(c, attrs, null);
} else if (name.equals("set")) {
anim = new AnimatorSet();
TypedArray a = c.obtainStyledAttributes(attrs,
/*com.android.internal.R.styleable.*/AnimatorSet);
TypedValue orderingValue = new TypedValue();
a.getValue(/*com.android.internal.R.styleable.*/AnimatorSet_ordering, orderingValue);
int ordering = orderingValue.type == TypedValue.TYPE_INT_DEC ? orderingValue.data : TOGETHER;
createAnimatorFromXml(c, parser, attrs, (AnimatorSet) anim, ordering);
a.recycle();
} else {
throw new RuntimeException("Unknown animator name: " + parser.getName());
}
if (parent != null) {
if (childAnims == null) {
childAnims = new ArrayList<Animator>();
}
childAnims.add(anim);
}
}
if (parent != null && childAnims != null) {
Animator[] animsArray = new Animator[childAnims.size()];
int index = 0;
for (Animator a : childAnims) {
animsArray[index++] = a;
}
if (sequenceOrdering == TOGETHER) {
parent.playTogether(animsArray);
} else {
parent.playSequentially(animsArray);
}
}
return anim;
}
private static ObjectAnimator loadObjectAnimator(Context context, AttributeSet attrs)
throws NotFoundException {
ObjectAnimator anim = new ObjectAnimator();
loadAnimator(context, attrs, anim);
TypedArray a =
context.obtainStyledAttributes(attrs, /*com.android.internal.R.styleable.*/PropertyAnimator);
String propertyName = a.getString(/*com.android.internal.R.styleable.*/PropertyAnimator_propertyName);
anim.setPropertyName(propertyName);
a.recycle();
return anim;
}
/**
* Creates a new animation whose parameters come from the specified context and
* attributes set.
*
* @param context the application environment
* @param attrs the set of attributes holding the animation parameters
*/
private static ValueAnimator loadAnimator(Context context, AttributeSet attrs, ValueAnimator anim)
throws NotFoundException {
TypedArray a =
context.obtainStyledAttributes(attrs, /*com.android.internal.R.styleable.*/Animator);
long duration = a.getInt(/*com.android.internal.R.styleable.*/Animator_duration, 0);
long startDelay = a.getInt(/*com.android.internal.R.styleable.*/Animator_startOffset, 0);
int valueType = a.getInt(/*com.android.internal.R.styleable.*/Animator_valueType,
VALUE_TYPE_FLOAT);
if (anim == null) {
anim = new ValueAnimator();
}
//TypeEvaluator evaluator = null;
int valueFromIndex = /*com.android.internal.R.styleable.*/Animator_valueFrom;
int valueToIndex = /*com.android.internal.R.styleable.*/Animator_valueTo;
boolean getFloats = (valueType == VALUE_TYPE_FLOAT);
TypedValue tvFrom = a.peekValue(valueFromIndex);
boolean hasFrom = (tvFrom != null);
int fromType = hasFrom ? tvFrom.type : 0;
TypedValue tvTo = a.peekValue(valueToIndex);
boolean hasTo = (tvTo != null);
int toType = hasTo ? tvTo.type : 0;
if ((hasFrom && (fromType >= TypedValue.TYPE_FIRST_COLOR_INT) &&
(fromType <= TypedValue.TYPE_LAST_COLOR_INT)) ||
(hasTo && (toType >= TypedValue.TYPE_FIRST_COLOR_INT) &&
(toType <= TypedValue.TYPE_LAST_COLOR_INT))) {
// special case for colors: ignore valueType and get ints
getFloats = false;
anim.setEvaluator(new ArgbEvaluator());
}
if (getFloats) {
float valueFrom;
float valueTo;
if (hasFrom) {
if (fromType == TypedValue.TYPE_DIMENSION) {
valueFrom = a.getDimension(valueFromIndex, 0f);
} else {
valueFrom = a.getFloat(valueFromIndex, 0f);
}
if (hasTo) {
if (toType == TypedValue.TYPE_DIMENSION) {
valueTo = a.getDimension(valueToIndex, 0f);
} else {
valueTo = a.getFloat(valueToIndex, 0f);
}
anim.setFloatValues(valueFrom, valueTo);
} else {
anim.setFloatValues(valueFrom);
}
} else {
if (toType == TypedValue.TYPE_DIMENSION) {
valueTo = a.getDimension(valueToIndex, 0f);
} else {
valueTo = a.getFloat(valueToIndex, 0f);
}
anim.setFloatValues(valueTo);
}
} else {
int valueFrom;
int valueTo;
if (hasFrom) {
if (fromType == TypedValue.TYPE_DIMENSION) {
valueFrom = (int) a.getDimension(valueFromIndex, 0f);
} else if ((fromType >= TypedValue.TYPE_FIRST_COLOR_INT) &&
(fromType <= TypedValue.TYPE_LAST_COLOR_INT)) {
valueFrom = a.getColor(valueFromIndex, 0);
} else {
valueFrom = a.getInt(valueFromIndex, 0);
}
if (hasTo) {
if (toType == TypedValue.TYPE_DIMENSION) {
valueTo = (int) a.getDimension(valueToIndex, 0f);
} else if ((toType >= TypedValue.TYPE_FIRST_COLOR_INT) &&
(toType <= TypedValue.TYPE_LAST_COLOR_INT)) {
valueTo = a.getColor(valueToIndex, 0);
} else {
valueTo = a.getInt(valueToIndex, 0);
}
anim.setIntValues(valueFrom, valueTo);
} else {
anim.setIntValues(valueFrom);
}
} else {
if (hasTo) {
if (toType == TypedValue.TYPE_DIMENSION) {
valueTo = (int) a.getDimension(valueToIndex, 0f);
} else if ((toType >= TypedValue.TYPE_FIRST_COLOR_INT) &&
(toType <= TypedValue.TYPE_LAST_COLOR_INT)) {
valueTo = a.getColor(valueToIndex, 0);
} else {
valueTo = a.getInt(valueToIndex, 0);
}
anim.setIntValues(valueTo);
}
}
}
anim.setDuration(duration);
anim.setStartDelay(startDelay);
if (a.hasValue(/*com.android.internal.R.styleable.*/Animator_repeatCount)) {
anim.setRepeatCount(
a.getInt(/*com.android.internal.R.styleable.*/Animator_repeatCount, 0));
}
if (a.hasValue(/*com.android.internal.R.styleable.*/Animator_repeatMode)) {
anim.setRepeatMode(
a.getInt(/*com.android.internal.R.styleable.*/Animator_repeatMode,
ValueAnimator.RESTART));
}
//if (evaluator != null) {
// anim.setEvaluator(evaluator);
//}
final int resID =
a.getResourceId(/*com.android.internal.R.styleable.*/Animator_interpolator, 0);
if (resID > 0) {
anim.setInterpolator(AnimationUtils.loadInterpolator(context, resID));
}
a.recycle();
return anim;
}
}

View file

@ -0,0 +1,54 @@
/*
* Copyright (C) 2010 The Android 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.
*/
package com.adins.libs.nineoldandroids.animation;
/**
* This adapter class provides empty implementations of the methods from {@link android.animation.Animator.AnimatorListener}.
* Any custom listener that cares only about a subset of the methods of this listener can
* simply subclass this adapter class instead of implementing the interface directly.
*/
public abstract class AnimatorListenerAdapter implements Animator.AnimatorListener {
/**
* {@inheritDoc}
*/
@Override
public void onAnimationCancel(Animator animation) {
}
/**
* {@inheritDoc}
*/
@Override
public void onAnimationEnd(Animator animation) {
}
/**
* {@inheritDoc}
*/
@Override
public void onAnimationRepeat(Animator animation) {
}
/**
* {@inheritDoc}
*/
@Override
public void onAnimationStart(Animator animation) {
}
}

View file

@ -0,0 +1,59 @@
/*
* Copyright (C) 2010 The Android 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.
*/
package com.adins.libs.nineoldandroids.animation;
/**
* This evaluator can be used to perform type interpolation between integer
* values that represent ARGB colors.
*/
public class ArgbEvaluator implements TypeEvaluator {
/**
* This function returns the calculated in-between value for a color
* given integers that represent the start and end values in the four
* bytes of the 32-bit int. Each channel is separately linearly interpolated
* and the resulting calculated values are recombined into the return value.
*
* @param fraction The fraction from the starting to the ending values
* @param startValue A 32-bit int value representing colors in the
* separate bytes of the parameter
* @param endValue A 32-bit int value representing colors in the
* separate bytes of the parameter
* @return A value that is calculated to be the linearly interpolated
* result, derived by separating the start and end values into separate
* color channels and interpolating each one separately, recombining the
* resulting values in the same way.
*/
public Object evaluate(float fraction, Object startValue, Object endValue) {
int startInt = (Integer) startValue;
int startA = (startInt >> 24);
int startR = (startInt >> 16) & 0xff;
int startG = (startInt >> 8) & 0xff;
int startB = startInt & 0xff;
int endInt = (Integer) endValue;
int endA = (endInt >> 24);
int endR = (endInt >> 16) & 0xff;
int endG = (endInt >> 8) & 0xff;
int endB = endInt & 0xff;
return (int) ((startA + (int) (fraction * (endA - startA))) << 24) |
(int) ((startR + (int) (fraction * (endR - startR))) << 16) |
(int) ((startG + (int) (fraction * (endG - startG))) << 8) |
(int) ((startB + (int) (fraction * (endB - startB))));
}
}

View file

@ -0,0 +1,42 @@
/*
* Copyright (C) 2010 The Android 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.
*/
package com.adins.libs.nineoldandroids.animation;
/**
* This evaluator can be used to perform type interpolation between <code>float</code> values.
*/
public class FloatEvaluator implements TypeEvaluator<Number> {
/**
* This function returns the result of linearly interpolating the start and end values, with
* <code>fraction</code> representing the proportion between the start and end values. The
* calculation is a simple parametric calculation: <code>result = x0 + t * (v1 - v0)</code>,
* where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>,
* and <code>t</code> is <code>fraction</code>.
*
* @param fraction The fraction from the starting to the ending values
* @param startValue The start value; should be of type <code>float</code> or
* <code>Float</code>
* @param endValue The end value; should be of type <code>float</code> or <code>Float</code>
* @return A linear interpolation between the start and end values, given the
* <code>fraction</code> parameter.
*/
public Float evaluate(float fraction, Number startValue, Number endValue) {
float startFloat = startValue.floatValue();
return startFloat + fraction * (endValue.floatValue() - startFloat);
}
}

View file

@ -0,0 +1,136 @@
/*
* Copyright (C) 2010 The Android 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.
*/
package com.adins.libs.nineoldandroids.animation;
import android.view.animation.Interpolator;
import com.adins.libs.nineoldandroids.animation.Keyframe.FloatKeyframe;
import java.util.ArrayList;
/**
* This class holds a collection of FloatKeyframe objects and is called by ValueAnimator to calculate
* values between those keyframes for a given animation. The class internal to the animation
* package because it is an implementation detail of how Keyframes are stored and used.
* <p>
* <p>This type-specific subclass of KeyframeSet, along with the other type-specific subclass for
* int, exists to speed up the getValue() method when there is no custom
* TypeEvaluator set for the animation, so that values can be calculated without autoboxing to the
* Object equivalents of these primitive types.</p>
*/
class FloatKeyframeSet extends KeyframeSet {
private float firstValue;
private float lastValue;
private float deltaValue;
private boolean firstTime = true;
public FloatKeyframeSet(FloatKeyframe... keyframes) {
super(keyframes);
}
@Override
public Object getValue(float fraction) {
return getFloatValue(fraction);
}
@Override
public FloatKeyframeSet clone() {
ArrayList<Keyframe> keyframes = mKeyframes;
int numKeyframes = mKeyframes.size();
FloatKeyframe[] newKeyframes = new FloatKeyframe[numKeyframes];
for (int i = 0; i < numKeyframes; ++i) {
newKeyframes[i] = (FloatKeyframe) keyframes.get(i).clone();
}
FloatKeyframeSet newSet = new FloatKeyframeSet(newKeyframes);
return newSet;
}
public float getFloatValue(float fraction) {
if (mNumKeyframes == 2) {
if (firstTime) {
firstTime = false;
firstValue = ((FloatKeyframe) mKeyframes.get(0)).getFloatValue();
lastValue = ((FloatKeyframe) mKeyframes.get(1)).getFloatValue();
deltaValue = lastValue - firstValue;
}
if (mInterpolator != null) {
fraction = mInterpolator.getInterpolation(fraction);
}
if (mEvaluator == null) {
return firstValue + fraction * deltaValue;
} else {
return ((Number) mEvaluator.evaluate(fraction, firstValue, lastValue)).floatValue();
}
}
if (fraction <= 0f) {
final FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(0);
final FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(1);
float prevValue = prevKeyframe.getFloatValue();
float nextValue = nextKeyframe.getFloatValue();
float prevFraction = prevKeyframe.getFraction();
float nextFraction = nextKeyframe.getFraction();
final /*Time*/ Interpolator interpolator = nextKeyframe.getInterpolator();
if (interpolator != null) {
fraction = interpolator.getInterpolation(fraction);
}
float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
return mEvaluator == null ?
prevValue + intervalFraction * (nextValue - prevValue) :
((Number) mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
floatValue();
} else if (fraction >= 1f) {
final FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(mNumKeyframes - 2);
final FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(mNumKeyframes - 1);
float prevValue = prevKeyframe.getFloatValue();
float nextValue = nextKeyframe.getFloatValue();
float prevFraction = prevKeyframe.getFraction();
float nextFraction = nextKeyframe.getFraction();
final /*Time*/ Interpolator interpolator = nextKeyframe.getInterpolator();
if (interpolator != null) {
fraction = interpolator.getInterpolation(fraction);
}
float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
return mEvaluator == null ?
prevValue + intervalFraction * (nextValue - prevValue) :
((Number) mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
floatValue();
}
FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(0);
for (int i = 1; i < mNumKeyframes; ++i) {
FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(i);
if (fraction < nextKeyframe.getFraction()) {
final /*Time*/ Interpolator interpolator = nextKeyframe.getInterpolator();
if (interpolator != null) {
fraction = interpolator.getInterpolation(fraction);
}
float intervalFraction = (fraction - prevKeyframe.getFraction()) /
(nextKeyframe.getFraction() - prevKeyframe.getFraction());
float prevValue = prevKeyframe.getFloatValue();
float nextValue = nextKeyframe.getFloatValue();
return mEvaluator == null ?
prevValue + intervalFraction * (nextValue - prevValue) :
((Number) mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
floatValue();
}
prevKeyframe = nextKeyframe;
}
// shouldn't get here
return ((Number) mKeyframes.get(mNumKeyframes - 1).getValue()).floatValue();
}
}

View file

@ -0,0 +1,42 @@
/*
* Copyright (C) 2010 The Android 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.
*/
package com.adins.libs.nineoldandroids.animation;
/**
* This evaluator can be used to perform type interpolation between <code>int</code> values.
*/
public class IntEvaluator implements TypeEvaluator<Integer> {
/**
* This function returns the result of linearly interpolating the start and end values, with
* <code>fraction</code> representing the proportion between the start and end values. The
* calculation is a simple parametric calculation: <code>result = x0 + t * (v1 - v0)</code>,
* where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>,
* and <code>t</code> is <code>fraction</code>.
*
* @param fraction The fraction from the starting to the ending values
* @param startValue The start value; should be of type <code>int</code> or
* <code>Integer</code>
* @param endValue The end value; should be of type <code>int</code> or <code>Integer</code>
* @return A linear interpolation between the start and end values, given the
* <code>fraction</code> parameter.
*/
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
int startInt = startValue;
return (int) (startInt + fraction * (endValue - startInt));
}
}

View file

@ -0,0 +1,135 @@
/*
* Copyright (C) 2010 The Android 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.
*/
package com.adins.libs.nineoldandroids.animation;
import android.view.animation.Interpolator;
import com.adins.libs.nineoldandroids.animation.Keyframe.IntKeyframe;
import java.util.ArrayList;
/**
* This class holds a collection of IntKeyframe objects and is called by ValueAnimator to calculate
* values between those keyframes for a given animation. The class internal to the animation
* package because it is an implementation detail of how Keyframes are stored and used.
* <p>
* <p>This type-specific subclass of KeyframeSet, along with the other type-specific subclass for
* float, exists to speed up the getValue() method when there is no custom
* TypeEvaluator set for the animation, so that values can be calculated without autoboxing to the
* Object equivalents of these primitive types.</p>
*/
class IntKeyframeSet extends KeyframeSet {
private int firstValue;
private int lastValue;
private int deltaValue;
private boolean firstTime = true;
public IntKeyframeSet(IntKeyframe... keyframes) {
super(keyframes);
}
@Override
public Object getValue(float fraction) {
return getIntValue(fraction);
}
@Override
public IntKeyframeSet clone() {
ArrayList<Keyframe> keyframes = mKeyframes;
int numKeyframes = mKeyframes.size();
IntKeyframe[] newKeyframes = new IntKeyframe[numKeyframes];
for (int i = 0; i < numKeyframes; ++i) {
newKeyframes[i] = (IntKeyframe) keyframes.get(i).clone();
}
IntKeyframeSet newSet = new IntKeyframeSet(newKeyframes);
return newSet;
}
public int getIntValue(float fraction) {
if (mNumKeyframes == 2) {
if (firstTime) {
firstTime = false;
firstValue = ((IntKeyframe) mKeyframes.get(0)).getIntValue();
lastValue = ((IntKeyframe) mKeyframes.get(1)).getIntValue();
deltaValue = lastValue - firstValue;
}
if (mInterpolator != null) {
fraction = mInterpolator.getInterpolation(fraction);
}
if (mEvaluator == null) {
return firstValue + (int) (fraction * deltaValue);
} else {
return ((Number) mEvaluator.evaluate(fraction, firstValue, lastValue)).intValue();
}
}
if (fraction <= 0f) {
final IntKeyframe prevKeyframe = (IntKeyframe) mKeyframes.get(0);
final IntKeyframe nextKeyframe = (IntKeyframe) mKeyframes.get(1);
int prevValue = prevKeyframe.getIntValue();
int nextValue = nextKeyframe.getIntValue();
float prevFraction = prevKeyframe.getFraction();
float nextFraction = nextKeyframe.getFraction();
final /*Time*/ Interpolator interpolator = nextKeyframe.getInterpolator();
if (interpolator != null) {
fraction = interpolator.getInterpolation(fraction);
}
float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
return mEvaluator == null ?
prevValue + (int) (intervalFraction * (nextValue - prevValue)) :
((Number) mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
intValue();
} else if (fraction >= 1f) {
final IntKeyframe prevKeyframe = (IntKeyframe) mKeyframes.get(mNumKeyframes - 2);
final IntKeyframe nextKeyframe = (IntKeyframe) mKeyframes.get(mNumKeyframes - 1);
int prevValue = prevKeyframe.getIntValue();
int nextValue = nextKeyframe.getIntValue();
float prevFraction = prevKeyframe.getFraction();
float nextFraction = nextKeyframe.getFraction();
final /*Time*/ Interpolator interpolator = nextKeyframe.getInterpolator();
if (interpolator != null) {
fraction = interpolator.getInterpolation(fraction);
}
float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
return mEvaluator == null ?
prevValue + (int) (intervalFraction * (nextValue - prevValue)) :
((Number) mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).intValue();
}
IntKeyframe prevKeyframe = (IntKeyframe) mKeyframes.get(0);
for (int i = 1; i < mNumKeyframes; ++i) {
IntKeyframe nextKeyframe = (IntKeyframe) mKeyframes.get(i);
if (fraction < nextKeyframe.getFraction()) {
final /*Time*/ Interpolator interpolator = nextKeyframe.getInterpolator();
if (interpolator != null) {
fraction = interpolator.getInterpolation(fraction);
}
float intervalFraction = (fraction - prevKeyframe.getFraction()) /
(nextKeyframe.getFraction() - prevKeyframe.getFraction());
int prevValue = prevKeyframe.getIntValue();
int nextValue = nextKeyframe.getIntValue();
return mEvaluator == null ?
prevValue + (int) (intervalFraction * (nextValue - prevValue)) :
((Number) mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
intValue();
}
prevKeyframe = nextKeyframe;
}
// shouldn't get here
return ((Number) mKeyframes.get(mNumKeyframes - 1).getValue()).intValue();
}
}

View file

@ -0,0 +1,358 @@
/*
* Copyright (C) 2010 The Android 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.
*/
package com.adins.libs.nineoldandroids.animation;
import android.view.animation.Interpolator;
/**
* This class holds a time/value pair for an animation. The Keyframe class is used
* by {@link ValueAnimator} to define the values that the animation target will have over the course
* of the animation. As the time proceeds from one keyframe to the other, the value of the
* target object will animate between the value at the previous keyframe and the value at the
* next keyframe. Each keyframe also holds an optional {@link TimeInterpolator}
* object, which defines the time interpolation over the intervalue preceding the keyframe.
* <p>
* <p>The Keyframe class itself is abstract. The type-specific factory methods will return
* a subclass of Keyframe specific to the type of value being stored. This is done to improve
* performance when dealing with the most common cases (e.g., <code>float</code> and
* <code>int</code> values). Other types will fall into a more general Keyframe class that
* treats its values as Objects. Unless your animation requires dealing with a custom type
* or a data structure that needs to be animated directly (and evaluated using an implementation
* of {@link TypeEvaluator}), you should stick to using float and int as animations using those
* types have lower runtime overhead than other types.</p>
*/
public abstract class Keyframe implements Cloneable {
/**
* The time at which mValue will hold true.
*/
float mFraction;
/**
* The type of the value in this Keyframe. This type is determined at construction time,
* based on the type of the <code>value</code> object passed into the constructor.
*/
Class mValueType;
/**
* Flag to indicate whether this keyframe has a valid value. This flag is used when an
* animation first starts, to populate placeholder keyframes with real values derived
* from the target object.
*/
boolean mHasValue = false;
/**
* The optional time interpolator for the interval preceding this keyframe. A null interpolator
* (the default) results in linear interpolation over the interval.
*/
private /*Time*/ Interpolator mInterpolator = null;
/**
* Constructs a Keyframe object with the given time and value. The time defines the
* time, as a proportion of an overall animation's duration, at which the value will hold true
* for the animation. The value for the animation between keyframes will be calculated as
* an interpolation between the values at those keyframes.
*
* @param fraction The time, expressed as a value between 0 and 1, representing the fraction
* of time elapsed of the overall animation duration.
* @param value The value that the object will animate to as the animation time approaches
* the time in this keyframe, and the the value animated from as the time passes the time in
* this keyframe.
*/
public static Keyframe ofInt(float fraction, int value) {
return new IntKeyframe(fraction, value);
}
/**
* Constructs a Keyframe object with the given time. The value at this time will be derived
* from the target object when the animation first starts (note that this implies that keyframes
* with no initial value must be used as part of an {@link ObjectAnimator}).
* The time defines the
* time, as a proportion of an overall animation's duration, at which the value will hold true
* for the animation. The value for the animation between keyframes will be calculated as
* an interpolation between the values at those keyframes.
*
* @param fraction The time, expressed as a value between 0 and 1, representing the fraction
* of time elapsed of the overall animation duration.
*/
public static Keyframe ofInt(float fraction) {
return new IntKeyframe(fraction);
}
/**
* Constructs a Keyframe object with the given time and value. The time defines the
* time, as a proportion of an overall animation's duration, at which the value will hold true
* for the animation. The value for the animation between keyframes will be calculated as
* an interpolation between the values at those keyframes.
*
* @param fraction The time, expressed as a value between 0 and 1, representing the fraction
* of time elapsed of the overall animation duration.
* @param value The value that the object will animate to as the animation time approaches
* the time in this keyframe, and the the value animated from as the time passes the time in
* this keyframe.
*/
public static Keyframe ofFloat(float fraction, float value) {
return new FloatKeyframe(fraction, value);
}
/**
* Constructs a Keyframe object with the given time. The value at this time will be derived
* from the target object when the animation first starts (note that this implies that keyframes
* with no initial value must be used as part of an {@link ObjectAnimator}).
* The time defines the
* time, as a proportion of an overall animation's duration, at which the value will hold true
* for the animation. The value for the animation between keyframes will be calculated as
* an interpolation between the values at those keyframes.
*
* @param fraction The time, expressed as a value between 0 and 1, representing the fraction
* of time elapsed of the overall animation duration.
*/
public static Keyframe ofFloat(float fraction) {
return new FloatKeyframe(fraction);
}
/**
* Constructs a Keyframe object with the given time and value. The time defines the
* time, as a proportion of an overall animation's duration, at which the value will hold true
* for the animation. The value for the animation between keyframes will be calculated as
* an interpolation between the values at those keyframes.
*
* @param fraction The time, expressed as a value between 0 and 1, representing the fraction
* of time elapsed of the overall animation duration.
* @param value The value that the object will animate to as the animation time approaches
* the time in this keyframe, and the the value animated from as the time passes the time in
* this keyframe.
*/
public static Keyframe ofObject(float fraction, Object value) {
return new ObjectKeyframe(fraction, value);
}
/**
* Constructs a Keyframe object with the given time. The value at this time will be derived
* from the target object when the animation first starts (note that this implies that keyframes
* with no initial value must be used as part of an {@link ObjectAnimator}).
* The time defines the
* time, as a proportion of an overall animation's duration, at which the value will hold true
* for the animation. The value for the animation between keyframes will be calculated as
* an interpolation between the values at those keyframes.
*
* @param fraction The time, expressed as a value between 0 and 1, representing the fraction
* of time elapsed of the overall animation duration.
*/
public static Keyframe ofObject(float fraction) {
return new ObjectKeyframe(fraction, null);
}
/**
* Indicates whether this keyframe has a valid value. This method is called internally when
* an {@link ObjectAnimator} first starts; keyframes without values are assigned values at
* that time by deriving the value for the property from the target object.
*
* @return boolean Whether this object has a value assigned.
*/
public boolean hasValue() {
return mHasValue;
}
/**
* Gets the value for this Keyframe.
*
* @return The value for this Keyframe.
*/
public abstract Object getValue();
/**
* Sets the value for this Keyframe.
*
* @param value value for this Keyframe.
*/
public abstract void setValue(Object value);
/**
* Gets the time for this keyframe, as a fraction of the overall animation duration.
*
* @return The time associated with this keyframe, as a fraction of the overall animation
* duration. This should be a value between 0 and 1.
*/
public float getFraction() {
return mFraction;
}
/**
* Sets the time for this keyframe, as a fraction of the overall animation duration.
*
* @param fraction time associated with this keyframe, as a fraction of the overall animation
* duration. This should be a value between 0 and 1.
*/
public void setFraction(float fraction) {
mFraction = fraction;
}
/**
* Gets the optional interpolator for this Keyframe. A value of <code>null</code> indicates
* that there is no interpolation, which is the same as linear interpolation.
*
* @return The optional interpolator for this Keyframe.
*/
public /*Time*/Interpolator getInterpolator() {
return mInterpolator;
}
/**
* Sets the optional interpolator for this Keyframe. A value of <code>null</code> indicates
* that there is no interpolation, which is the same as linear interpolation.
*
* @return The optional interpolator for this Keyframe.
*/
public void setInterpolator(/*Time*/Interpolator interpolator) {
mInterpolator = interpolator;
}
/**
* Gets the type of keyframe. This information is used by ValueAnimator to determine the type of
* {@link TypeEvaluator} to use when calculating values between keyframes. The type is based
* on the type of Keyframe created.
*
* @return The type of the value stored in the Keyframe.
*/
public Class getType() {
return mValueType;
}
@Override
public abstract Keyframe clone();
/**
* This internal subclass is used for all types which are not int or float.
*/
static class ObjectKeyframe extends Keyframe {
/**
* The value of the animation at the time mFraction.
*/
Object mValue;
ObjectKeyframe(float fraction, Object value) {
mFraction = fraction;
mValue = value;
mHasValue = (value != null);
mValueType = mHasValue ? value.getClass() : Object.class;
}
public Object getValue() {
return mValue;
}
public void setValue(Object value) {
mValue = value;
mHasValue = (value != null);
}
@Override
public ObjectKeyframe clone() {
ObjectKeyframe kfClone = new ObjectKeyframe(getFraction(), mValue);
kfClone.setInterpolator(getInterpolator());
return kfClone;
}
}
/**
* Internal subclass used when the keyframe value is of type int.
*/
static class IntKeyframe extends Keyframe {
/**
* The value of the animation at the time mFraction.
*/
int mValue;
IntKeyframe(float fraction, int value) {
mFraction = fraction;
mValue = value;
mValueType = int.class;
mHasValue = true;
}
IntKeyframe(float fraction) {
mFraction = fraction;
mValueType = int.class;
}
public int getIntValue() {
return mValue;
}
public Object getValue() {
return mValue;
}
public void setValue(Object value) {
if (value != null && value.getClass() == Integer.class) {
mValue = ((Integer) value).intValue();
mHasValue = true;
}
}
@Override
public IntKeyframe clone() {
IntKeyframe kfClone = new IntKeyframe(getFraction(), mValue);
kfClone.setInterpolator(getInterpolator());
return kfClone;
}
}
/**
* Internal subclass used when the keyframe value is of type float.
*/
static class FloatKeyframe extends Keyframe {
/**
* The value of the animation at the time mFraction.
*/
float mValue;
FloatKeyframe(float fraction, float value) {
mFraction = fraction;
mValue = value;
mValueType = float.class;
mHasValue = true;
}
FloatKeyframe(float fraction) {
mFraction = fraction;
mValueType = float.class;
}
public float getFloatValue() {
return mValue;
}
public Object getValue() {
return mValue;
}
public void setValue(Object value) {
if (value != null && value.getClass() == Float.class) {
mValue = ((Float) value).floatValue();
mHasValue = true;
}
}
@Override
public FloatKeyframe clone() {
FloatKeyframe kfClone = new FloatKeyframe(getFraction(), mValue);
kfClone.setInterpolator(getInterpolator());
return kfClone;
}
}
}

View file

@ -0,0 +1,227 @@
/*
* Copyright (C) 2010 The Android 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.
*/
package com.adins.libs.nineoldandroids.animation;
import android.view.animation.Interpolator;
import com.adins.libs.nineoldandroids.animation.Keyframe.FloatKeyframe;
import com.adins.libs.nineoldandroids.animation.Keyframe.IntKeyframe;
import com.adins.libs.nineoldandroids.animation.Keyframe.ObjectKeyframe;
import java.util.ArrayList;
import java.util.Arrays;
/**
* This class holds a collection of Keyframe objects and is called by ValueAnimator to calculate
* values between those keyframes for a given animation. The class internal to the animation
* package because it is an implementation detail of how Keyframes are stored and used.
*/
class KeyframeSet {
int mNumKeyframes;
Keyframe mFirstKeyframe;
Keyframe mLastKeyframe;
/*Time*/ Interpolator mInterpolator; // only used in the 2-keyframe case
ArrayList<Keyframe> mKeyframes; // only used when there are not 2 keyframes
TypeEvaluator mEvaluator;
public KeyframeSet(Keyframe... keyframes) {
mNumKeyframes = keyframes.length;
mKeyframes = new ArrayList<Keyframe>();
mKeyframes.addAll(Arrays.asList(keyframes));
mFirstKeyframe = mKeyframes.get(0);
mLastKeyframe = mKeyframes.get(mNumKeyframes - 1);
mInterpolator = mLastKeyframe.getInterpolator();
}
public static KeyframeSet ofInt(int... values) {
int numKeyframes = values.length;
IntKeyframe keyframes[] = new IntKeyframe[Math.max(numKeyframes, 2)];
if (numKeyframes == 1) {
keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f);
keyframes[1] = (IntKeyframe) Keyframe.ofInt(1f, values[0]);
} else {
keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f, values[0]);
for (int i = 1; i < numKeyframes; ++i) {
keyframes[i] = (IntKeyframe) Keyframe.ofInt((float) i / (numKeyframes - 1), values[i]);
}
}
return new IntKeyframeSet(keyframes);
}
public static KeyframeSet ofFloat(float... values) {
int numKeyframes = values.length;
FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes, 2)];
if (numKeyframes == 1) {
keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f);
keyframes[1] = (FloatKeyframe) Keyframe.ofFloat(1f, values[0]);
} else {
keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f, values[0]);
for (int i = 1; i < numKeyframes; ++i) {
keyframes[i] = (FloatKeyframe) Keyframe.ofFloat((float) i / (numKeyframes - 1), values[i]);
}
}
return new FloatKeyframeSet(keyframes);
}
public static KeyframeSet ofKeyframe(Keyframe... keyframes) {
// if all keyframes of same primitive type, create the appropriate KeyframeSet
int numKeyframes = keyframes.length;
boolean hasFloat = false;
boolean hasInt = false;
boolean hasOther = false;
for (int i = 0; i < numKeyframes; ++i) {
if (keyframes[i] instanceof FloatKeyframe) {
hasFloat = true;
} else if (keyframes[i] instanceof IntKeyframe) {
hasInt = true;
} else {
hasOther = true;
}
}
if (hasFloat && !hasInt && !hasOther) {
FloatKeyframe floatKeyframes[] = new FloatKeyframe[numKeyframes];
for (int i = 0; i < numKeyframes; ++i) {
floatKeyframes[i] = (FloatKeyframe) keyframes[i];
}
return new FloatKeyframeSet(floatKeyframes);
} else if (hasInt && !hasFloat && !hasOther) {
IntKeyframe intKeyframes[] = new IntKeyframe[numKeyframes];
for (int i = 0; i < numKeyframes; ++i) {
intKeyframes[i] = (IntKeyframe) keyframes[i];
}
return new IntKeyframeSet(intKeyframes);
} else {
return new KeyframeSet(keyframes);
}
}
public static KeyframeSet ofObject(Object... values) {
int numKeyframes = values.length;
ObjectKeyframe keyframes[] = new ObjectKeyframe[Math.max(numKeyframes, 2)];
if (numKeyframes == 1) {
keyframes[0] = (ObjectKeyframe) Keyframe.ofObject(0f);
keyframes[1] = (ObjectKeyframe) Keyframe.ofObject(1f, values[0]);
} else {
keyframes[0] = (ObjectKeyframe) Keyframe.ofObject(0f, values[0]);
for (int i = 1; i < numKeyframes; ++i) {
keyframes[i] = (ObjectKeyframe) Keyframe.ofObject((float) i / (numKeyframes - 1), values[i]);
}
}
return new KeyframeSet(keyframes);
}
/**
* Sets the TypeEvaluator to be used when calculating animated values. This object
* is required only for KeyframeSets that are not either IntKeyframeSet or FloatKeyframeSet,
* both of which assume their own evaluator to speed up calculations with those primitive
* types.
*
* @param evaluator The TypeEvaluator to be used to calculate animated values.
*/
public void setEvaluator(TypeEvaluator evaluator) {
mEvaluator = evaluator;
}
@Override
public KeyframeSet clone() {
ArrayList<Keyframe> keyframes = mKeyframes;
int numKeyframes = mKeyframes.size();
Keyframe[] newKeyframes = new Keyframe[numKeyframes];
for (int i = 0; i < numKeyframes; ++i) {
newKeyframes[i] = keyframes.get(i).clone();
}
KeyframeSet newSet = new KeyframeSet(newKeyframes);
return newSet;
}
/**
* Gets the animated value, given the elapsed fraction of the animation (interpolated by the
* animation's interpolator) and the evaluator used to calculate in-between values. This
* function maps the input fraction to the appropriate keyframe interval and a fraction
* between them and returns the interpolated value. Note that the input fraction may fall
* outside the [0-1] bounds, if the animation's interpolator made that happen (e.g., a
* spring interpolation that might send the fraction past 1.0). We handle this situation by
* just using the two keyframes at the appropriate end when the value is outside those bounds.
*
* @param fraction The elapsed fraction of the animation
* @return The animated value.
*/
public Object getValue(float fraction) {
// Special-case optimization for the common case of only two keyframes
if (mNumKeyframes == 2) {
if (mInterpolator != null) {
fraction = mInterpolator.getInterpolation(fraction);
}
return mEvaluator.evaluate(fraction, mFirstKeyframe.getValue(),
mLastKeyframe.getValue());
}
if (fraction <= 0f) {
final Keyframe nextKeyframe = mKeyframes.get(1);
final /*Time*/ Interpolator interpolator = nextKeyframe.getInterpolator();
if (interpolator != null) {
fraction = interpolator.getInterpolation(fraction);
}
final float prevFraction = mFirstKeyframe.getFraction();
float intervalFraction = (fraction - prevFraction) /
(nextKeyframe.getFraction() - prevFraction);
return mEvaluator.evaluate(intervalFraction, mFirstKeyframe.getValue(),
nextKeyframe.getValue());
} else if (fraction >= 1f) {
final Keyframe prevKeyframe = mKeyframes.get(mNumKeyframes - 2);
final /*Time*/ Interpolator interpolator = mLastKeyframe.getInterpolator();
if (interpolator != null) {
fraction = interpolator.getInterpolation(fraction);
}
final float prevFraction = prevKeyframe.getFraction();
float intervalFraction = (fraction - prevFraction) /
(mLastKeyframe.getFraction() - prevFraction);
return mEvaluator.evaluate(intervalFraction, prevKeyframe.getValue(),
mLastKeyframe.getValue());
}
Keyframe prevKeyframe = mFirstKeyframe;
for (int i = 1; i < mNumKeyframes; ++i) {
Keyframe nextKeyframe = mKeyframes.get(i);
if (fraction < nextKeyframe.getFraction()) {
final /*Time*/ Interpolator interpolator = nextKeyframe.getInterpolator();
if (interpolator != null) {
fraction = interpolator.getInterpolation(fraction);
}
final float prevFraction = prevKeyframe.getFraction();
float intervalFraction = (fraction - prevFraction) /
(nextKeyframe.getFraction() - prevFraction);
return mEvaluator.evaluate(intervalFraction, prevKeyframe.getValue(),
nextKeyframe.getValue());
}
prevKeyframe = nextKeyframe;
}
// shouldn't reach here
return mLastKeyframe.getValue();
}
@Override
public String toString() {
String returnVal = " ";
for (int i = 0; i < mNumKeyframes; ++i) {
returnVal += mKeyframes.get(i).getValue() + " ";
}
return returnVal;
}
}

View file

@ -0,0 +1,514 @@
/*
* Copyright (C) 2010 The Android 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.
*/
package com.adins.libs.nineoldandroids.animation;
import android.view.View;
import com.adins.libs.nineoldandroids.util.Property;
import com.adins.libs.nineoldandroids.view.animation.AnimatorProxy;
import com.adins.mss.foundation.camerainapp.helper.Logger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
/**
* This subclass of {@link ValueAnimator} provides support for animating properties on target objects.
* The constructors of this class take parameters to define the target object that will be animated
* as well as the name of the property that will be animated. Appropriate set/get functions
* are then determined internally and the animation will call these functions as necessary to
* animate the property.
*
* @see #setPropertyName(String)
*/
public final class ObjectAnimator extends ValueAnimator {
private static final boolean DBG = false;
private static final Map<String, Property> PROXY_PROPERTIES = new HashMap<String, Property>();
static {
PROXY_PROPERTIES.put("alpha", PreHoneycombCompat.ALPHA);
PROXY_PROPERTIES.put("pivotX", PreHoneycombCompat.PIVOT_X);
PROXY_PROPERTIES.put("pivotY", PreHoneycombCompat.PIVOT_Y);
PROXY_PROPERTIES.put("translationX", PreHoneycombCompat.TRANSLATION_X);
PROXY_PROPERTIES.put("translationY", PreHoneycombCompat.TRANSLATION_Y);
PROXY_PROPERTIES.put("rotation", PreHoneycombCompat.ROTATION);
PROXY_PROPERTIES.put("rotationX", PreHoneycombCompat.ROTATION_X);
PROXY_PROPERTIES.put("rotationY", PreHoneycombCompat.ROTATION_Y);
PROXY_PROPERTIES.put("scaleX", PreHoneycombCompat.SCALE_X);
PROXY_PROPERTIES.put("scaleY", PreHoneycombCompat.SCALE_Y);
PROXY_PROPERTIES.put("scrollX", PreHoneycombCompat.SCROLL_X);
PROXY_PROPERTIES.put("scrollY", PreHoneycombCompat.SCROLL_Y);
PROXY_PROPERTIES.put("x", PreHoneycombCompat.X);
PROXY_PROPERTIES.put("y", PreHoneycombCompat.Y);
}
// The target object on which the property exists, set in the constructor
private Object mTarget;
private String mPropertyName;
private Property mProperty;
/**
* Creates a new ObjectAnimator object. This default constructor is primarily for
* use internally; the other constructors which take parameters are more generally
* useful.
*/
public ObjectAnimator() {
}
/**
* Private utility constructor that initializes the target object and name of the
* property being animated.
*
* @param target The object whose property is to be animated. This object should
* have a public method on it called <code>setName()</code>, where <code>name</code> is
* the value of the <code>propertyName</code> parameter.
* @param propertyName The name of the property being animated.
*/
private ObjectAnimator(Object target, String propertyName) {
mTarget = target;
setPropertyName(propertyName);
}
/**
* Private utility constructor that initializes the target object and property being animated.
*
* @param target The object whose property is to be animated.
* @param property The property being animated.
*/
private <T> ObjectAnimator(T target, Property<T, ?> property) {
mTarget = target;
setProperty(property);
}
/**
* Constructs and returns an ObjectAnimator that animates between int values. A single
* value implies that that value is the one being animated to. Two values imply a starting
* and ending values. More than two values imply a starting value, values to animate through
* along the way, and an ending value (these values will be distributed evenly across
* the duration of the animation).
*
* @param target The object whose property is to be animated. This object should
* have a public method on it called <code>setName()</code>, where <code>name</code> is
* the value of the <code>propertyName</code> parameter.
* @param propertyName The name of the property being animated.
* @param values A set of values that the animation will animate between over time.
* @return An ObjectAnimator object that is set up to animate between the given values.
*/
public static ObjectAnimator ofInt(Object target, String propertyName, int... values) {
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
anim.setIntValues(values);
return anim;
}
/**
* Constructs and returns an ObjectAnimator that animates between int values. A single
* value implies that that value is the one being animated to. Two values imply a starting
* and ending values. More than two values imply a starting value, values to animate through
* along the way, and an ending value (these values will be distributed evenly across
* the duration of the animation).
*
* @param target The object whose property is to be animated.
* @param property The property being animated.
* @param values A set of values that the animation will animate between over time.
* @return An ObjectAnimator object that is set up to animate between the given values.
*/
public static <T> ObjectAnimator ofInt(T target, Property<T, Integer> property, int... values) {
ObjectAnimator anim = new ObjectAnimator(target, property);
anim.setIntValues(values);
return anim;
}
/**
* Constructs and returns an ObjectAnimator that animates between float values. A single
* value implies that that value is the one being animated to. Two values imply a starting
* and ending values. More than two values imply a starting value, values to animate through
* along the way, and an ending value (these values will be distributed evenly across
* the duration of the animation).
*
* @param target The object whose property is to be animated. This object should
* have a public method on it called <code>setName()</code>, where <code>name</code> is
* the value of the <code>propertyName</code> parameter.
* @param propertyName The name of the property being animated.
* @param values A set of values that the animation will animate between over time.
* @return An ObjectAnimator object that is set up to animate between the given values.
*/
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
anim.setFloatValues(values);
return anim;
}
/**
* Constructs and returns an ObjectAnimator that animates between float values. A single
* value implies that that value is the one being animated to. Two values imply a starting
* and ending values. More than two values imply a starting value, values to animate through
* along the way, and an ending value (these values will be distributed evenly across
* the duration of the animation).
*
* @param target The object whose property is to be animated.
* @param property The property being animated.
* @param values A set of values that the animation will animate between over time.
* @return An ObjectAnimator object that is set up to animate between the given values.
*/
public static <T> ObjectAnimator ofFloat(T target, Property<T, Float> property,
float... values) {
ObjectAnimator anim = new ObjectAnimator(target, property);
anim.setFloatValues(values);
return anim;
}
/**
* Constructs and returns an ObjectAnimator that animates between Object values. A single
* value implies that that value is the one being animated to. Two values imply a starting
* and ending values. More than two values imply a starting value, values to animate through
* along the way, and an ending value (these values will be distributed evenly across
* the duration of the animation).
*
* @param target The object whose property is to be animated. This object should
* have a public method on it called <code>setName()</code>, where <code>name</code> is
* the value of the <code>propertyName</code> parameter.
* @param propertyName The name of the property being animated.
* @param evaluator A TypeEvaluator that will be called on each animation frame to
* provide the necessary interpolation between the Object values to derive the animated
* value.
* @param values A set of values that the animation will animate between over time.
* @return An ObjectAnimator object that is set up to animate between the given values.
*/
public static ObjectAnimator ofObject(Object target, String propertyName,
TypeEvaluator evaluator, Object... values) {
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
anim.setObjectValues(values);
anim.setEvaluator(evaluator);
return anim;
}
/**
* Constructs and returns an ObjectAnimator that animates between Object values. A single
* value implies that that value is the one being animated to. Two values imply a starting
* and ending values. More than two values imply a starting value, values to animate through
* along the way, and an ending value (these values will be distributed evenly across
* the duration of the animation).
*
* @param target The object whose property is to be animated.
* @param property The property being animated.
* @param evaluator A TypeEvaluator that will be called on each animation frame to
* provide the necessary interpolation between the Object values to derive the animated
* value.
* @param values A set of values that the animation will animate between over time.
* @return An ObjectAnimator object that is set up to animate between the given values.
*/
public static <T, V> ObjectAnimator ofObject(T target, Property<T, V> property,
TypeEvaluator<V> evaluator, V... values) {
ObjectAnimator anim = new ObjectAnimator(target, property);
anim.setObjectValues(values);
anim.setEvaluator(evaluator);
return anim;
}
/**
* Constructs and returns an ObjectAnimator that animates between the sets of values specified
* in <code>PropertyValueHolder</code> objects. This variant should be used when animating
* several properties at once with the same ObjectAnimator, since PropertyValuesHolder allows
* you to associate a set of animation values with a property name.
*
* @param target The object whose property is to be animated. Depending on how the
* PropertyValuesObjects were constructed, the target object should either have the {@link
* android.util.Property} objects used to construct the PropertyValuesHolder objects or (if the
* PropertyValuesHOlder objects were created with property names) the target object should have
* public methods on it called <code>setName()</code>, where <code>name</code> is the name of
* the property passed in as the <code>propertyName</code> parameter for each of the
* PropertyValuesHolder objects.
* @param values A set of PropertyValuesHolder objects whose values will be animated between
* over time.
* @return An ObjectAnimator object that is set up to animate between the given values.
*/
public static ObjectAnimator ofPropertyValuesHolder(Object target,
PropertyValuesHolder... values) {
ObjectAnimator anim = new ObjectAnimator();
anim.mTarget = target;
anim.setValues(values);
return anim;
}
/**
* Sets the property that will be animated. Property objects will take precedence over
* properties specified by the {@link #setPropertyName(String)} method. Animations should
* be set up to use one or the other, not both.
*
* @param property The property being animated. Should not be null.
*/
public void setProperty(Property property) {
// mValues could be null if this is being constructed piecemeal. Just record the
// propertyName to be used later when setValues() is called if so.
if (mValues != null) {
PropertyValuesHolder valuesHolder = mValues[0];
String oldName = valuesHolder.getPropertyName();
valuesHolder.setProperty(property);
mValuesMap.remove(oldName);
mValuesMap.put(mPropertyName, valuesHolder);
}
if (mProperty != null) {
mPropertyName = property.getName();
}
mProperty = property;
// New property/values/target should cause re-initialization prior to starting
mInitialized = false;
}
/**
* Gets the name of the property that will be animated. This name will be used to derive
* a setter function that will be called to set animated values.
* For example, a property name of <code>foo</code> will result
* in a call to the function <code>setFoo()</code> on the target object. If either
* <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
* also be derived and called.
*/
public String getPropertyName() {
return mPropertyName;
}
/**
* Sets the name of the property that will be animated. This name is used to derive
* a setter function that will be called to set animated values.
* For example, a property name of <code>foo</code> will result
* in a call to the function <code>setFoo()</code> on the target object. If either
* <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
* also be derived and called.
* <p>
* <p>For best performance of the mechanism that calls the setter function determined by the
* name of the property being animated, use <code>float</code> or <code>int</code> typed values,
* and make the setter function for those properties have a <code>void</code> return value. This
* will cause the code to take an optimized path for these constrained circumstances. Other
* property types and return types will work, but will have more overhead in processing
* the requests due to normal reflection mechanisms.</p>
* <p>
* <p>Note that the setter function derived from this property name
* must take the same parameter type as the
* <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to
* the setter function will fail.</p>
* <p>
* <p>If this ObjectAnimator has been set up to animate several properties together,
* using more than one PropertyValuesHolder objects, then setting the propertyName simply
* sets the propertyName in the first of those PropertyValuesHolder objects.</p>
*
* @param propertyName The name of the property being animated. Should not be null.
*/
public void setPropertyName(String propertyName) {
// mValues could be null if this is being constructed piecemeal. Just record the
// propertyName to be used later when setValues() is called if so.
if (mValues != null) {
PropertyValuesHolder valuesHolder = mValues[0];
String oldName = valuesHolder.getPropertyName();
valuesHolder.setPropertyName(propertyName);
mValuesMap.remove(oldName);
mValuesMap.put(propertyName, valuesHolder);
}
mPropertyName = propertyName;
// New property/values/target should cause re-initialization prior to starting
mInitialized = false;
}
@Override
public void setIntValues(int... values) {
if (mValues == null || mValues.length == 0) {
// No values yet - this animator is being constructed piecemeal. Init the values with
// whatever the current propertyName is
if (mProperty != null) {
setValues(PropertyValuesHolder.ofInt(mProperty, values));
} else {
setValues(PropertyValuesHolder.ofInt(mPropertyName, values));
}
} else {
super.setIntValues(values);
}
}
@Override
public void setFloatValues(float... values) {
if (mValues == null || mValues.length == 0) {
// No values yet - this animator is being constructed piecemeal. Init the values with
// whatever the current propertyName is
if (mProperty != null) {
setValues(PropertyValuesHolder.ofFloat(mProperty, values));
} else {
setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
}
} else {
super.setFloatValues(values);
}
}
@Override
public void setObjectValues(Object... values) {
if (mValues == null || mValues.length == 0) {
// No values yet - this animator is being constructed piecemeal. Init the values with
// whatever the current propertyName is
if (mProperty != null) {
setValues(PropertyValuesHolder.ofObject(mProperty, (TypeEvaluator) null, values));
} else {
setValues(PropertyValuesHolder.ofObject(mPropertyName, (TypeEvaluator) null, values));
}
} else {
super.setObjectValues(values);
}
}
@Override
public void start() {
if (DBG) {
Logger.d("ObjectAnimator", "Anim target, duration: " + mTarget + ", " + getDuration());
for (int i = 0; i < mValues.length; ++i) {
PropertyValuesHolder pvh = mValues[i];
ArrayList<Keyframe> keyframes = pvh.mKeyframeSet.mKeyframes;
Logger.d("ObjectAnimator", " Values[" + i + "]: " +
pvh.getPropertyName() + ", " + keyframes.get(0).getValue() + ", " +
keyframes.get(pvh.mKeyframeSet.mNumKeyframes - 1).getValue());
}
}
super.start();
}
/**
* This function is called immediately before processing the first animation
* frame of an animation. If there is a nonzero <code>startDelay</code>, the
* function is called after that delay ends.
* It takes care of the final initialization steps for the
* animation. This includes setting mEvaluator, if the user has not yet
* set it up, and the setter/getter methods, if the user did not supply
* them.
* <p>
* <p>Overriders of this method should call the superclass method to cause
* internal mechanisms to be set up correctly.</p>
*/
@Override
void initAnimation() {
if (!mInitialized) {
// mValueType may change due to setter/getter setup; do this before calling super.init(),
// which uses mValueType to set up the default type evaluator.
if ((mProperty == null) && AnimatorProxy.NEEDS_PROXY && (mTarget instanceof View) && PROXY_PROPERTIES.containsKey(mPropertyName)) {
setProperty(PROXY_PROPERTIES.get(mPropertyName));
}
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].setupSetterAndGetter(mTarget);
}
super.initAnimation();
}
}
/**
* Sets the length of the animation. The default duration is 300 milliseconds.
*
* @param duration The length of the animation, in milliseconds.
* @return ObjectAnimator The object called with setDuration(). This return
* value makes it easier to compose statements together that construct and then set the
* duration, as in
* <code>ObjectAnimator.ofInt(target, propertyName, 0, 10).setDuration(500).start()</code>.
*/
@Override
public ObjectAnimator setDuration(long duration) {
super.setDuration(duration);
return this;
}
/**
* The target object whose property will be animated by this animation
*
* @return The object being animated
*/
public Object getTarget() {
return mTarget;
}
/**
* Sets the target object whose property will be animated by this animation
*
* @param target The object being animated
*/
@Override
public void setTarget(Object target) {
if (mTarget != target) {
final Object oldTarget = mTarget;
mTarget = target;
if (oldTarget != null && target != null && oldTarget.getClass() == target.getClass()) {
return;
}
// New target type should cause re-initialization prior to starting
mInitialized = false;
}
}
@Override
public void setupStartValues() {
initAnimation();
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].setupStartValue(mTarget);
}
}
@Override
public void setupEndValues() {
initAnimation();
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].setupEndValue(mTarget);
}
}
/**
* This method is called with the elapsed fraction of the animation during every
* animation frame. This function turns the elapsed fraction into an interpolated fraction
* and then into an animated value (from the evaluator. The function is called mostly during
* animation updates, but it is also called when the <code>end()</code>
* function is called, to set the final value on the property.
* <p>
* <p>Overrides of this method must call the superclass to perform the calculation
* of the animated value.</p>
*
* @param fraction The elapsed fraction of the animation.
*/
@Override
void animateValue(float fraction) {
super.animateValue(fraction);
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].setAnimatedValue(mTarget);
}
}
@Override
public ObjectAnimator clone() {
final ObjectAnimator anim = (ObjectAnimator) super.clone();
return anim;
}
@Override
public String toString() {
String returnVal = "ObjectAnimator@" + Integer.toHexString(hashCode()) + ", target " +
mTarget;
if (mValues != null) {
for (int i = 0; i < mValues.length; ++i) {
returnVal += "\n " + mValues[i].toString();
}
}
return returnVal;
}
}

View file

@ -0,0 +1,170 @@
package com.adins.libs.nineoldandroids.animation;
import android.view.View;
import com.adins.libs.nineoldandroids.util.FloatProperty;
import com.adins.libs.nineoldandroids.util.IntProperty;
import com.adins.libs.nineoldandroids.util.Property;
import com.adins.libs.nineoldandroids.view.animation.AnimatorProxy;
final class PreHoneycombCompat {
static Property<View, Float> ALPHA = new FloatProperty<View>("alpha") {
@Override
public void setValue(View object, float value) {
AnimatorProxy.wrap(object).setAlpha(value);
}
@Override
public Float get(View object) {
return AnimatorProxy.wrap(object).getAlpha();
}
};
static Property<View, Float> PIVOT_X = new FloatProperty<View>("pivotX") {
@Override
public void setValue(View object, float value) {
AnimatorProxy.wrap(object).setPivotX(value);
}
@Override
public Float get(View object) {
return AnimatorProxy.wrap(object).getPivotX();
}
};
static Property<View, Float> PIVOT_Y = new FloatProperty<View>("pivotY") {
@Override
public void setValue(View object, float value) {
AnimatorProxy.wrap(object).setPivotY(value);
}
@Override
public Float get(View object) {
return AnimatorProxy.wrap(object).getPivotY();
}
};
static Property<View, Float> TRANSLATION_X = new FloatProperty<View>("translationX") {
@Override
public void setValue(View object, float value) {
AnimatorProxy.wrap(object).setTranslationX(value);
}
@Override
public Float get(View object) {
return AnimatorProxy.wrap(object).getTranslationX();
}
};
static Property<View, Float> TRANSLATION_Y = new FloatProperty<View>("translationY") {
@Override
public void setValue(View object, float value) {
AnimatorProxy.wrap(object).setTranslationY(value);
}
@Override
public Float get(View object) {
return AnimatorProxy.wrap(object).getTranslationY();
}
};
static Property<View, Float> ROTATION = new FloatProperty<View>("rotation") {
@Override
public void setValue(View object, float value) {
AnimatorProxy.wrap(object).setRotation(value);
}
@Override
public Float get(View object) {
return AnimatorProxy.wrap(object).getRotation();
}
};
static Property<View, Float> ROTATION_X = new FloatProperty<View>("rotationX") {
@Override
public void setValue(View object, float value) {
AnimatorProxy.wrap(object).setRotationX(value);
}
@Override
public Float get(View object) {
return AnimatorProxy.wrap(object).getRotationX();
}
};
static Property<View, Float> ROTATION_Y = new FloatProperty<View>("rotationY") {
@Override
public void setValue(View object, float value) {
AnimatorProxy.wrap(object).setRotationY(value);
}
@Override
public Float get(View object) {
return AnimatorProxy.wrap(object).getRotationY();
}
};
static Property<View, Float> SCALE_X = new FloatProperty<View>("scaleX") {
@Override
public void setValue(View object, float value) {
AnimatorProxy.wrap(object).setScaleX(value);
}
@Override
public Float get(View object) {
return AnimatorProxy.wrap(object).getScaleX();
}
};
static Property<View, Float> SCALE_Y = new FloatProperty<View>("scaleY") {
@Override
public void setValue(View object, float value) {
AnimatorProxy.wrap(object).setScaleY(value);
}
@Override
public Float get(View object) {
return AnimatorProxy.wrap(object).getScaleY();
}
};
static Property<View, Integer> SCROLL_X = new IntProperty<View>("scrollX") {
@Override
public void setValue(View object, int value) {
AnimatorProxy.wrap(object).setScrollX(value);
}
@Override
public Integer get(View object) {
return AnimatorProxy.wrap(object).getScrollX();
}
};
static Property<View, Integer> SCROLL_Y = new IntProperty<View>("scrollY") {
@Override
public void setValue(View object, int value) {
AnimatorProxy.wrap(object).setScrollY(value);
}
@Override
public Integer get(View object) {
return AnimatorProxy.wrap(object).getScrollY();
}
};
static Property<View, Float> X = new FloatProperty<View>("x") {
@Override
public void setValue(View object, float value) {
AnimatorProxy.wrap(object).setX(value);
}
@Override
public Float get(View object) {
return AnimatorProxy.wrap(object).getX();
}
};
static Property<View, Float> Y = new FloatProperty<View>("y") {
@Override
public void setValue(View object, float value) {
AnimatorProxy.wrap(object).setY(value);
}
@Override
public Float get(View object) {
return AnimatorProxy.wrap(object).getY();
}
};
//No instances
private PreHoneycombCompat() {
}
}

View file

@ -0,0 +1,78 @@
package com.adins.libs.nineoldandroids.animation;
/**
* This class provides a simple callback mechanism to listeners that is synchronized with other
* animators in the system. There is no duration, interpolation, or object value-setting
* with this Animator. Instead, it is simply started and proceeds to send out events on every
* animation frame to its TimeListener (if set), with information about this animator,
* the total elapsed time, and the time since the last animation frame.
*
* @hide
*/
public class TimeAnimator extends ValueAnimator {
private TimeListener mListener;
private long mPreviousTime = -1;
@Override
boolean animationFrame(long currentTime) {
if (mPlayingState == STOPPED) {
mPlayingState = RUNNING;
if (mSeekTime < 0) {
mStartTime = currentTime;
} else {
mStartTime = currentTime - mSeekTime;
// Now that we're playing, reset the seek time
mSeekTime = -1;
}
}
if (mListener != null) {
long totalTime = currentTime - mStartTime;
long deltaTime = (mPreviousTime < 0) ? 0 : (currentTime - mPreviousTime);
mPreviousTime = currentTime;
mListener.onTimeUpdate(this, totalTime, deltaTime);
}
return false;
}
/**
* Sets a listener that is sent update events throughout the life of
* an animation.
*
* @param listener the listener to be set.
*/
public void setTimeListener(TimeListener listener) {
mListener = listener;
}
@Override
void animateValue(float fraction) {
// Noop
}
@Override
void initAnimation() {
// noop
}
/**
* Implementors of this interface can set themselves as update listeners
* to a <code>TimeAnimator</code> instance to receive callbacks on every animation
* frame to receive the total time since the animator started and the delta time
* since the last frame. The first time the listener is called, totalTime and
* deltaTime should both be zero.
*
* @hide
*/
public static interface TimeListener {
/**
* <p>Notifies listeners of the occurrence of another frame of the animation,
* along with information about the elapsed time.</p>
*
* @param animation The animator sending out the notification.
* @param totalTime The
*/
void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime);
}
}

View file

@ -0,0 +1,44 @@
/*
* Copyright (C) 2010 The Android 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.
*/
package com.adins.libs.nineoldandroids.animation;
/**
* Interface for use with the {@link ValueAnimator#setEvaluator(TypeEvaluator)} function. Evaluators
* allow developers to create animations on arbitrary property types, by allowing them to supply
* custom evaulators for types that are not automatically understood and used by the animation
* system.
*
* @see ValueAnimator#setEvaluator(TypeEvaluator)
*/
public interface TypeEvaluator<T> {
/**
* This function returns the result of linearly interpolating the start and end values, with
* <code>fraction</code> representing the proportion between the start and end values. The
* calculation is a simple parametric calculation: <code>result = x0 + t * (v1 - v0)</code>,
* where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>,
* and <code>t</code> is <code>fraction</code>.
*
* @param fraction The fraction from the starting to the ending values
* @param startValue The start value.
* @param endValue The end value.
* @return A linear interpolation between the start and end values, given the
* <code>fraction</code> parameter.
*/
public T evaluate(float fraction, T startValue, T endValue);
}

View file

@ -0,0 +1,45 @@
/*
* Copyright (C) 2011 The Android 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.
*/
package com.adins.libs.nineoldandroids.util;
/**
* An implementation of {@link android.util.Property} to be used specifically with fields of type
* <code>float</code>. This type-specific subclass enables performance benefit by allowing
* calls to a {@link #set(Object, Float) set()} function that takes the primitive
* <code>float</code> type and avoids autoboxing and other overhead associated with the
* <code>Float</code> class.
*
* @param <T> The class on which the Property is declared.
* @hide
*/
public abstract class FloatProperty<T> extends Property<T, Float> {
public FloatProperty(String name) {
super(Float.class, name);
}
/**
* A type-specific override of the {@link #set(Object, Float)} that is faster when dealing
* with fields of type <code>float</code>.
*/
public abstract void setValue(T object, float value);
@Override
final public void set(T object, Float value) {
setValue(object, value);
}
}

View file

@ -0,0 +1,45 @@
/*
* Copyright (C) 2011 The Android 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.
*/
package com.adins.libs.nineoldandroids.util;
/**
* An implementation of {@link android.util.Property} to be used specifically with fields of type
* <code>int</code>. This type-specific subclass enables performance benefit by allowing
* calls to a {@link #set(Object, Integer) set()} function that takes the primitive
* <code>int</code> type and avoids autoboxing and other overhead associated with the
* <code>Integer</code> class.
*
* @param <T> The class on which the Property is declared.
* @hide
*/
public abstract class IntProperty<T> extends Property<T, Integer> {
public IntProperty(String name) {
super(Integer.class, name);
}
/**
* A type-specific override of the {@link #set(Object, Integer)} that is faster when dealing
* with fields of type <code>int</code>.
*/
public abstract void setValue(T object, int value);
@Override
final public void set(T object, Integer value) {
set(object, value.intValue());
}
}

View file

@ -0,0 +1,30 @@
/*
* Copyright (C) 2011 The Android 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.
*/
package com.adins.libs.nineoldandroids.util;
/**
* Thrown when code requests a {@link Property} on a class that does
* not expose the appropriate method or field.
*
* @see Property#of(java.lang.Class, java.lang.Class, java.lang.String)
*/
public class NoSuchPropertyException extends RuntimeException {
public NoSuchPropertyException(String s) {
super(s);
}
}

View file

@ -0,0 +1,106 @@
/*
* Copyright (C) 2011 The Android 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.
*/
package com.adins.libs.nineoldandroids.util;
/**
* A property is an abstraction that can be used to represent a <emb>mutable</em> value that is held
* in a <em>host</em> object. The Property's {@link #set(Object, Object)} or {@link #get(Object)}
* methods can be implemented in terms of the private fields of the host object, or via "setter" and
* "getter" methods or by some other mechanism, as appropriate.
*
* @param <T> The class on which the property is declared.
* @param <V> The type that this property represents.
*/
public abstract class Property<T, V> {
private final String mName;
private final Class<V> mType;
/**
* A constructor that takes an identifying name and {@link #getType() type} for the property.
*/
public Property(Class<V> type, String name) {
mName = name;
mType = type;
}
/**
* This factory method creates and returns a Property given the <code>class</code> and
* <code>name</code> parameters, where the <code>"name"</code> parameter represents either:
* <ul>
* <li>a public <code>getName()</code> method on the class which takes no arguments, plus an
* optional public <code>setName()</code> method which takes a value of the same type
* returned by <code>getName()</code>
* <li>a public <code>isName()</code> method on the class which takes no arguments, plus an
* optional public <code>setName()</code> method which takes a value of the same type
* returned by <code>isName()</code>
* <li>a public <code>name</code> field on the class
* </ul>
* <p>
* <p>If either of the get/is method alternatives is found on the class, but an appropriate
* <code>setName()</code> method is not found, the <code>Property</code> will be
* {@link #isReadOnly() readOnly}. Calling the {@link #set(Object, Object)} method on such
* a property is allowed, but will have no effect.</p>
* <p>
* <p>If neither the methods nor the field are found on the class a
* {@link NoSuchPropertyException} exception will be thrown.</p>
*/
public static <T, V> Property<T, V> of(Class<T> hostType, Class<V> valueType, String name) {
return new ReflectiveProperty<T, V>(hostType, valueType, name);
}
/**
* Returns true if the {@link #set(Object, Object)} method does not set the value on the target
* object (in which case the {@link #set(Object, Object) set()} method should throw a {@link
* NoSuchPropertyException} exception). This may happen if the Property wraps functionality that
* allows querying the underlying value but not setting it. For example, the {@link #of(Class,
* Class, String)} factory method may return a Property with name "foo" for an object that has
* only a <code>getFoo()</code> or <code>isFoo()</code> method, but no matching
* <code>setFoo()</code> method.
*/
public boolean isReadOnly() {
return false;
}
/**
* Sets the value on <code>object</code> which this property represents. If the method is unable
* to set the value on the target object it will throw an {@link UnsupportedOperationException}
* exception.
*/
public void set(T object, V value) {
throw new UnsupportedOperationException("Property " + getName() + " is read-only");
}
/**
* Returns the current value that this property represents on the given <code>object</code>.
*/
public abstract V get(T object);
/**
* Returns the name for this property.
*/
public String getName() {
return mName;
}
/**
* Returns the type for this property.
*/
public Class<V> getType() {
return mType;
}
}

View file

@ -0,0 +1,182 @@
/*
* Copyright (C) 2011 The Android 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.
*/
package com.adins.libs.nineoldandroids.util;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* Internal class to automatically generate a Property for a given class/name pair, given the
* specification of {@link Property#of(java.lang.Class, java.lang.Class, java.lang.String)}
*/
class ReflectiveProperty<T, V> extends Property<T, V> {
private static final String PREFIX_GET = "get";
private static final String PREFIX_IS = "is";
private static final String PREFIX_SET = "set";
private Method mSetter;
private Method mGetter;
private Field mField;
/**
* For given property name 'name', look for getName/isName method or 'name' field.
* Also look for setName method (optional - could be readonly). Failing method getters and
* field results in throwing NoSuchPropertyException.
*
* @param propertyHolder The class on which the methods or field are found
* @param name The name of the property, where this name is capitalized and appended to
* "get" and "is to search for the appropriate methods. If the get/is methods are not found,
* the constructor will search for a field with that exact name.
*/
public ReflectiveProperty(Class<T> propertyHolder, Class<V> valueType, String name) {
// TODO: cache reflection info for each new class/name pair
super(valueType, name);
char firstLetter = Character.toUpperCase(name.charAt(0));
String theRest = name.substring(1);
String capitalizedName = firstLetter + theRest;
String getterName = PREFIX_GET + capitalizedName;
try {
mGetter = propertyHolder.getMethod(getterName, (Class<?>[]) null);
} catch (NoSuchMethodException e) {
try {
/* The native implementation uses JNI to do reflection, which allows access to private methods.
* getDeclaredMethod(..) does not find superclass methods, so it's implemented as a fallback.
*/
mGetter = propertyHolder.getDeclaredMethod(getterName, (Class<?>[]) null);
mGetter.setAccessible(true);
} catch (NoSuchMethodException e2) {
// getName() not available - try isName() instead
getterName = PREFIX_IS + capitalizedName;
try {
mGetter = propertyHolder.getMethod(getterName, (Class<?>[]) null);
} catch (NoSuchMethodException e3) {
try {
/* The native implementation uses JNI to do reflection, which allows access to private methods.
* getDeclaredMethod(..) does not find superclass methods, so it's implemented as a fallback.
*/
mGetter = propertyHolder.getDeclaredMethod(getterName, (Class<?>[]) null);
mGetter.setAccessible(true);
} catch (NoSuchMethodException e4) {
// Try public field instead
try {
mField = propertyHolder.getField(name);
Class fieldType = mField.getType();
if (!typesMatch(valueType, fieldType)) {
throw new NoSuchPropertyException("Underlying type (" + fieldType + ") " +
"does not match Property type (" + valueType + ")");
}
return;
} catch (NoSuchFieldException e5) {
// no way to access property - throw appropriate exception
throw new NoSuchPropertyException("No accessor method or field found for"
+ " property with name " + name);
}
}
}
}
}
Class getterType = mGetter.getReturnType();
// Check to make sure our getter type matches our valueType
if (!typesMatch(valueType, getterType)) {
throw new NoSuchPropertyException("Underlying type (" + getterType + ") " +
"does not match Property type (" + valueType + ")");
}
String setterName = PREFIX_SET + capitalizedName;
try {
// mSetter = propertyHolder.getMethod(setterName, getterType);
// The native implementation uses JNI to do reflection, which allows access to private methods.
mSetter = propertyHolder.getDeclaredMethod(setterName, getterType);
mSetter.setAccessible(true);
} catch (NoSuchMethodException ignored) {
// Okay to not have a setter - just a readonly property
}
}
/**
* Utility method to check whether the type of the underlying field/method on the target
* object matches the type of the Property. The extra checks for primitive types are because
* generics will force the Property type to be a class, whereas the type of the underlying
* method/field will probably be a primitive type instead. Accept float as matching Float,
* etc.
*/
private boolean typesMatch(Class<V> valueType, Class getterType) {
if (getterType != valueType) {
if (getterType.isPrimitive()) {
return (getterType == float.class && valueType == Float.class) ||
(getterType == int.class && valueType == Integer.class) ||
(getterType == boolean.class && valueType == Boolean.class) ||
(getterType == long.class && valueType == Long.class) ||
(getterType == double.class && valueType == Double.class) ||
(getterType == short.class && valueType == Short.class) ||
(getterType == byte.class && valueType == Byte.class) ||
(getterType == char.class && valueType == Character.class);
}
return false;
}
return true;
}
@Override
public void set(T object, V value) {
if (mSetter != null) {
try {
mSetter.invoke(object, value);
} catch (IllegalAccessException e) {
throw new AssertionError();
} catch (InvocationTargetException e) {
throw new RuntimeException(e.getCause());
}
} else if (mField != null) {
try {
mField.set(object, value);
} catch (IllegalAccessException e) {
throw new AssertionError();
}
} else {
throw new UnsupportedOperationException("Property " + getName() + " is read-only");
}
}
@Override
public V get(T object) {
if (mGetter != null) {
try {
return (V) mGetter.invoke(object, (Object[]) null);
} catch (IllegalAccessException e) {
throw new AssertionError();
} catch (InvocationTargetException e) {
throw new RuntimeException(e.getCause());
}
} else if (mField != null) {
try {
return (V) mField.get(object);
} catch (IllegalAccessException e) {
throw new AssertionError();
}
}
// Should not get here: there should always be a non-null getter or field
throw new AssertionError();
}
/**
* Returns false if there is no setter or public field underlying this Property.
*/
@Override
public boolean isReadOnly() {
return (mSetter == null && mField == null);
}
}

View file

@ -0,0 +1,293 @@
package com.adins.libs.nineoldandroids.view;
import android.view.View;
import static com.adins.libs.nineoldandroids.view.animation.AnimatorProxy.NEEDS_PROXY;
import static com.adins.libs.nineoldandroids.view.animation.AnimatorProxy.wrap;
public final class ViewHelper {
private ViewHelper() {
}
public static float getAlpha(View view) {
return NEEDS_PROXY ? wrap(view).getAlpha() : Honeycomb.getAlpha(view);
}
public static void setAlpha(View view, float alpha) {
if (NEEDS_PROXY) {
wrap(view).setAlpha(alpha);
} else {
Honeycomb.setAlpha(view, alpha);
}
}
public static float getPivotX(View view) {
return NEEDS_PROXY ? wrap(view).getPivotX() : Honeycomb.getPivotX(view);
}
public static void setPivotX(View view, float pivotX) {
if (NEEDS_PROXY) {
wrap(view).setPivotX(pivotX);
} else {
Honeycomb.setPivotX(view, pivotX);
}
}
public static float getPivotY(View view) {
return NEEDS_PROXY ? wrap(view).getPivotY() : Honeycomb.getPivotY(view);
}
public static void setPivotY(View view, float pivotY) {
if (NEEDS_PROXY) {
wrap(view).setPivotY(pivotY);
} else {
Honeycomb.setPivotY(view, pivotY);
}
}
public static float getRotation(View view) {
return NEEDS_PROXY ? wrap(view).getRotation() : Honeycomb.getRotation(view);
}
public static void setRotation(View view, float rotation) {
if (NEEDS_PROXY) {
wrap(view).setRotation(rotation);
} else {
Honeycomb.setRotation(view, rotation);
}
}
public static float getRotationX(View view) {
return NEEDS_PROXY ? wrap(view).getRotationX() : Honeycomb.getRotationX(view);
}
public static void setRotationX(View view, float rotationX) {
if (NEEDS_PROXY) {
wrap(view).setRotationX(rotationX);
} else {
Honeycomb.setRotationX(view, rotationX);
}
}
public static float getRotationY(View view) {
return NEEDS_PROXY ? wrap(view).getRotationY() : Honeycomb.getRotationY(view);
}
public static void setRotationY(View view, float rotationY) {
if (NEEDS_PROXY) {
wrap(view).setRotationY(rotationY);
} else {
Honeycomb.setRotationY(view, rotationY);
}
}
public static float getScaleX(View view) {
return NEEDS_PROXY ? wrap(view).getScaleX() : Honeycomb.getScaleX(view);
}
public static void setScaleX(View view, float scaleX) {
if (NEEDS_PROXY) {
wrap(view).setScaleX(scaleX);
} else {
Honeycomb.setScaleX(view, scaleX);
}
}
public static float getScaleY(View view) {
return NEEDS_PROXY ? wrap(view).getScaleY() : Honeycomb.getScaleY(view);
}
public static void setScaleY(View view, float scaleY) {
if (NEEDS_PROXY) {
wrap(view).setScaleY(scaleY);
} else {
Honeycomb.setScaleY(view, scaleY);
}
}
public static float getScrollX(View view) {
return NEEDS_PROXY ? wrap(view).getScrollX() : Honeycomb.getScrollX(view);
}
public static void setScrollX(View view, int scrollX) {
if (NEEDS_PROXY) {
wrap(view).setScrollX(scrollX);
} else {
Honeycomb.setScrollX(view, scrollX);
}
}
public static float getScrollY(View view) {
return NEEDS_PROXY ? wrap(view).getScrollY() : Honeycomb.getScrollY(view);
}
public static void setScrollY(View view, int scrollY) {
if (NEEDS_PROXY) {
wrap(view).setScrollY(scrollY);
} else {
Honeycomb.setScrollY(view, scrollY);
}
}
public static float getTranslationX(View view) {
return NEEDS_PROXY ? wrap(view).getTranslationX() : Honeycomb.getTranslationX(view);
}
public static void setTranslationX(View view, float translationX) {
if (NEEDS_PROXY) {
wrap(view).setTranslationX(translationX);
} else {
Honeycomb.setTranslationX(view, translationX);
}
}
public static float getTranslationY(View view) {
return NEEDS_PROXY ? wrap(view).getTranslationY() : Honeycomb.getTranslationY(view);
}
public static void setTranslationY(View view, float translationY) {
if (NEEDS_PROXY) {
wrap(view).setTranslationY(translationY);
} else {
Honeycomb.setTranslationY(view, translationY);
}
}
public static float getX(View view) {
return NEEDS_PROXY ? wrap(view).getX() : Honeycomb.getX(view);
}
public static void setX(View view, float x) {
if (NEEDS_PROXY) {
wrap(view).setX(x);
} else {
Honeycomb.setX(view, x);
}
}
public static float getY(View view) {
return NEEDS_PROXY ? wrap(view).getY() : Honeycomb.getY(view);
}
public static void setY(View view, float y) {
if (NEEDS_PROXY) {
wrap(view).setY(y);
} else {
Honeycomb.setY(view, y);
}
}
private static final class Honeycomb {
static float getAlpha(View view) {
return view.getAlpha();
}
static void setAlpha(View view, float alpha) {
view.setAlpha(alpha);
}
static float getPivotX(View view) {
return view.getPivotX();
}
static void setPivotX(View view, float pivotX) {
view.setPivotX(pivotX);
}
static float getPivotY(View view) {
return view.getPivotY();
}
static void setPivotY(View view, float pivotY) {
view.setPivotY(pivotY);
}
static float getRotation(View view) {
return view.getRotation();
}
static void setRotation(View view, float rotation) {
view.setRotation(rotation);
}
static float getRotationX(View view) {
return view.getRotationX();
}
static void setRotationX(View view, float rotationX) {
view.setRotationX(rotationX);
}
static float getRotationY(View view) {
return view.getRotationY();
}
static void setRotationY(View view, float rotationY) {
view.setRotationY(rotationY);
}
static float getScaleX(View view) {
return view.getScaleX();
}
static void setScaleX(View view, float scaleX) {
view.setScaleX(scaleX);
}
static float getScaleY(View view) {
return view.getScaleY();
}
static void setScaleY(View view, float scaleY) {
view.setScaleY(scaleY);
}
static float getScrollX(View view) {
return view.getScrollX();
}
static void setScrollX(View view, int scrollX) {
view.setScrollX(scrollX);
}
static float getScrollY(View view) {
return view.getScrollY();
}
static void setScrollY(View view, int scrollY) {
view.setScrollY(scrollY);
}
static float getTranslationX(View view) {
return view.getTranslationX();
}
static void setTranslationX(View view, float translationX) {
view.setTranslationX(translationX);
}
static float getTranslationY(View view) {
return view.getTranslationY();
}
static void setTranslationY(View view, float translationY) {
view.setTranslationY(translationY);
}
static float getX(View view) {
return view.getX();
}
static void setX(View view, float x) {
view.setX(x);
}
static float getY(View view) {
return view.getY();
}
static void setY(View view, float y) {
view.setY(y);
}
}
}

View file

@ -0,0 +1,348 @@
/*
* Copyright (C) 2011 The Android 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.
*/
package com.adins.libs.nineoldandroids.view;
import android.os.Build;
import android.view.View;
import android.view.animation.Interpolator;
import com.adins.libs.nineoldandroids.animation.Animator;
import java.util.WeakHashMap;
/**
* This class enables automatic and optimized animation of select properties on View objects.
* If only one or two properties on a View object are being animated, then using an
* {@link android.animation.ObjectAnimator} is fine; the property setters called by ObjectAnimator
* are well equipped to do the right thing to set the property and invalidate the view
* appropriately. But if several properties are animated simultaneously, or if you just want a
* more convenient syntax to animate a specific property, then ViewPropertyAnimator might be
* more well-suited to the task.
* <p>
* <p>This class may provide better performance for several simultaneous animations, because
* it will optimize invalidate calls to take place only once for several properties instead of each
* animated property independently causing its own invalidation. Also, the syntax of using this
* class could be easier to use because the caller need only tell the View object which
* property to animate, and the value to animate either to or by, and this class handles the
* details of configuring the underlying Animator class and starting it.</p>
* <p>
* <p>This class is not constructed by the caller, but rather by the View whose properties
* it will animate. Calls to {@link android.view.View#animate()} will return a reference
* to the appropriate ViewPropertyAnimator object for that View.</p>
*/
public abstract class ViewPropertyAnimator {
private static final WeakHashMap<View, ViewPropertyAnimator> ANIMATORS =
new WeakHashMap<View, ViewPropertyAnimator>(0);
/**
* This method returns a ViewPropertyAnimator object, which can be used to animate specific
* properties on this View.
*
* @param view View to animate.
* @return The ViewPropertyAnimator associated with this View.
*/
public static ViewPropertyAnimator animate(View view) {
ViewPropertyAnimator animator = ANIMATORS.get(view);
if (animator == null) {
final int version = Build.VERSION.SDK_INT;
if (version >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
animator = new ViewPropertyAnimatorICS(view);
} else if (version >= Build.VERSION_CODES.HONEYCOMB) {
animator = new ViewPropertyAnimatorHC(view);
} else {
animator = new ViewPropertyAnimatorPreHC(view);
}
ANIMATORS.put(view, animator);
}
return animator;
}
/**
* Returns the current duration of property animations. If the duration was set on this
* object, that value is returned. Otherwise, the default value of the underlying Animator
* is returned.
*
* @return The duration of animations, in milliseconds.
* @see #setDuration(long)
*/
public abstract long getDuration();
/**
* Sets the duration for the underlying animator that animates the requested properties.
* By default, the animator uses the default value for ValueAnimator. Calling this method
* will cause the declared value to be used instead.
*
* @param duration The length of ensuing property animations, in milliseconds. The value
* cannot be negative.
* @return This object, allowing calls to methods in this class to be chained.
*/
public abstract ViewPropertyAnimator setDuration(long duration);
/**
* Returns the current startDelay of property animations. If the startDelay was set on this
* object, that value is returned. Otherwise, the default value of the underlying Animator
* is returned.
*
* @return The startDelay of animations, in milliseconds.
* @see #setStartDelay(long)
*/
public abstract long getStartDelay();
/**
* Sets the startDelay for the underlying animator that animates the requested properties.
* By default, the animator uses the default value for ValueAnimator. Calling this method
* will cause the declared value to be used instead.
*
* @param startDelay The delay of ensuing property animations, in milliseconds. The value
* cannot be negative.
* @return This object, allowing calls to methods in this class to be chained.
*/
public abstract ViewPropertyAnimator setStartDelay(long startDelay);
/**
* Sets the interpolator for the underlying animator that animates the requested properties.
* By default, the animator uses the default interpolator for ValueAnimator. Calling this method
* will cause the declared object to be used instead.
*
* @param interpolator The TimeInterpolator to be used for ensuing property animations.
* @return This object, allowing calls to methods in this class to be chained.
*/
public abstract ViewPropertyAnimator setInterpolator(/*Time*/Interpolator interpolator);
/**
* Sets a listener for events in the underlying Animators that run the property
* animations.
*
* @param listener The listener to be called with AnimatorListener events.
* @return This object, allowing calls to methods in this class to be chained.
*/
public abstract ViewPropertyAnimator setListener(Animator.AnimatorListener listener);
/**
* Starts the currently pending property animations immediately. Calling <code>start()</code>
* is optional because all animations start automatically at the next opportunity. However,
* if the animations are needed to start immediately and synchronously (not at the time when
* the next event is processed by the hierarchy, which is when the animations would begin
* otherwise), then this method can be used.
*/
public abstract void start();
/**
* Cancels all property animations that are currently running or pending.
*/
public abstract void cancel();
/**
* This method will cause the View's <code>x</code> property to be animated to the
* specified value. Animations already running on the property will be canceled.
*
* @param value The value to be animated to.
* @return This object, allowing calls to methods in this class to be chained.
* @see View#setX(float)
*/
public abstract ViewPropertyAnimator x(float value);
/**
* This method will cause the View's <code>x</code> property to be animated by the
* specified value. Animations already running on the property will be canceled.
*
* @param value The amount to be animated by, as an offset from the current value.
* @return This object, allowing calls to methods in this class to be chained.
* @see View#setX(float)
*/
public abstract ViewPropertyAnimator xBy(float value);
/**
* This method will cause the View's <code>y</code> property to be animated to the
* specified value. Animations already running on the property will be canceled.
*
* @param value The value to be animated to.
* @return This object, allowing calls to methods in this class to be chained.
* @see View#setY(float)
*/
public abstract ViewPropertyAnimator y(float value);
/**
* This method will cause the View's <code>y</code> property to be animated by the
* specified value. Animations already running on the property will be canceled.
*
* @param value The amount to be animated by, as an offset from the current value.
* @return This object, allowing calls to methods in this class to be chained.
* @see View#setY(float)
*/
public abstract ViewPropertyAnimator yBy(float value);
/**
* This method will cause the View's <code>rotation</code> property to be animated to the
* specified value. Animations already running on the property will be canceled.
*
* @param value The value to be animated to.
* @return This object, allowing calls to methods in this class to be chained.
* @see View#setRotation(float)
*/
public abstract ViewPropertyAnimator rotation(float value);
/**
* This method will cause the View's <code>rotation</code> property to be animated by the
* specified value. Animations already running on the property will be canceled.
*
* @param value The amount to be animated by, as an offset from the current value.
* @return This object, allowing calls to methods in this class to be chained.
* @see View#setRotation(float)
*/
public abstract ViewPropertyAnimator rotationBy(float value);
/**
* This method will cause the View's <code>rotationX</code> property to be animated to the
* specified value. Animations already running on the property will be canceled.
*
* @param value The value to be animated to.
* @return This object, allowing calls to methods in this class to be chained.
* @see View#setRotationX(float)
*/
public abstract ViewPropertyAnimator rotationX(float value);
/**
* This method will cause the View's <code>rotationX</code> property to be animated by the
* specified value. Animations already running on the property will be canceled.
*
* @param value The amount to be animated by, as an offset from the current value.
* @return This object, allowing calls to methods in this class to be chained.
* @see View#setRotationX(float)
*/
public abstract ViewPropertyAnimator rotationXBy(float value);
/**
* This method will cause the View's <code>rotationY</code> property to be animated to the
* specified value. Animations already running on the property will be canceled.
*
* @param value The value to be animated to.
* @return This object, allowing calls to methods in this class to be chained.
* @see View#setRotationY(float)
*/
public abstract ViewPropertyAnimator rotationY(float value);
/**
* This method will cause the View's <code>rotationY</code> property to be animated by the
* specified value. Animations already running on the property will be canceled.
*
* @param value The amount to be animated by, as an offset from the current value.
* @return This object, allowing calls to methods in this class to be chained.
* @see View#setRotationY(float)
*/
public abstract ViewPropertyAnimator rotationYBy(float value);
/**
* This method will cause the View's <code>translationX</code> property to be animated to the
* specified value. Animations already running on the property will be canceled.
*
* @param value The value to be animated to.
* @return This object, allowing calls to methods in this class to be chained.
* @see View#setTranslationX(float)
*/
public abstract ViewPropertyAnimator translationX(float value);
/**
* This method will cause the View's <code>translationX</code> property to be animated by the
* specified value. Animations already running on the property will be canceled.
*
* @param value The amount to be animated by, as an offset from the current value.
* @return This object, allowing calls to methods in this class to be chained.
* @see View#setTranslationX(float)
*/
public abstract ViewPropertyAnimator translationXBy(float value);
/**
* This method will cause the View's <code>translationY</code> property to be animated to the
* specified value. Animations already running on the property will be canceled.
*
* @param value The value to be animated to.
* @return This object, allowing calls to methods in this class to be chained.
* @see View#setTranslationY(float)
*/
public abstract ViewPropertyAnimator translationY(float value);
/**
* This method will cause the View's <code>translationY</code> property to be animated by the
* specified value. Animations already running on the property will be canceled.
*
* @param value The amount to be animated by, as an offset from the current value.
* @return This object, allowing calls to methods in this class to be chained.
* @see View#setTranslationY(float)
*/
public abstract ViewPropertyAnimator translationYBy(float value);
/**
* This method will cause the View's <code>scaleX</code> property to be animated to the
* specified value. Animations already running on the property will be canceled.
*
* @param value The value to be animated to.
* @return This object, allowing calls to methods in this class to be chained.
* @see View#setScaleX(float)
*/
public abstract ViewPropertyAnimator scaleX(float value);
/**
* This method will cause the View's <code>scaleX</code> property to be animated by the
* specified value. Animations already running on the property will be canceled.
*
* @param value The amount to be animated by, as an offset from the current value.
* @return This object, allowing calls to methods in this class to be chained.
* @see View#setScaleX(float)
*/
public abstract ViewPropertyAnimator scaleXBy(float value);
/**
* This method will cause the View's <code>scaleY</code> property to be animated to the
* specified value. Animations already running on the property will be canceled.
*
* @param value The value to be animated to.
* @return This object, allowing calls to methods in this class to be chained.
* @see View#setScaleY(float)
*/
public abstract ViewPropertyAnimator scaleY(float value);
/**
* This method will cause the View's <code>scaleY</code> property to be animated by the
* specified value. Animations already running on the property will be canceled.
*
* @param value The amount to be animated by, as an offset from the current value.
* @return This object, allowing calls to methods in this class to be chained.
* @see View#setScaleY(float)
*/
public abstract ViewPropertyAnimator scaleYBy(float value);
/**
* This method will cause the View's <code>alpha</code> property to be animated to the
* specified value. Animations already running on the property will be canceled.
*
* @param value The value to be animated to.
* @return This object, allowing calls to methods in this class to be chained.
* @see View#setAlpha(float)
*/
public abstract ViewPropertyAnimator alpha(float value);
/**
* This method will cause the View's <code>alpha</code> property to be animated by the
* specified value. Animations already running on the property will be canceled.
*
* @param value The amount to be animated by, as an offset from the current value.
* @return This object, allowing calls to methods in this class to be chained.
* @see View#setAlpha(float)
*/
public abstract ViewPropertyAnimator alphaBy(float value);
}

View file

@ -0,0 +1,714 @@
/*
* Copyright (C) 2011 The Android 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.
*/
package com.adins.libs.nineoldandroids.view;
import android.view.View;
import android.view.animation.Interpolator;
import com.adins.libs.nineoldandroids.animation.Animator;
import com.adins.libs.nineoldandroids.animation.ValueAnimator;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;
class ViewPropertyAnimatorHC extends ViewPropertyAnimator {
/**
* Constants used to associate a property being requested and the mechanism used to set
* the property (this class calls directly into View to set the properties in question).
*/
private static final int NONE = 0x0000;
private static final int TRANSLATION_X = 0x0001;
private static final int TRANSLATION_Y = 0x0002;
private static final int SCALE_X = 0x0004;
private static final int SCALE_Y = 0x0008;
private static final int ROTATION = 0x0010;
private static final int ROTATION_X = 0x0020;
private static final int ROTATION_Y = 0x0040;
private static final int X = 0x0080;
private static final int Y = 0x0100;
private static final int ALPHA = 0x0200;
private static final int TRANSFORM_MASK = TRANSLATION_X | TRANSLATION_Y | SCALE_X | SCALE_Y |
ROTATION | ROTATION_X | ROTATION_Y | X | Y;
/**
* A WeakReference holding the View whose properties are being animated by this class.
* This is set at construction time.
*/
private final WeakReference<View> mView;
/**
* This list holds the properties that have been asked to animate. We allow the caller to
* request several animations prior to actually starting the underlying animator. This
* enables us to run one single animator to handle several properties in parallel. Each
* property is tossed onto the pending list until the animation actually starts (which is
* done by posting it onto mView), at which time the pending list is cleared and the properties
* on that list are added to the list of properties associated with that animator.
*/
ArrayList<NameValuesHolder> mPendingAnimations = new ArrayList<NameValuesHolder>();
/**
* The duration of the underlying Animator object. By default, we don't set the duration
* on the Animator and just use its default duration. If the duration is ever set on this
* Animator, then we use the duration that it was set to.
*/
private long mDuration;
/**
* A flag indicating whether the duration has been set on this object. If not, we don't set
* the duration on the underlying Animator, but instead just use its default duration.
*/
private boolean mDurationSet = false;
/**
* The startDelay of the underlying Animator object. By default, we don't set the startDelay
* on the Animator and just use its default startDelay. If the startDelay is ever set on this
* Animator, then we use the startDelay that it was set to.
*/
private long mStartDelay = 0;
/**
* A flag indicating whether the startDelay has been set on this object. If not, we don't set
* the startDelay on the underlying Animator, but instead just use its default startDelay.
*/
private boolean mStartDelaySet = false;
/**
* The interpolator of the underlying Animator object. By default, we don't set the interpolator
* on the Animator and just use its default interpolator. If the interpolator is ever set on
* this Animator, then we use the interpolator that it was set to.
*/
private /*Time*/ Interpolator mInterpolator;
/**
* A flag indicating whether the interpolator has been set on this object. If not, we don't set
* the interpolator on the underlying Animator, but instead just use its default interpolator.
*/
private boolean mInterpolatorSet = false;
/**
* Listener for the lifecycle events of the underlying
*/
private Animator.AnimatorListener mListener = null;
/**
* This listener is the mechanism by which the underlying Animator causes changes to the
* properties currently being animated, as well as the cleanup after an animation is
* complete.
*/
private AnimatorEventListener mAnimatorEventListener = new AnimatorEventListener();
/**
* This list tracks the list of properties being animated by any particular animator.
* In most situations, there would only ever be one animator running at a time. But it is
* possible to request some properties to animate together, then while those properties
* are animating, to request some other properties to animate together. The way that
* works is by having this map associate the group of properties being animated with the
* animator handling the animation. On every update event for an Animator, we ask the
* map for the associated properties and set them accordingly.
*/
private HashMap<Animator, PropertyBundle> mAnimatorMap =
new HashMap<Animator, PropertyBundle>();
/**
* The mechanism by which the user can request several properties that are then animated
* together works by posting this Runnable to start the underlying Animator. Every time
* a property animation is requested, we cancel any previous postings of the Runnable
* and re-post it. This means that we will only ever run the Runnable (and thus start the
* underlying animator) after the caller is done setting the properties that should be
* animated together.
*/
private Runnable mAnimationStarter = new Runnable() {
@Override
public void run() {
startAnimation();
}
};
/**
* Constructor, called by View. This is private by design, as the user should only
* get a ViewPropertyAnimator by calling View.animate().
*
* @param view The View associated with this ViewPropertyAnimator
*/
ViewPropertyAnimatorHC(View view) {
mView = new WeakReference<View>(view);
}
/**
* Sets the duration for the underlying animator that animates the requested properties.
* By default, the animator uses the default value for ValueAnimator. Calling this method
* will cause the declared value to be used instead.
*
* @param duration The length of ensuing property animations, in milliseconds. The value
* cannot be negative.
* @return This object, allowing calls to methods in this class to be chained.
*/
public ViewPropertyAnimator setDuration(long duration) {
if (duration < 0) {
throw new IllegalArgumentException("Animators cannot have negative duration: " +
duration);
}
mDurationSet = true;
mDuration = duration;
return this;
}
/**
* Returns the current duration of property animations. If the duration was set on this
* object, that value is returned. Otherwise, the default value of the underlying Animator
* is returned.
*
* @return The duration of animations, in milliseconds.
* @see #setDuration(long)
*/
public long getDuration() {
if (mDurationSet) {
return mDuration;
} else {
// Just return the default from ValueAnimator, since that's what we'd get if
// the value has not been set otherwise
return new ValueAnimator().getDuration();
}
}
@Override
public long getStartDelay() {
if (mStartDelaySet) {
return mStartDelay;
} else {
// Just return the default from ValueAnimator (0), since that's what we'd get if
// the value has not been set otherwise
return 0;
}
}
@Override
public ViewPropertyAnimator setStartDelay(long startDelay) {
if (startDelay < 0) {
throw new IllegalArgumentException("Animators cannot have negative duration: " +
startDelay);
}
mStartDelaySet = true;
mStartDelay = startDelay;
return this;
}
@Override
public ViewPropertyAnimator setInterpolator(/*Time*/Interpolator interpolator) {
mInterpolatorSet = true;
mInterpolator = interpolator;
return this;
}
@Override
public ViewPropertyAnimator setListener(Animator.AnimatorListener listener) {
mListener = listener;
return this;
}
@Override
public void start() {
startAnimation();
}
@Override
public void cancel() {
if (mAnimatorMap.size() > 0) {
HashMap<Animator, PropertyBundle> mAnimatorMapCopy =
(HashMap<Animator, PropertyBundle>) mAnimatorMap.clone();
Set<Animator> animatorSet = mAnimatorMapCopy.keySet();
for (Animator runningAnim : animatorSet) {
runningAnim.cancel();
}
}
mPendingAnimations.clear();
View v = mView.get();
if (v != null) {
v.removeCallbacks(mAnimationStarter);
}
}
@Override
public ViewPropertyAnimator x(float value) {
animateProperty(X, value);
return this;
}
@Override
public ViewPropertyAnimator xBy(float value) {
animatePropertyBy(X, value);
return this;
}
@Override
public ViewPropertyAnimator y(float value) {
animateProperty(Y, value);
return this;
}
@Override
public ViewPropertyAnimator yBy(float value) {
animatePropertyBy(Y, value);
return this;
}
@Override
public ViewPropertyAnimator rotation(float value) {
animateProperty(ROTATION, value);
return this;
}
@Override
public ViewPropertyAnimator rotationBy(float value) {
animatePropertyBy(ROTATION, value);
return this;
}
@Override
public ViewPropertyAnimator rotationX(float value) {
animateProperty(ROTATION_X, value);
return this;
}
@Override
public ViewPropertyAnimator rotationXBy(float value) {
animatePropertyBy(ROTATION_X, value);
return this;
}
@Override
public ViewPropertyAnimator rotationY(float value) {
animateProperty(ROTATION_Y, value);
return this;
}
@Override
public ViewPropertyAnimator rotationYBy(float value) {
animatePropertyBy(ROTATION_Y, value);
return this;
}
@Override
public ViewPropertyAnimator translationX(float value) {
animateProperty(TRANSLATION_X, value);
return this;
}
@Override
public ViewPropertyAnimator translationXBy(float value) {
animatePropertyBy(TRANSLATION_X, value);
return this;
}
@Override
public ViewPropertyAnimator translationY(float value) {
animateProperty(TRANSLATION_Y, value);
return this;
}
@Override
public ViewPropertyAnimator translationYBy(float value) {
animatePropertyBy(TRANSLATION_Y, value);
return this;
}
@Override
public ViewPropertyAnimator scaleX(float value) {
animateProperty(SCALE_X, value);
return this;
}
@Override
public ViewPropertyAnimator scaleXBy(float value) {
animatePropertyBy(SCALE_X, value);
return this;
}
@Override
public ViewPropertyAnimator scaleY(float value) {
animateProperty(SCALE_Y, value);
return this;
}
@Override
public ViewPropertyAnimator scaleYBy(float value) {
animatePropertyBy(SCALE_Y, value);
return this;
}
@Override
public ViewPropertyAnimator alpha(float value) {
animateProperty(ALPHA, value);
return this;
}
@Override
public ViewPropertyAnimator alphaBy(float value) {
animatePropertyBy(ALPHA, value);
return this;
}
/**
* Starts the underlying Animator for a set of properties. We use a single animator that
* simply runs from 0 to 1, and then use that fractional value to set each property
* value accordingly.
*/
private void startAnimation() {
ValueAnimator animator = ValueAnimator.ofFloat(1.0f);
ArrayList<NameValuesHolder> nameValueList =
(ArrayList<NameValuesHolder>) mPendingAnimations.clone();
mPendingAnimations.clear();
int propertyMask = 0;
int propertyCount = nameValueList.size();
for (int i = 0; i < propertyCount; ++i) {
NameValuesHolder nameValuesHolder = nameValueList.get(i);
propertyMask |= nameValuesHolder.mNameConstant;
}
mAnimatorMap.put(animator, new PropertyBundle(propertyMask, nameValueList));
animator.addUpdateListener(mAnimatorEventListener);
animator.addListener(mAnimatorEventListener);
if (mStartDelaySet) {
animator.setStartDelay(mStartDelay);
}
if (mDurationSet) {
animator.setDuration(mDuration);
}
if (mInterpolatorSet) {
animator.setInterpolator(mInterpolator);
}
animator.start();
}
/**
* Utility function, called by the various x(), y(), etc. methods. This stores the
* constant name for the property along with the from/delta values that will be used to
* calculate and set the property during the animation. This structure is added to the
* pending animations, awaiting the eventual start() of the underlying animator. A
* Runnable is posted to start the animation, and any pending such Runnable is canceled
* (which enables us to end up starting just one animator for all of the properties
* specified at one time).
*
* @param constantName The specifier for the property being animated
* @param toValue The value to which the property will animate
*/
private void animateProperty(int constantName, float toValue) {
float fromValue = getValue(constantName);
float deltaValue = toValue - fromValue;
animatePropertyBy(constantName, fromValue, deltaValue);
}
/**
* Utility function, called by the various xBy(), yBy(), etc. methods. This method is
* just like animateProperty(), except the value is an offset from the property's
* current value, instead of an absolute "to" value.
*
* @param constantName The specifier for the property being animated
* @param byValue The amount by which the property will change
*/
private void animatePropertyBy(int constantName, float byValue) {
float fromValue = getValue(constantName);
animatePropertyBy(constantName, fromValue, byValue);
}
/**
* Utility function, called by animateProperty() and animatePropertyBy(), which handles the
* details of adding a pending animation and posting the request to start the animation.
*
* @param constantName The specifier for the property being animated
* @param startValue The starting value of the property
* @param byValue The amount by which the property will change
*/
private void animatePropertyBy(int constantName, float startValue, float byValue) {
// First, cancel any existing animations on this property
if (mAnimatorMap.size() > 0) {
Animator animatorToCancel = null;
Set<Animator> animatorSet = mAnimatorMap.keySet();
for (Animator runningAnim : animatorSet) {
PropertyBundle bundle = mAnimatorMap.get(runningAnim);
if (bundle.cancel(constantName)) {
// property was canceled - cancel the animation if it's now empty
// Note that it's safe to break out here because every new animation
// on a property will cancel a previous animation on that property, so
// there can only ever be one such animation running.
if (bundle.mPropertyMask == NONE) {
// the animation is no longer changing anything - cancel it
animatorToCancel = runningAnim;
break;
}
}
}
if (animatorToCancel != null) {
animatorToCancel.cancel();
}
}
NameValuesHolder nameValuePair = new NameValuesHolder(constantName, startValue, byValue);
mPendingAnimations.add(nameValuePair);
View v = mView.get();
if (v != null) {
v.removeCallbacks(mAnimationStarter);
v.post(mAnimationStarter);
}
}
/**
* This method handles setting the property values directly in the View object's fields.
* propertyConstant tells it which property should be set, value is the value to set
* the property to.
*
* @param propertyConstant The property to be set
* @param value The value to set the property to
*/
private void setValue(int propertyConstant, float value) {
//final View.TransformationInfo info = mView.mTransformationInfo;
View v = mView.get();
if (v != null) {
switch (propertyConstant) {
case TRANSLATION_X:
//info.mTranslationX = value;
v.setTranslationX(value);
break;
case TRANSLATION_Y:
//info.mTranslationY = value;
v.setTranslationY(value);
break;
case ROTATION:
//info.mRotation = value;
v.setRotation(value);
break;
case ROTATION_X:
//info.mRotationX = value;
v.setRotationX(value);
break;
case ROTATION_Y:
//info.mRotationY = value;
v.setRotationY(value);
break;
case SCALE_X:
//info.mScaleX = value;
v.setScaleX(value);
break;
case SCALE_Y:
//info.mScaleY = value;
v.setScaleY(value);
break;
case X:
//info.mTranslationX = value - v.mLeft;
v.setX(value);
break;
case Y:
//info.mTranslationY = value - v.mTop;
v.setY(value);
break;
case ALPHA:
//info.mAlpha = value;
v.setAlpha(value);
break;
}
}
}
/**
* This method gets the value of the named property from the View object.
*
* @param propertyConstant The property whose value should be returned
* @return float The value of the named property
*/
private float getValue(int propertyConstant) {
//final View.TransformationInfo info = mView.mTransformationInfo;
View v = mView.get();
if (v != null) {
switch (propertyConstant) {
case TRANSLATION_X:
//return info.mTranslationX;
return v.getTranslationX();
case TRANSLATION_Y:
//return info.mTranslationY;
return v.getTranslationY();
case ROTATION:
//return info.mRotation;
return v.getRotation();
case ROTATION_X:
//return info.mRotationX;
return v.getRotationX();
case ROTATION_Y:
//return info.mRotationY;
return v.getRotationY();
case SCALE_X:
//return info.mScaleX;
return v.getScaleX();
case SCALE_Y:
//return info.mScaleY;
return v.getScaleY();
case X:
//return v.mLeft + info.mTranslationX;
return v.getX();
case Y:
//return v.mTop + info.mTranslationY;
return v.getY();
case ALPHA:
//return info.mAlpha;
return v.getAlpha();
}
}
return 0;
}
/**
* This class holds information about the overall animation being run on the set of
* properties. The mask describes which properties are being animated and the
* values holder is the list of all property/value objects.
*/
private static class PropertyBundle {
int mPropertyMask;
ArrayList<NameValuesHolder> mNameValuesHolder;
PropertyBundle(int propertyMask, ArrayList<NameValuesHolder> nameValuesHolder) {
mPropertyMask = propertyMask;
mNameValuesHolder = nameValuesHolder;
}
/**
* Removes the given property from being animated as a part of this
* PropertyBundle. If the property was a part of this bundle, it returns
* true to indicate that it was, in fact, canceled. This is an indication
* to the caller that a cancellation actually occurred.
*
* @param propertyConstant The property whose cancellation is requested.
* @return true if the given property is a part of this bundle and if it
* has therefore been canceled.
*/
boolean cancel(int propertyConstant) {
if ((mPropertyMask & propertyConstant) != 0 && mNameValuesHolder != null) {
int count = mNameValuesHolder.size();
for (int i = 0; i < count; ++i) {
NameValuesHolder nameValuesHolder = mNameValuesHolder.get(i);
if (nameValuesHolder.mNameConstant == propertyConstant) {
mNameValuesHolder.remove(i);
mPropertyMask &= ~propertyConstant;
return true;
}
}
}
return false;
}
}
/**
* This is the information we need to set each property during the animation.
* mNameConstant is used to set the appropriate field in View, and the from/delta
* values are used to calculate the animated value for a given animation fraction
* during the animation.
*/
private static class NameValuesHolder {
int mNameConstant;
float mFromValue;
float mDeltaValue;
NameValuesHolder(int nameConstant, float fromValue, float deltaValue) {
mNameConstant = nameConstant;
mFromValue = fromValue;
mDeltaValue = deltaValue;
}
}
/**
* Utility class that handles the various Animator events. The only ones we care
* about are the end event (which we use to clean up the animator map when an animator
* finishes) and the update event (which we use to calculate the current value of each
* property and then set it on the view object).
*/
private class AnimatorEventListener
implements Animator.AnimatorListener, ValueAnimator.AnimatorUpdateListener {
@Override
public void onAnimationStart(Animator animation) {
if (mListener != null) {
mListener.onAnimationStart(animation);
}
}
@Override
public void onAnimationCancel(Animator animation) {
if (mListener != null) {
mListener.onAnimationCancel(animation);
}
}
@Override
public void onAnimationRepeat(Animator animation) {
if (mListener != null) {
mListener.onAnimationRepeat(animation);
}
}
@Override
public void onAnimationEnd(Animator animation) {
if (mListener != null) {
mListener.onAnimationEnd(animation);
}
mAnimatorMap.remove(animation);
// If the map is empty, it means all animation are done or canceled, so the listener
// isn't needed anymore. Not nulling it would cause it to leak any objects used in
// its implementation
if (mAnimatorMap.isEmpty()) {
mListener = null;
}
}
/**
* Calculate the current value for each property and set it on the view. Invalidate
* the view object appropriately, depending on which properties are being animated.
*
* @param animation The animator associated with the properties that need to be
* set. This animator holds the animation fraction which we will use to calculate
* the current value of each property.
*/
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// alpha requires slightly different treatment than the other (transform) properties.
// The logic in setAlpha() is not simply setting mAlpha, plus the invalidation
// logic is dependent on how the view handles an internal call to onSetAlpha().
// We track what kinds of properties are set, and how alpha is handled when it is
// set, and perform the invalidation steps appropriately.
//boolean alphaHandled = false;
//mView.invalidateParentCaches();
float fraction = animation.getAnimatedFraction();
PropertyBundle propertyBundle = mAnimatorMap.get(animation);
int propertyMask = propertyBundle.mPropertyMask;
if ((propertyMask & TRANSFORM_MASK) != 0) {
View v = mView.get();
if (v != null) {
v.invalidate(/*false*/);
}
}
ArrayList<NameValuesHolder> valueList = propertyBundle.mNameValuesHolder;
if (valueList != null) {
int count = valueList.size();
for (int i = 0; i < count; ++i) {
NameValuesHolder values = valueList.get(i);
float value = values.mFromValue + fraction * values.mDeltaValue;
//if (values.mNameConstant == ALPHA) {
// alphaHandled = mView.setAlphaNoInvalidation(value);
//} else {
setValue(values.mNameConstant, value);
//}
}
}
/*if ((propertyMask & TRANSFORM_MASK) != 0) {
mView.mTransformationInfo.mMatrixDirty = true;
mView.mPrivateFlags |= View.DRAWN; // force another invalidation
}*/
// invalidate(false) in all cases except if alphaHandled gets set to true
// via the call to setAlphaNoInvalidation(), above
View v = mView.get();
if (v != null) {
v.invalidate(/*alphaHandled*/);
}
}
}
}

View file

@ -0,0 +1,299 @@
package com.adins.libs.nineoldandroids.view;
import android.view.View;
import android.view.animation.Interpolator;
import com.adins.libs.nineoldandroids.animation.Animator.AnimatorListener;
import java.lang.ref.WeakReference;
class ViewPropertyAnimatorICS extends ViewPropertyAnimator {
/**
* A value to be returned when the WeakReference holding the native implementation
* returns <code>null</code>
*/
private final static long RETURN_WHEN_NULL = -1L;
/**
* A WeakReference holding the native implementation of ViewPropertyAnimator
*/
private final WeakReference<android.view.ViewPropertyAnimator> mNative;
ViewPropertyAnimatorICS(View view) {
mNative = new WeakReference<android.view.ViewPropertyAnimator>(view.animate());
}
@Override
public ViewPropertyAnimator setDuration(long duration) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.setDuration(duration);
}
return this;
}
@Override
public long getDuration() {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
return n.getDuration();
}
return RETURN_WHEN_NULL;
}
@Override
public ViewPropertyAnimator setStartDelay(long startDelay) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.setStartDelay(startDelay);
}
return this;
}
@Override
public long getStartDelay() {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
return n.getStartDelay();
}
return RETURN_WHEN_NULL;
}
@Override
public ViewPropertyAnimator setInterpolator(Interpolator interpolator) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.setInterpolator(interpolator);
}
return this;
}
@Override
public ViewPropertyAnimator setListener(final AnimatorListener listener) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
if (listener == null) {
n.setListener(null);
} else {
n.setListener(new android.animation.Animator.AnimatorListener() {
@Override
public void onAnimationStart(android.animation.Animator animation) {
listener.onAnimationStart(null);
}
@Override
public void onAnimationRepeat(android.animation.Animator animation) {
listener.onAnimationRepeat(null);
}
@Override
public void onAnimationEnd(android.animation.Animator animation) {
listener.onAnimationEnd(null);
}
@Override
public void onAnimationCancel(android.animation.Animator animation) {
listener.onAnimationCancel(null);
}
});
}
}
return this;
}
@Override
public void start() {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.start();
}
}
@Override
public void cancel() {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.cancel();
}
}
@Override
public ViewPropertyAnimator x(float value) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.x(value);
}
return this;
}
@Override
public ViewPropertyAnimator xBy(float value) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.xBy(value);
}
return this;
}
@Override
public ViewPropertyAnimator y(float value) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.y(value);
}
return this;
}
@Override
public ViewPropertyAnimator yBy(float value) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.yBy(value);
}
return this;
}
@Override
public ViewPropertyAnimator rotation(float value) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.rotation(value);
}
return this;
}
@Override
public ViewPropertyAnimator rotationBy(float value) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.rotationBy(value);
}
return this;
}
@Override
public ViewPropertyAnimator rotationX(float value) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.rotationX(value);
}
return this;
}
@Override
public ViewPropertyAnimator rotationXBy(float value) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.rotationXBy(value);
}
return this;
}
@Override
public ViewPropertyAnimator rotationY(float value) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.rotationY(value);
}
return this;
}
@Override
public ViewPropertyAnimator rotationYBy(float value) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.rotationYBy(value);
}
return this;
}
@Override
public ViewPropertyAnimator translationX(float value) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.translationX(value);
}
return this;
}
@Override
public ViewPropertyAnimator translationXBy(float value) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.translationXBy(value);
}
return this;
}
@Override
public ViewPropertyAnimator translationY(float value) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.translationY(value);
}
return this;
}
@Override
public ViewPropertyAnimator translationYBy(float value) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.translationYBy(value);
}
return this;
}
@Override
public ViewPropertyAnimator scaleX(float value) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.scaleX(value);
}
return this;
}
@Override
public ViewPropertyAnimator scaleXBy(float value) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.scaleXBy(value);
}
return this;
}
@Override
public ViewPropertyAnimator scaleY(float value) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.scaleY(value);
}
return this;
}
@Override
public ViewPropertyAnimator scaleYBy(float value) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.scaleYBy(value);
}
return this;
}
@Override
public ViewPropertyAnimator alpha(float value) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.alpha(value);
}
return this;
}
@Override
public ViewPropertyAnimator alphaBy(float value) {
android.view.ViewPropertyAnimator n = mNative.get();
if (n != null) {
n.alphaBy(value);
}
return this;
}
}

View file

@ -0,0 +1,714 @@
/*
* Copyright (C) 2011 The Android 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.
*/
package com.adins.libs.nineoldandroids.view;
import android.view.View;
import android.view.animation.Interpolator;
import com.adins.libs.nineoldandroids.animation.Animator;
import com.adins.libs.nineoldandroids.animation.ValueAnimator;
import com.adins.libs.nineoldandroids.view.animation.AnimatorProxy;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;
class ViewPropertyAnimatorPreHC extends ViewPropertyAnimator {
/**
* Constants used to associate a property being requested and the mechanism used to set
* the property (this class calls directly into View to set the properties in question).
*/
private static final int NONE = 0x0000;
private static final int TRANSLATION_X = 0x0001;
private static final int TRANSLATION_Y = 0x0002;
private static final int SCALE_X = 0x0004;
private static final int SCALE_Y = 0x0008;
private static final int ROTATION = 0x0010;
private static final int ROTATION_X = 0x0020;
private static final int ROTATION_Y = 0x0040;
private static final int X = 0x0080;
private static final int Y = 0x0100;
private static final int ALPHA = 0x0200;
private static final int TRANSFORM_MASK = TRANSLATION_X | TRANSLATION_Y | SCALE_X | SCALE_Y |
ROTATION | ROTATION_X | ROTATION_Y | X | Y;
/**
* Proxy animation class which will allow us access to post-Honeycomb properties that were not
* otherwise available.
*/
private final AnimatorProxy mProxy;
/**
* A WeakReference holding the View whose properties are being animated by this class. This is
* set at construction time.
*/
private final WeakReference<View> mView;
/**
* This list holds the properties that have been asked to animate. We allow the caller to
* request several animations prior to actually starting the underlying animator. This
* enables us to run one single animator to handle several properties in parallel. Each
* property is tossed onto the pending list until the animation actually starts (which is
* done by posting it onto mView), at which time the pending list is cleared and the properties
* on that list are added to the list of properties associated with that animator.
*/
ArrayList<NameValuesHolder> mPendingAnimations = new ArrayList<NameValuesHolder>();
/**
* The duration of the underlying Animator object. By default, we don't set the duration
* on the Animator and just use its default duration. If the duration is ever set on this
* Animator, then we use the duration that it was set to.
*/
private long mDuration;
/**
* A flag indicating whether the duration has been set on this object. If not, we don't set
* the duration on the underlying Animator, but instead just use its default duration.
*/
private boolean mDurationSet = false;
/**
* The startDelay of the underlying Animator object. By default, we don't set the startDelay
* on the Animator and just use its default startDelay. If the startDelay is ever set on this
* Animator, then we use the startDelay that it was set to.
*/
private long mStartDelay = 0;
/**
* A flag indicating whether the startDelay has been set on this object. If not, we don't set
* the startDelay on the underlying Animator, but instead just use its default startDelay.
*/
private boolean mStartDelaySet = false;
/**
* The interpolator of the underlying Animator object. By default, we don't set the interpolator
* on the Animator and just use its default interpolator. If the interpolator is ever set on
* this Animator, then we use the interpolator that it was set to.
*/
private /*Time*/ Interpolator mInterpolator;
/**
* A flag indicating whether the interpolator has been set on this object. If not, we don't set
* the interpolator on the underlying Animator, but instead just use its default interpolator.
*/
private boolean mInterpolatorSet = false;
/**
* Listener for the lifecycle events of the underlying
*/
private Animator.AnimatorListener mListener = null;
/**
* This listener is the mechanism by which the underlying Animator causes changes to the
* properties currently being animated, as well as the cleanup after an animation is
* complete.
*/
private AnimatorEventListener mAnimatorEventListener = new AnimatorEventListener();
/**
* This list tracks the list of properties being animated by any particular animator.
* In most situations, there would only ever be one animator running at a time. But it is
* possible to request some properties to animate together, then while those properties
* are animating, to request some other properties to animate together. The way that
* works is by having this map associate the group of properties being animated with the
* animator handling the animation. On every update event for an Animator, we ask the
* map for the associated properties and set them accordingly.
*/
private HashMap<Animator, PropertyBundle> mAnimatorMap =
new HashMap<Animator, PropertyBundle>();
/**
* The mechanism by which the user can request several properties that are then animated
* together works by posting this Runnable to start the underlying Animator. Every time
* a property animation is requested, we cancel any previous postings of the Runnable
* and re-post it. This means that we will only ever run the Runnable (and thus start the
* underlying animator) after the caller is done setting the properties that should be
* animated together.
*/
private Runnable mAnimationStarter = new Runnable() {
@Override
public void run() {
startAnimation();
}
};
/**
* Constructor, called by View. This is private by design, as the user should only
* get a ViewPropertyAnimator by calling View.animate().
*
* @param view The View associated with this ViewPropertyAnimator
*/
ViewPropertyAnimatorPreHC(View view) {
mView = new WeakReference<View>(view);
mProxy = AnimatorProxy.wrap(view);
}
/**
* Sets the duration for the underlying animator that animates the requested properties.
* By default, the animator uses the default value for ValueAnimator. Calling this method
* will cause the declared value to be used instead.
*
* @param duration The length of ensuing property animations, in milliseconds. The value
* cannot be negative.
* @return This object, allowing calls to methods in this class to be chained.
*/
public ViewPropertyAnimator setDuration(long duration) {
if (duration < 0) {
throw new IllegalArgumentException("Animators cannot have negative duration: " +
duration);
}
mDurationSet = true;
mDuration = duration;
return this;
}
/**
* Returns the current duration of property animations. If the duration was set on this
* object, that value is returned. Otherwise, the default value of the underlying Animator
* is returned.
*
* @return The duration of animations, in milliseconds.
* @see #setDuration(long)
*/
public long getDuration() {
if (mDurationSet) {
return mDuration;
} else {
// Just return the default from ValueAnimator, since that's what we'd get if
// the value has not been set otherwise
return new ValueAnimator().getDuration();
}
}
@Override
public long getStartDelay() {
if (mStartDelaySet) {
return mStartDelay;
} else {
// Just return the default from ValueAnimator (0), since that's what we'd get if
// the value has not been set otherwise
return 0;
}
}
@Override
public ViewPropertyAnimator setStartDelay(long startDelay) {
if (startDelay < 0) {
throw new IllegalArgumentException("Animators cannot have negative duration: " +
startDelay);
}
mStartDelaySet = true;
mStartDelay = startDelay;
return this;
}
@Override
public ViewPropertyAnimator setInterpolator(/*Time*/Interpolator interpolator) {
mInterpolatorSet = true;
mInterpolator = interpolator;
return this;
}
@Override
public ViewPropertyAnimator setListener(Animator.AnimatorListener listener) {
mListener = listener;
return this;
}
@Override
public void start() {
startAnimation();
}
@Override
public void cancel() {
if (mAnimatorMap.size() > 0) {
HashMap<Animator, PropertyBundle> mAnimatorMapCopy =
(HashMap<Animator, PropertyBundle>) mAnimatorMap.clone();
Set<Animator> animatorSet = mAnimatorMapCopy.keySet();
for (Animator runningAnim : animatorSet) {
runningAnim.cancel();
}
}
mPendingAnimations.clear();
View v = mView.get();
if (v != null) {
v.removeCallbacks(mAnimationStarter);
}
}
@Override
public ViewPropertyAnimator x(float value) {
animateProperty(X, value);
return this;
}
@Override
public ViewPropertyAnimator xBy(float value) {
animatePropertyBy(X, value);
return this;
}
@Override
public ViewPropertyAnimator y(float value) {
animateProperty(Y, value);
return this;
}
@Override
public ViewPropertyAnimator yBy(float value) {
animatePropertyBy(Y, value);
return this;
}
@Override
public ViewPropertyAnimator rotation(float value) {
animateProperty(ROTATION, value);
return this;
}
@Override
public ViewPropertyAnimator rotationBy(float value) {
animatePropertyBy(ROTATION, value);
return this;
}
@Override
public ViewPropertyAnimator rotationX(float value) {
animateProperty(ROTATION_X, value);
return this;
}
@Override
public ViewPropertyAnimator rotationXBy(float value) {
animatePropertyBy(ROTATION_X, value);
return this;
}
@Override
public ViewPropertyAnimator rotationY(float value) {
animateProperty(ROTATION_Y, value);
return this;
}
@Override
public ViewPropertyAnimator rotationYBy(float value) {
animatePropertyBy(ROTATION_Y, value);
return this;
}
@Override
public ViewPropertyAnimator translationX(float value) {
animateProperty(TRANSLATION_X, value);
return this;
}
@Override
public ViewPropertyAnimator translationXBy(float value) {
animatePropertyBy(TRANSLATION_X, value);
return this;
}
@Override
public ViewPropertyAnimator translationY(float value) {
animateProperty(TRANSLATION_Y, value);
return this;
}
@Override
public ViewPropertyAnimator translationYBy(float value) {
animatePropertyBy(TRANSLATION_Y, value);
return this;
}
@Override
public ViewPropertyAnimator scaleX(float value) {
animateProperty(SCALE_X, value);
return this;
}
@Override
public ViewPropertyAnimator scaleXBy(float value) {
animatePropertyBy(SCALE_X, value);
return this;
}
@Override
public ViewPropertyAnimator scaleY(float value) {
animateProperty(SCALE_Y, value);
return this;
}
@Override
public ViewPropertyAnimator scaleYBy(float value) {
animatePropertyBy(SCALE_Y, value);
return this;
}
@Override
public ViewPropertyAnimator alpha(float value) {
animateProperty(ALPHA, value);
return this;
}
@Override
public ViewPropertyAnimator alphaBy(float value) {
animatePropertyBy(ALPHA, value);
return this;
}
/**
* Starts the underlying Animator for a set of properties. We use a single animator that
* simply runs from 0 to 1, and then use that fractional value to set each property
* value accordingly.
*/
private void startAnimation() {
ValueAnimator animator = ValueAnimator.ofFloat(1.0f);
ArrayList<NameValuesHolder> nameValueList =
(ArrayList<NameValuesHolder>) mPendingAnimations.clone();
mPendingAnimations.clear();
int propertyMask = 0;
int propertyCount = nameValueList.size();
for (int i = 0; i < propertyCount; ++i) {
NameValuesHolder nameValuesHolder = nameValueList.get(i);
propertyMask |= nameValuesHolder.mNameConstant;
}
mAnimatorMap.put(animator, new PropertyBundle(propertyMask, nameValueList));
animator.addUpdateListener(mAnimatorEventListener);
animator.addListener(mAnimatorEventListener);
if (mStartDelaySet) {
animator.setStartDelay(mStartDelay);
}
if (mDurationSet) {
animator.setDuration(mDuration);
}
if (mInterpolatorSet) {
animator.setInterpolator(mInterpolator);
}
animator.start();
}
/**
* Utility function, called by the various x(), y(), etc. methods. This stores the
* constant name for the property along with the from/delta values that will be used to
* calculate and set the property during the animation. This structure is added to the
* pending animations, awaiting the eventual start() of the underlying animator. A
* Runnable is posted to start the animation, and any pending such Runnable is canceled
* (which enables us to end up starting just one animator for all of the properties
* specified at one time).
*
* @param constantName The specifier for the property being animated
* @param toValue The value to which the property will animate
*/
private void animateProperty(int constantName, float toValue) {
float fromValue = getValue(constantName);
float deltaValue = toValue - fromValue;
animatePropertyBy(constantName, fromValue, deltaValue);
}
/**
* Utility function, called by the various xBy(), yBy(), etc. methods. This method is
* just like animateProperty(), except the value is an offset from the property's
* current value, instead of an absolute "to" value.
*
* @param constantName The specifier for the property being animated
* @param byValue The amount by which the property will change
*/
private void animatePropertyBy(int constantName, float byValue) {
float fromValue = getValue(constantName);
animatePropertyBy(constantName, fromValue, byValue);
}
/**
* Utility function, called by animateProperty() and animatePropertyBy(), which handles the
* details of adding a pending animation and posting the request to start the animation.
*
* @param constantName The specifier for the property being animated
* @param startValue The starting value of the property
* @param byValue The amount by which the property will change
*/
private void animatePropertyBy(int constantName, float startValue, float byValue) {
// First, cancel any existing animations on this property
if (mAnimatorMap.size() > 0) {
Animator animatorToCancel = null;
Set<Animator> animatorSet = mAnimatorMap.keySet();
for (Animator runningAnim : animatorSet) {
PropertyBundle bundle = mAnimatorMap.get(runningAnim);
if (bundle.cancel(constantName)) {
// property was canceled - cancel the animation if it's now empty
// Note that it's safe to break out here because every new animation
// on a property will cancel a previous animation on that property, so
// there can only ever be one such animation running.
if (bundle.mPropertyMask == NONE) {
// the animation is no longer changing anything - cancel it
animatorToCancel = runningAnim;
break;
}
}
}
if (animatorToCancel != null) {
animatorToCancel.cancel();
}
}
NameValuesHolder nameValuePair = new NameValuesHolder(constantName, startValue, byValue);
mPendingAnimations.add(nameValuePair);
View v = mView.get();
if (v != null) {
v.removeCallbacks(mAnimationStarter);
v.post(mAnimationStarter);
}
}
/**
* This method handles setting the property values directly in the View object's fields.
* propertyConstant tells it which property should be set, value is the value to set
* the property to.
*
* @param propertyConstant The property to be set
* @param value The value to set the property to
*/
private void setValue(int propertyConstant, float value) {
//final View.TransformationInfo info = mView.mTransformationInfo;
switch (propertyConstant) {
case TRANSLATION_X:
//info.mTranslationX = value;
mProxy.setTranslationX(value);
break;
case TRANSLATION_Y:
//info.mTranslationY = value;
mProxy.setTranslationY(value);
break;
case ROTATION:
//info.mRotation = value;
mProxy.setRotation(value);
break;
case ROTATION_X:
//info.mRotationX = value;
mProxy.setRotationX(value);
break;
case ROTATION_Y:
//info.mRotationY = value;
mProxy.setRotationY(value);
break;
case SCALE_X:
//info.mScaleX = value;
mProxy.setScaleX(value);
break;
case SCALE_Y:
//info.mScaleY = value;
mProxy.setScaleY(value);
break;
case X:
//info.mTranslationX = value - mView.mLeft;
mProxy.setX(value);
break;
case Y:
//info.mTranslationY = value - mView.mTop;
mProxy.setY(value);
break;
case ALPHA:
//info.mAlpha = value;
mProxy.setAlpha(value);
break;
}
}
/**
* This method gets the value of the named property from the View object.
*
* @param propertyConstant The property whose value should be returned
* @return float The value of the named property
*/
private float getValue(int propertyConstant) {
//final View.TransformationInfo info = mView.mTransformationInfo;
switch (propertyConstant) {
case TRANSLATION_X:
//return info.mTranslationX;
return mProxy.getTranslationX();
case TRANSLATION_Y:
//return info.mTranslationY;
return mProxy.getTranslationY();
case ROTATION:
//return info.mRotation;
return mProxy.getRotation();
case ROTATION_X:
//return info.mRotationX;
return mProxy.getRotationX();
case ROTATION_Y:
//return info.mRotationY;
return mProxy.getRotationY();
case SCALE_X:
//return info.mScaleX;
return mProxy.getScaleX();
case SCALE_Y:
//return info.mScaleY;
return mProxy.getScaleY();
case X:
//return mView.mLeft + info.mTranslationX;
return mProxy.getX();
case Y:
//return mView.mTop + info.mTranslationY;
return mProxy.getY();
case ALPHA:
//return info.mAlpha;
return mProxy.getAlpha();
}
return 0;
}
/**
* This class holds information about the overall animation being run on the set of
* properties. The mask describes which properties are being animated and the
* values holder is the list of all property/value objects.
*/
private static class PropertyBundle {
int mPropertyMask;
ArrayList<NameValuesHolder> mNameValuesHolder;
PropertyBundle(int propertyMask, ArrayList<NameValuesHolder> nameValuesHolder) {
mPropertyMask = propertyMask;
mNameValuesHolder = nameValuesHolder;
}
/**
* Removes the given property from being animated as a part of this
* PropertyBundle. If the property was a part of this bundle, it returns
* true to indicate that it was, in fact, canceled. This is an indication
* to the caller that a cancellation actually occurred.
*
* @param propertyConstant The property whose cancellation is requested.
* @return true if the given property is a part of this bundle and if it
* has therefore been canceled.
*/
boolean cancel(int propertyConstant) {
if ((mPropertyMask & propertyConstant) != 0 && mNameValuesHolder != null) {
int count = mNameValuesHolder.size();
for (int i = 0; i < count; ++i) {
NameValuesHolder nameValuesHolder = mNameValuesHolder.get(i);
if (nameValuesHolder.mNameConstant == propertyConstant) {
mNameValuesHolder.remove(i);
mPropertyMask &= ~propertyConstant;
return true;
}
}
}
return false;
}
}
/**
* This is the information we need to set each property during the animation.
* mNameConstant is used to set the appropriate field in View, and the from/delta
* values are used to calculate the animated value for a given animation fraction
* during the animation.
*/
private static class NameValuesHolder {
int mNameConstant;
float mFromValue;
float mDeltaValue;
NameValuesHolder(int nameConstant, float fromValue, float deltaValue) {
mNameConstant = nameConstant;
mFromValue = fromValue;
mDeltaValue = deltaValue;
}
}
/**
* Utility class that handles the various Animator events. The only ones we care
* about are the end event (which we use to clean up the animator map when an animator
* finishes) and the update event (which we use to calculate the current value of each
* property and then set it on the view object).
*/
private class AnimatorEventListener
implements Animator.AnimatorListener, ValueAnimator.AnimatorUpdateListener {
@Override
public void onAnimationStart(Animator animation) {
if (mListener != null) {
mListener.onAnimationStart(animation);
}
}
@Override
public void onAnimationCancel(Animator animation) {
if (mListener != null) {
mListener.onAnimationCancel(animation);
}
}
@Override
public void onAnimationRepeat(Animator animation) {
if (mListener != null) {
mListener.onAnimationRepeat(animation);
}
}
@Override
public void onAnimationEnd(Animator animation) {
if (mListener != null) {
mListener.onAnimationEnd(animation);
}
mAnimatorMap.remove(animation);
// If the map is empty, it means all animation are done or canceled, so the listener
// isn't needed anymore. Not nulling it would cause it to leak any objects used in
// its implementation
if (mAnimatorMap.isEmpty()) {
mListener = null;
}
}
/**
* Calculate the current value for each property and set it on the view. Invalidate
* the view object appropriately, depending on which properties are being animated.
*
* @param animation The animator associated with the properties that need to be
* set. This animator holds the animation fraction which we will use to calculate
* the current value of each property.
*/
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// alpha requires slightly different treatment than the other (transform) properties.
// The logic in setAlpha() is not simply setting mAlpha, plus the invalidation
// logic is dependent on how the view handles an internal call to onSetAlpha().
// We track what kinds of properties are set, and how alpha is handled when it is
// set, and perform the invalidation steps appropriately.
//boolean alphaHandled = false;
//mView.invalidateParentCaches();
float fraction = animation.getAnimatedFraction();
PropertyBundle propertyBundle = mAnimatorMap.get(animation);
int propertyMask = propertyBundle.mPropertyMask;
if ((propertyMask & TRANSFORM_MASK) != 0) {
View v = mView.get();
if (v != null) {
v.invalidate(/*false*/);
}
}
ArrayList<NameValuesHolder> valueList = propertyBundle.mNameValuesHolder;
if (valueList != null) {
int count = valueList.size();
for (int i = 0; i < count; ++i) {
NameValuesHolder values = valueList.get(i);
float value = values.mFromValue + fraction * values.mDeltaValue;
//if (values.mNameConstant == ALPHA) {
// alphaHandled = mView.setAlphaNoInvalidation(value);
//} else {
setValue(values.mNameConstant, value);
//}
}
}
/*if ((propertyMask & TRANSFORM_MASK) != 0) {
mView.mTransformationInfo.mMatrixDirty = true;
mView.mPrivateFlags |= View.DRAWN; // force another invalidation
}*/
// invalidate(false) in all cases except if alphaHandled gets set to true
// via the call to setAlphaNoInvalidation(), above
View v = mView.get();
if (v != null) {
v.invalidate(/*alphaHandled*/);
}
}
}
}

View file

@ -0,0 +1,346 @@
package com.adins.libs.nineoldandroids.view.animation;
import android.graphics.Camera;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.os.Build;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.Transformation;
import java.lang.ref.WeakReference;
import java.util.WeakHashMap;
/**
* A proxy class to allow for modifying post-3.0 view properties on all pre-3.0
* platforms. <strong>DO NOT</strong> wrap your views with this class if you
* are using {@code ObjectAnimator} as it will handle that itself.
*/
public final class AnimatorProxy extends Animation {
/**
* Whether or not the current running platform needs to be proxied.
*/
public static final boolean NEEDS_PROXY = Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB;
private static final WeakHashMap<View, AnimatorProxy> PROXIES =
new WeakHashMap<View, AnimatorProxy>();
private final WeakReference<View> mView;
private final Camera mCamera = new Camera();
private final RectF mBefore = new RectF();
private final RectF mAfter = new RectF();
private final Matrix mTempMatrix = new Matrix();
private boolean mHasPivot;
private float mAlpha = 1;
private float mPivotX;
private float mPivotY;
private float mRotationX;
private float mRotationY;
private float mRotationZ;
private float mScaleX = 1;
private float mScaleY = 1;
private float mTranslationX;
private float mTranslationY;
private AnimatorProxy(View view) {
setDuration(0); //perform transformation immediately
setFillAfter(true); //persist transformation beyond duration
view.setAnimation(this);
mView = new WeakReference<View>(view);
}
/**
* Create a proxy to allow for modifying post-3.0 view properties on all
* pre-3.0 platforms. <strong>DO NOT</strong> wrap your views if you are
* using {@code ObjectAnimator} as it will handle that itself.
*
* @param view View to wrap.
* @return Proxy to post-3.0 properties.
*/
public static AnimatorProxy wrap(View view) {
AnimatorProxy proxy = PROXIES.get(view);
// This checks if the proxy already exists and whether it still is the animation of the given view
if (proxy == null || proxy != view.getAnimation()) {
proxy = new AnimatorProxy(view);
PROXIES.put(view, proxy);
}
return proxy;
}
public float getAlpha() {
return mAlpha;
}
public void setAlpha(float alpha) {
if (mAlpha != alpha) {
mAlpha = alpha;
View view = mView.get();
if (view != null) {
view.invalidate();
}
}
}
public float getPivotX() {
return mPivotX;
}
public void setPivotX(float pivotX) {
if (!mHasPivot || mPivotX != pivotX) {
prepareForUpdate();
mHasPivot = true;
mPivotX = pivotX;
invalidateAfterUpdate();
}
}
public float getPivotY() {
return mPivotY;
}
public void setPivotY(float pivotY) {
if (!mHasPivot || mPivotY != pivotY) {
prepareForUpdate();
mHasPivot = true;
mPivotY = pivotY;
invalidateAfterUpdate();
}
}
public float getRotation() {
return mRotationZ;
}
public void setRotation(float rotation) {
if (mRotationZ != rotation) {
prepareForUpdate();
mRotationZ = rotation;
invalidateAfterUpdate();
}
}
public float getRotationX() {
return mRotationX;
}
public void setRotationX(float rotationX) {
if (mRotationX != rotationX) {
prepareForUpdate();
mRotationX = rotationX;
invalidateAfterUpdate();
}
}
public float getRotationY() {
return mRotationY;
}
public void setRotationY(float rotationY) {
if (mRotationY != rotationY) {
prepareForUpdate();
mRotationY = rotationY;
invalidateAfterUpdate();
}
}
public float getScaleX() {
return mScaleX;
}
public void setScaleX(float scaleX) {
if (mScaleX != scaleX) {
prepareForUpdate();
mScaleX = scaleX;
invalidateAfterUpdate();
}
}
public float getScaleY() {
return mScaleY;
}
public void setScaleY(float scaleY) {
if (mScaleY != scaleY) {
prepareForUpdate();
mScaleY = scaleY;
invalidateAfterUpdate();
}
}
public int getScrollX() {
View view = mView.get();
if (view == null) {
return 0;
}
return view.getScrollX();
}
public void setScrollX(int value) {
View view = mView.get();
if (view != null) {
view.scrollTo(value, view.getScrollY());
}
}
public int getScrollY() {
View view = mView.get();
if (view == null) {
return 0;
}
return view.getScrollY();
}
public void setScrollY(int value) {
View view = mView.get();
if (view != null) {
view.scrollTo(view.getScrollX(), value);
}
}
public float getTranslationX() {
return mTranslationX;
}
public void setTranslationX(float translationX) {
if (mTranslationX != translationX) {
prepareForUpdate();
mTranslationX = translationX;
invalidateAfterUpdate();
}
}
public float getTranslationY() {
return mTranslationY;
}
public void setTranslationY(float translationY) {
if (mTranslationY != translationY) {
prepareForUpdate();
mTranslationY = translationY;
invalidateAfterUpdate();
}
}
public float getX() {
View view = mView.get();
if (view == null) {
return 0;
}
return view.getLeft() + mTranslationX;
}
public void setX(float x) {
View view = mView.get();
if (view != null) {
setTranslationX(x - view.getLeft());
}
}
public float getY() {
View view = mView.get();
if (view == null) {
return 0;
}
return view.getTop() + mTranslationY;
}
public void setY(float y) {
View view = mView.get();
if (view != null) {
setTranslationY(y - view.getTop());
}
}
private void prepareForUpdate() {
View view = mView.get();
if (view != null) {
computeRect(mBefore, view);
}
}
private void invalidateAfterUpdate() {
View view = mView.get();
if (view == null || view.getParent() == null) {
return;
}
final RectF after = mAfter;
computeRect(after, view);
after.union(mBefore);
((View) view.getParent()).invalidate(
(int) Math.floor(after.left),
(int) Math.floor(after.top),
(int) Math.ceil(after.right),
(int) Math.ceil(after.bottom));
}
private void computeRect(final RectF r, View view) {
// compute current rectangle according to matrix transformation
final float w = view.getWidth();
final float h = view.getHeight();
// use a rectangle at 0,0 to make sure we don't run into issues with scaling
r.set(0, 0, w, h);
final Matrix m = mTempMatrix;
m.reset();
transformMatrix(m, view);
mTempMatrix.mapRect(r);
r.offset(view.getLeft(), view.getTop());
// Straighten coords if rotations flipped them
if (r.right < r.left) {
final float f = r.right;
r.right = r.left;
r.left = f;
}
if (r.bottom < r.top) {
final float f = r.top;
r.top = r.bottom;
r.bottom = f;
}
}
private void transformMatrix(Matrix m, View view) {
final float w = view.getWidth();
final float h = view.getHeight();
final boolean hasPivot = mHasPivot;
final float pX = hasPivot ? mPivotX : w / 2f;
final float pY = hasPivot ? mPivotY : h / 2f;
final float rX = mRotationX;
final float rY = mRotationY;
final float rZ = mRotationZ;
if ((rX != 0) || (rY != 0) || (rZ != 0)) {
final Camera camera = mCamera;
camera.save();
camera.rotateX(rX);
camera.rotateY(rY);
camera.rotateZ(-rZ);
camera.getMatrix(m);
camera.restore();
m.preTranslate(-pX, -pY);
m.postTranslate(pX, pY);
}
final float sX = mScaleX;
final float sY = mScaleY;
if ((sX != 1.0f) || (sY != 1.0f)) {
m.postScale(sX, sY);
final float sPX = -(pX / w) * ((sX * w) - w);
final float sPY = -(pY / h) * ((sY * h) - h);
m.postTranslate(sPX, sPY);
}
m.postTranslate(mTranslationX, mTranslationY);
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
View view = mView.get();
if (view != null) {
t.setAlpha(mAlpha);
transformMatrix(t.getMatrix(), view);
}
}
}

View file

@ -0,0 +1,106 @@
package com.adins.mss.base;
import android.content.ComponentCallbacks2;
import android.content.Context;
import android.content.pm.PackageManager;
import androidx.multidex.MultiDexApplication;
import com.adins.mss.base.crashlytics.FireCrash;
import java.util.ArrayList;
import java.util.List;
/**
* Created by gigin.ginanjar on 25/02/2016.
*/
public abstract class AppContext extends MultiDexApplication {
protected static Context context = null;
protected static List<IMemoryInfo> memInfoList = new ArrayList<AppContext.IMemoryInfo>();
protected static AppContext instance;
private String versionName;
private int versionCode;
public static AppContext getInstance() {
return instance;
}
public static Context getAppContext() {
return AppContext.context;
}
public static void registerMemoryListener(IMemoryInfo implementor) {
memInfoList.add(implementor);
}
public static void unregisterMemoryListener(IMemoryInfo implementor) {
memInfoList.remove(implementor);
}
@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
if (level >= ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW) {
try {
// Activity at the front will get earliest than activity at the
// back
for (int i = memInfoList.size() - 1; i >= 0; i--) {
try {
memInfoList.get(i).goodTimeToReleaseMemory();
} catch (Exception e) {
FireCrash.log(e);
e.printStackTrace();
}
}
} catch (Exception e) {
FireCrash.log(e);
e.printStackTrace();
}
}
}
@Override
public void onCreate() {
// ACRA.init(this);
super.onCreate();
instance = this;
}
public String getVersionName() {
if (versionName == null || versionName.isEmpty()) {
try {
versionName = retrieveVersionName();
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
}
return versionName;
}
private String retrieveVersionName() throws PackageManager.NameNotFoundException {
return getPackageManager().getPackageInfo(
getPackageName(), 0).versionName;
}
public int getVersionCode() {
if (versionCode == 0) {
try {
versionCode = retrieveVersionCode();
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
}
return versionCode;
}
private int retrieveVersionCode() throws PackageManager.NameNotFoundException {
return getPackageManager().getPackageInfo(
getPackageName(), 0).versionCode;
}
public abstract Class getHomeClass();
public interface IMemoryInfo {
void goodTimeToReleaseMemory();
}
}

View file

@ -0,0 +1,562 @@
package com.adins.mss.base;
import android.Manifest;
import android.app.Activity;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Environment;
import android.os.Looper;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;
import androidx.core.app.NotificationCompat;
import androidx.core.content.ContextCompat;
import com.adins.mss.constant.Global;
import com.adins.mss.dao.TaskD;
import com.adins.mss.dao.TaskH;
import com.adins.mss.foundation.db.dataaccess.TaskDDataAccess;
import com.adins.mss.foundation.db.dataaccess.TaskHDataAccess;
import com.google.gson.Gson;
import com.google.gson.annotations.SerializedName;
import com.google.gson.reflect.TypeToken;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import static com.services.NotificationThread.getNotificationIcon;
import static java.lang.Thread.NORM_PRIORITY;
class BackupFormat {
@SerializedName("taskH")
private TaskH taskH;
@SerializedName("taskDList")
private List<TaskD> taskDList;
public TaskH getTaskH() {
return taskH;
}
public void setTaskH(TaskH taskH) {
this.taskH = taskH;
}
public List<TaskD> getTaskDList() {
return taskDList;
}
public void setTaskDList(List<TaskD> taskDList) {
this.taskDList = taskDList;
}
}
public class Backup {
private final Context context;
private File dir;
private static final String ENCRYPTION_ALGORITHM = "AES/ECB/PKCS5Padding";
private static final String ENCRYPTION_KEY = "8]6eUcL'b4}xhqC~";
public static List<String> updatingTask = new ArrayList<>();
public Backup(Context context) {
this.context = context;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
// Request permission if not granted
ActivityCompat.requestPermissions((Activity) context, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
return;
}
}
PackageInfo pInfo;
String appName = "";
try {
pInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
appName = pInfo.packageName+"."+BuildConfig.BUILD_TYPE+"/";
} catch (PackageManager.NameNotFoundException e) {
if(Global.IS_DEV)
e.printStackTrace();
}
String filePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/MssLocal/" + appName + GlobalData.getSharedGlobalData().getUser().getLogin_id();
// String filePath = context.getExternalFilesDir(null) + "/MssLocal/" + appName + GlobalData.getSharedGlobalData().getUser().getLogin_id();
// String filePath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS).getAbsolutePath() + "/MssLocal/" + appName + GlobalData.getSharedGlobalData().getUser().getUuid_user();
this.dir = new File(filePath);
// Ensure the directory exists
if (!dir.exists()) {
dir.mkdirs();
}
// Debugging output to check the directory
Log.d("Backup", "External storage state: " + Environment.getExternalStorageState());
Log.d("Backup", "Directory path: " + dir.getAbsolutePath());
Log.d("Backup", "Directory exists: " + dir.exists());
Log.d("Backup", "Directory readable: " + dir.canRead());
Log.d("Backup", "Directory writable: " + dir.canWrite());
Log.d("Backup", "Available space: " + dir.getFreeSpace());
}
private void showNotifBackup(String aggrNo, String content) {
String title;
if(!aggrNo.isEmpty()) {
title = "Task Backup - " + aggrNo;
} else {
title = "Task Backup";
}
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, "backup_channel");
builder.setSmallIcon(getNotificationIcon());
builder.setContentTitle(title);
builder.setContentText(content);
builder.setPriority(NORM_PRIORITY);
NotificationCompat.BigTextStyle inboxStyle =
new NotificationCompat.BigTextStyle();
// Sets a title for the Inbox in expanded layoutInflater
inboxStyle.setBigContentTitle(title);
inboxStyle.bigText(content);
inboxStyle.setSummaryText(context.getString(com.adins.mss.base.R.string.click_to_open));
builder.setDefaults(android.app.Notification.DEFAULT_ALL);
builder.setStyle(inboxStyle);
builder.setAutoCancel(true);
NotificationManager mNotificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
String channelId = "backup_channel";
NotificationChannel channel = new NotificationChannel(
channelId,
title,
NotificationManager.IMPORTANCE_HIGH);
mNotificationManager.createNotificationChannel(channel);
builder.setChannelId(channelId);
}
mNotificationManager.notify(1, builder.build());
}
public byte[] encrypt(String backupData) {
try {
SecretKey secretKey = getSecretKey();
Cipher cipher = Cipher.getInstance(ENCRYPTION_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] ciphertextBytes = backupData.getBytes("UTF-8");
return cipher.doFinal(ciphertextBytes);
} catch (Exception e) {
e.printStackTrace();
return null;
} catch (Error err) {
err.printStackTrace();
return null;
}
}
public String decrypt(InputStream encryptedStream) {
try {
SecretKey secretKey = getSecretKey();
Cipher cipher = Cipher.getInstance(ENCRYPTION_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, secretKey);
CipherInputStream cipherInputStream = new CipherInputStream(encryptedStream, cipher);
ByteArrayOutputStream output = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = cipherInputStream.read(buffer)) != -1) {
output.write(buffer, 0, bytesRead);
}
return new String(output.toByteArray(), "UTF-8");
} catch (Exception e) {
e.printStackTrace();
return null;
} catch (Error err) {
err.printStackTrace();
return null;
}
}
private SecretKey getSecretKey() throws NoSuchAlgorithmException {
byte[] keyBytes = ENCRYPTION_KEY.getBytes();
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, ENCRYPTION_ALGORITHM);
return secretKeySpec;
}
public BackupFormat getCurrentLocal(String uuidTaskH) {
if (dir.exists()) {
try {
File backupData = new File(dir, uuidTaskH);
FileInputStream inputStream = new FileInputStream(backupData);
String jsonBackup = decrypt(inputStream);
if (jsonBackup != null) {
Type type = new TypeToken<BackupFormat>() {}.getType();
return new Gson().fromJson(jsonBackup, type);
} else {
return null;
}
} catch (Exception e) {
e.printStackTrace();
return null;
} catch (Error err){
err.printStackTrace();
return null;
}
} else {
return null;
}
}
public void removeTask(List<TaskH> taskHList) {
if(null != taskHList && taskHList.size() > 0) {
new RemoveTask(taskHList).execute();
}
}
private int removeBackup(List<TaskH> taskHList, int attempt) {
int deletedCount = 0;
if (taskHList.size() > 0 && attempt <= 10) {
List<TaskH> tempTaskH = new ArrayList<>(taskHList);
for (TaskH taskH : tempTaskH) {
File file = new File(dir, taskH.getTask_id());
if (!updatingTask.contains(taskH.getUuid_task_h())) {
if (file.exists()) {
if (file.delete()) {
taskHList.remove(taskH);
deletedCount++;
} else{
taskHList.remove(taskH);
}
} else {
taskHList.remove(taskH);
}
}
}
if (taskHList.size() > 0) {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
deletedCount += removeBackup(taskHList, attempt + 1);
}
}
return deletedCount;
}
private class RemoveTask extends AsyncTask<Void, Void, Integer> {
List<TaskH> taskHList;
private RemoveTask(List<TaskH> taskHList) {
this.taskHList = taskHList;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
if (taskHList.size() == 1) {
showNotifBackup(taskHList.get(0).getTask_id(), context.getString(R.string.delete_backup_progress));
} else if (taskHList.size() > 1) {
showNotifBackup("", context.getString(R.string.delete_backup_progress));
}
}
@Override
protected Integer doInBackground(Void... voids) {
try {
return removeBackup(new ArrayList<>(taskHList), 1);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
@Override
protected void onPostExecute(Integer integer) {
super.onPostExecute(integer);
if(integer > 0) {
if(taskHList.size() == 1) {
showNotifBackup(taskHList.get(0).getTask_id(), context.getString(R.string.delete_backup_success));
} else if(taskHList.size() > 1) {
if(taskHList.size() == integer) {
showNotifBackup("", context.getString(R.string.delete_backup_success));
} else {
showNotifBackup("", context.getString(R.string.delete_backup_half_failed));
}
}
} else {
if(taskHList.size() == 1) {
showNotifBackup(taskHList.get(0).getTask_id(), context.getString(R.string.delete_backup_failed));
} else if(taskHList.size() > 1) {
showNotifBackup("", context.getString(R.string.delete_backup_failed));
}
}
}
}
public void performBackup(TaskH taskH) {
if(Thread.currentThread() != Looper.getMainLooper().getThread()) {
//called from non main thread
if (null != taskH) {
performBackups(taskH);
}
} else {
//called from main thread
if (null != taskH) {
new PerformBackupTask(taskH).execute();
}
}
}
private void performBackups(TaskH taskH) {
updatingTask.add(taskH.getUuid_task_h());
showNotifBackup(taskH.getTask_id(), context.getString(R.string.update_backup_progress));
BackupFormat backupFormat = new BackupFormat();
boolean isSuccess = false;
try {
List<TaskD> taskDList = TaskDDataAccess.getListByTaskH(context, taskH.getUuid_task_h());
backupFormat.setTaskH(taskH);
backupFormat.setTaskDList(taskDList);
Gson gsonBackupFormat = new Gson();
byte[] encryptedJson = encrypt(gsonBackupFormat.toJson(backupFormat));
FileOutputStream outputStream = null;
try {
if(!dir.exists()) {
dir.mkdirs();
}
long requiredSpace = encryptedJson.length;
long availableSpace = dir.getFreeSpace();
if (requiredSpace < availableSpace) {
File file = new File(dir, taskH.getTask_id());
outputStream = new FileOutputStream(file);
outputStream.write(encryptedJson);
outputStream.close();
isSuccess = true;
}
} catch (IOException e) {
e.printStackTrace();
isSuccess = false;
} finally {
assert outputStream != null;
outputStream.close();
}
} catch (Exception e) {
e.printStackTrace();
isSuccess = false;
}
if(isSuccess) {
showNotifBackup(taskH.getTask_id(), context.getString(R.string.update_backup_success));
} else {
showNotifBackup(taskH.getTask_id(), context.getString(R.string.update_backup_failed));
}
updatingTask.remove(taskH.getUuid_task_h());
}
private class PerformBackupTask extends AsyncTask<Void, Void, Boolean> {
BackupFormat backupFormat;
ProgressDialog progressDialog;
TaskH taskH;
private PerformBackupTask(TaskH taskH) {
this.taskH = taskH;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
try {
progressDialog = ProgressDialog.show(context,
"", context.getString(R.string.progressWait), true);
} catch (Exception e) {
e.printStackTrace();
}
updatingTask.add(taskH.getUuid_task_h());
showNotifBackup(taskH.getTask_id(), context.getString(R.string.update_backup_progress));
backupFormat = new BackupFormat();
}
@Override
protected Boolean doInBackground(Void... voids) {
if(!dir.exists()) {
dir.mkdirs();
}
backupFormat = new BackupFormat();
try {
List<TaskD> taskDList = TaskDDataAccess.getListByTaskH(context, taskH.getUuid_task_h());
backupFormat.setTaskH(taskH);
backupFormat.setTaskDList(taskDList);
Gson gsonBackupFormat = new Gson();
byte[] encryptedJson = encrypt(gsonBackupFormat.toJson(backupFormat));
FileOutputStream outputStream = null;
try {
long requiredSpace = encryptedJson.length;
long availableSpace = dir.getFreeSpace();
if (requiredSpace > availableSpace) {
return false;
}
File file = new File(dir, taskH.getTask_id());
outputStream = new FileOutputStream(file);
outputStream.write(encryptedJson);
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
return false;
} finally {
assert outputStream != null;
outputStream.close();
}
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
@Override
protected void onPostExecute(Boolean aBoolean) {
super.onPostExecute(aBoolean);
if (null != progressDialog && progressDialog.isShowing()){
try {
progressDialog.dismiss();
} catch (Exception e) {
e.printStackTrace();
}
}
updatingTask.remove(taskH.getUuid_task_h());
if(aBoolean) {
showNotifBackup(taskH.getTask_id(), context.getString(R.string.update_backup_success));
} else {
showNotifBackup(taskH.getTask_id(), context.getString(R.string.update_backup_failed));
}
}
}
public void performRestore() {
RetrieveBackupTask retrieveBackup = new RetrieveBackupTask();
retrieveBackup.execute();
}
private class RetrieveBackupTask extends AsyncTask<Void, Void, Integer> {
ProgressDialog progressDialog;
BackupFormat backupFormat;
@Override
protected void onPreExecute() {
super.onPreExecute();
progressDialog = ProgressDialog.show(context,
"", context.getString(R.string.progressWait), true);
showNotifBackup("", context.getString(R.string.restore_backup_progress));
}
@Override
protected Integer doInBackground(Void... voids) {
String uuidTaskHList[] = dir.list();
int restoredTask = 0;
if(null != uuidTaskHList && uuidTaskHList.length > 0) {
for (int i = 0; i < uuidTaskHList.length; i++) {
backupFormat = getCurrentLocal(uuidTaskHList[i]);
if (backupFormat == null) {
continue;
}
try {
TaskHDataAccess.addOrReplace(context, backupFormat.getTaskH());
TaskDDataAccess.addOrReplace(context, backupFormat.getTaskDList());
restoredTask++;
} catch (Exception e) {
e.printStackTrace();
}
}
}
return restoredTask;
}
@Override
protected void onPostExecute(Integer integer) {
super.onPostExecute(integer);
if (null != progressDialog && progressDialog.isShowing()){
try {
progressDialog.dismiss();
} catch (Exception e) {
e.printStackTrace();
}
}
if(integer == 0) {
showNotifBackup("", context.getString(R.string.restore_backup_failed));
} else {
showNotifBackup("", context.getString(R.string.restore_backup_success) + integer);
}
}
}
}

View file

@ -0,0 +1,87 @@
package com.adins.mss.base;
/**
* BaseCommunicationModel provides a class with basic parameters needed in most communication with MSS Server,
* like user login Id and IMEI code. Any class which are going to communicate in JSON should use/subclass this model object
* to convert to JSON to ensure the same JSON format with MSS Server's JSON
*
* @author glen.iglesias
* @deprecated as of 17 Dec 2014, communication should use MssRequestType and MssResponseType as standard format
*/
public class BaseCommunicationModel {
protected String osName;
protected String userId;
protected String deviceModel;
protected String imei;
protected String imsi;
protected String pin;
protected String androidId;
public BaseCommunicationModel() {
}
public BaseCommunicationModel(boolean useDefault) {
if (useDefault) {
GlobalData gd = GlobalData.getSharedGlobalData();
osName = gd.getOsName();
deviceModel = gd.getDeviceModel();
imei = gd.getImei();
imsi = gd.getImsi();
pin = "ANDROID";
androidId = GlobalData.getSharedGlobalData().getAndroidId();
userId = gd.getUser().getLogin_id();
}
}
public String getOsName() {
return osName;
}
public void setOsName(String osName) {
this.osName = osName;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getDeviceModel() {
return deviceModel;
}
public void setDeviceModel(String deviceModel) {
this.deviceModel = deviceModel;
}
public String getImei() {
return imei;
}
public void setImei(String imei) {
this.imei = imei;
}
public String getImsi() {
return imsi;
}
public void setImsi(String imsi) {
this.imsi = imsi;
}
public String getPin() {
return pin;
}
public void setPin(String pin) {
this.pin = pin;
}
}

View file

@ -0,0 +1,423 @@
package com.adins.mss.base;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.ImageFormat;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.Menu;
import android.view.MenuItem;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ListAdapter;
import android.widget.RelativeLayout;
import android.widget.Toast;
import com.adins.mss.base.crashlytics.FireCrash;
import com.adins.mss.base.util.LocaleHelper;
import com.adins.mss.constant.Global;
import com.adins.mss.foundation.camera.Camera;
import com.adins.mss.foundation.camera.ImageCallBack;
import com.adins.mss.foundation.image.Utils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
/**
* Created by winy.firdasari on 14/01/2015.
*/
public class CameraPreviewActivity extends Activity {
// ---
private static final int BACK_MENU_ID = Menu.FIRST;
private static final int OPTIONS_MENU_ID = Menu.FIRST + 1;
private static final int CAPTURE_MENU_ID = Menu.FIRST + 2;
RelativeLayout container;
Context context;
boolean hasCamera;
private Preview mPreview;
private ListAdapter listPicSize;
private int rotate = 0;
private Button btCapture;
private Activity activity;
private int actualWidht = Utils.picWidth;
private int actualHeight = Utils.picHeight;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Hide the window title.
requestWindowFeature(Window.FEATURE_NO_TITLE);
this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
context = this;
mPreview = new Preview(this);
setContentView(mPreview);
hasCamera = Camera.checkCameraHardware(this);
setContentView(R.layout.camera_preview_layout);
container = (RelativeLayout) findViewById(R.id.container);
LinearLayout previewContainer = (LinearLayout) findViewById(R.id.previewContainer);
previewContainer.addView(mPreview);
btCapture = (Button) findViewById(R.id.btCaptureCam);
btCapture.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mPreview.mCamera.setAutoFocus();
mPreview.mCamera.setFlashAuto();
mPreview.mCamera.getPicture(new ImageCallBack() {
@Override
public void onPictureTaken(byte[] bytes, Object o) {
Intent intent = new Intent();
intent.putExtra(Camera.BUND_KEY_IMAGE_BYTE, bytes);
setResult(Global.REQUEST_CAMERA, intent);
}
});
}
});
this.activity = this;
}
@Override
protected void attachBaseContext(Context newBase) {
Context context = newBase;
Locale locale;
try{
locale = new Locale(GlobalData.getSharedGlobalData().getLocale());
context = LocaleHelper.wrap(newBase, locale);
} catch (Exception e) {
locale = new Locale(LocaleHelper.ENGLSIH);
context = LocaleHelper.wrap(newBase, locale);
} finally {
super.attachBaseContext(context);
}
}
public boolean getLCDSceenTipeLandscape() {
boolean result = false;
DisplayMetrics dm = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);
String str_ScreenSize = "The Android Screen is: W="
+ dm.widthPixels
+ " x H="
+ dm.heightPixels;
result = dm.widthPixels > dm.heightPixels;
return result;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
menu.add(0, BACK_MENU_ID, 0, "Back");
menu.add(0, OPTIONS_MENU_ID, 0, "Options");
menu.add(0, CAPTURE_MENU_ID, 0, "Capture");
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case BACK_MENU_ID:
this.finish();
return true;
case OPTIONS_MENU_ID:
this.showOptions();
return true;
case CAPTURE_MENU_ID:
return true;
default:
return super.onOptionsItemSelected(item);
}
}
private void showOptions() {
this.loadSupportedPicSize();
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Picture Size");
builder.setSingleChoiceItems(listPicSize, -1, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
String resolution = (String) listPicSize.getItem(item);
String[] sz = resolution.split("x");
int w = Integer.parseInt(sz[0]);
int h = Integer.parseInt(sz[1]);
mPreview.setPictureSize(w, h);
mPreview.mCamera.getCamera().stopPreview();
android.hardware.Camera.Size previewSize = mPreview.getOptimalPreviewSize(w, h);
mPreview.setPreviewSize(previewSize.width, previewSize.height);
mPreview.mCamera.getCamera().startPreview();
try {
dialog.dismiss();
} catch (Exception e) {
FireCrash.log(e);
}
Toast.makeText(getApplicationContext(), getString(R.string.picture_resolution, resolution), Toast.LENGTH_SHORT).show();
}
});
builder.create().show();
}
private void loadSupportedPicSize() {
if (listPicSize == null) {
List<String> listSize = mPreview.getSupportedPictureSize();
listPicSize = new ArrayAdapter<String>(this, R.layout.picture_size_list, listSize);
}
}
public class Preview extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder mHolder;
private Camera mCamera;
public Preview(Context context) {
super(context);
mHolder = getHolder();
mHolder.addCallback(this);
}
public List<String> getSupportedPictureSize() {
List<android.hardware.Camera.Size> listSize = mCamera.getCamera().getParameters().getSupportedPictureSizes();
List<String> listSizeStr = new ArrayList<>();
for (android.hardware.Camera.Size size : listSize) {
listSizeStr.add(size.width + "x" + size.height);
}
return listSizeStr;
}
public void surfaceCreated(SurfaceHolder holder) {
try {
android.hardware.Camera cam = android.hardware.Camera.open();
mCamera = new Camera(context, activity, cam, cam.getParameters());
} catch (Exception e) {
FireCrash.log(e);
}
// default to set picture size to lowest resolution
List<android.hardware.Camera.Size> listSupportedSize = mCamera.getCamera().getParameters().getSupportedPictureSizes();
if (listSupportedSize != null && !listSupportedSize.isEmpty()) {
android.hardware.Camera.Size minimumSize = listSupportedSize.get(searchMostSuportedSizeFromParam(listSupportedSize));
android.hardware.Camera.Parameters parameters = mCamera.getCamera().getParameters();
parameters.setPictureSize(minimumSize.width, minimumSize.height);
mCamera.getCamera().setParameters(parameters);
}
try {
mCamera.getCamera().setPreviewDisplay(holder);
mCamera.getCamera().setFaceDetectionListener(new com.adins.mss.foundation.camera.FaceDetectionListener());
mCamera.startFaceDetection();
} catch (IOException exception) {
mCamera.getCamera().release();
mCamera = null;
}
}
public int searchMostSuportedSizeFromParam(List<android.hardware.Camera.Size> listSupportedSize) {
android.hardware.Camera.Size suportedSize;
int idx = 0;
//search for match w x h
for (int i = 0; i < listSupportedSize.size(); i++) {
if (listSupportedSize.get(i).width == actualWidht && listSupportedSize.get(i).height == actualHeight) {
return i;
}
}
//search for match h
for (int i = 0; i < listSupportedSize.size(); i++) {
if (listSupportedSize.get(i).height == actualHeight) {
return i;
}
}
//search for match w
for (int i = 0; i < listSupportedSize.size(); i++) {
if (listSupportedSize.get(i).width == actualWidht) {
return i;
}
}
return listSupportedSize.size() - 1;
}
public void surfaceDestroyed(SurfaceHolder holder) {
mCamera.getCamera().stopPreview();
mCamera.getCamera().release();
mCamera = null;
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
android.hardware.Camera.Parameters parameters = mCamera.getCamera().getParameters();
android.hardware.Camera.Size resultSize = null;
try {
resultSize = getOptimalPreviewSize(w, h);
if (resultSize != null) {
w = resultSize.width;
h = resultSize.height;
}
if (resultSize != null)
parameters.setPreviewSize(w, h);
} catch (Exception e) {
FireCrash.log(e);
resultSize = null;
}
parameters.setPictureFormat(ImageFormat.JPEG);
parameters.setFocusMode(android.hardware.Camera.Parameters.FOCUS_MODE_AUTO);
parameters.setFlashMode(android.hardware.Camera.Parameters.FLASH_MODE_OFF);
if (resultSize != null) {
int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
android.hardware.Camera.CameraInfo info =
new android.hardware.Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(android.hardware.Camera.CameraInfo.CAMERA_FACING_BACK, info);
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0:
degrees = 0;
break;
case Surface.ROTATION_90:
degrees = 90;
break;
case Surface.ROTATION_180:
degrees = 180;
break;
case Surface.ROTATION_270:
degrees = 270;
break;
default:
break;
}
int result;
if (info.facing == android.hardware.Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degrees) % 360;
result = (360 - result) % 360; // compensate the mirror
} else { // back-facing
result = (info.orientation - degrees + 360) % 360;
}
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
rotate = 90;
}
//khusus untuk device dengan lcd landscape, ex: htc chacha
if (result == 90 && getLCDSceenTipeLandscape()) {
result = 0;
} else if (result == 0 && !getLCDSceenTipeLandscape()) {
result = 270;
} else if (result == 180 && !getLCDSceenTipeLandscape()) {
result = 90;
} else if (result == 270 && getLCDSceenTipeLandscape()) {
result = 180;
}
mCamera.getCamera().setDisplayOrientation(result);
}
try {
mCamera.getCamera().setParameters(parameters);
mCamera.getCamera().setFaceDetectionListener(new com.adins.mss.foundation.camera.FaceDetectionListener());
mCamera.startFaceDetection();
} catch (Exception e) {
FireCrash.log(e);
parameters = mCamera.getCamera().getParameters();
parameters.setPictureFormat(ImageFormat.JPEG);
parameters.setFocusMode(android.hardware.Camera.Parameters.FOCUS_MODE_AUTO);
parameters.setFlashMode(android.hardware.Camera.Parameters.FLASH_MODE_AUTO);
try {
mCamera.getCamera().setParameters(parameters);
mCamera.getCamera().setFaceDetectionListener(new com.adins.mss.foundation.camera.FaceDetectionListener());
mCamera.startFaceDetection();
} catch (Exception e2) {
parameters = mCamera.getCamera().getParameters();
mCamera.getCamera().setParameters(parameters);
}
}
mCamera.getCamera().startPreview();
}
public void setPictureSize(int width, int height) {
android.hardware.Camera.Parameters cp = mCamera.getCamera().getParameters();
cp.setPictureSize(width, height);
mCamera.getCamera().setParameters(cp);
}
public android.hardware.Camera.Size getOptimalPreviewSize(int picWidth, int picHeight) {
List<android.hardware.Camera.Size> sizes = mCamera.getCamera().getParameters().getSupportedPreviewSizes();
final double ASPECT_TOLERANCE = 0.1;
double targetRatio = (double) picWidth / picHeight;
if (sizes == null)
return null;
android.hardware.Camera.Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
int targetHeight = picHeight;
// Try to find an size match aspect ratio and size
for (android.hardware.Camera.Size size : sizes) {
double ratio = (double) size.width / size.height;
if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE)
continue;
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
// Cannot find the one match the aspect ratio, ignore the
// requirement
if (optimalSize == null) {
minDiff = Double.MAX_VALUE;
for (android.hardware.Camera.Size size : sizes) {
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
}
return optimalSize;
}
public void setPreviewSize(int width, int height) {
android.hardware.Camera.Parameters cp = mCamera.getCamera().getParameters();
cp.setPreviewSize(width, height);
mCamera.getCamera().setParameters(cp);
}
}
}

View file

@ -0,0 +1,48 @@
package com.adins.mss.base;
import android.content.Context;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentTransaction;
import com.adins.mss.base.util.LocaleHelper;
import org.acra.ACRA;
import java.util.Locale;
/**
* Created by adityapurwa on 15/04/15.
*/
public class ChangePasswordActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
ACRA.getErrorReporter().putCustomData("LAST_CLASS_ACCESSED", getClass().getSimpleName());
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_change_password);
Fragment fragment = new ChangePasswordFragment();
Bundle args = new Bundle();
args.putBoolean(ChangePasswordFragment.AS_ACTIVITY, true);
fragment.setArguments(args);
FragmentTransaction
transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragmentRoot, fragment);
transaction.addToBackStack(null);
transaction.commit();
}
@Override
protected void attachBaseContext(Context newBase) {
Context context = newBase;
Locale locale;
try{
locale = new Locale(GlobalData.getSharedGlobalData().getLocale());
context = LocaleHelper.wrap(newBase, locale);
} catch (Exception e) {
locale = new Locale(LocaleHelper.ENGLSIH);
context = LocaleHelper.wrap(newBase, locale);
} finally {
super.attachBaseContext(context);
}
}
}

View file

@ -0,0 +1,210 @@
package com.adins.mss.base;
import android.annotation.SuppressLint;
import android.app.ActionBar;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import com.adins.mss.base.api.ChangePasswordApi;
import com.adins.mss.base.api.ChangePasswordApiCallback;
import com.adins.mss.base.crashlytics.FireCrash;
import com.adins.mss.base.dynamicform.form.questions.QuestionsValidator;
import com.adins.mss.base.errorhandler.ErrorMessageHandler;
import com.adins.mss.base.errorhandler.IShowError;
import com.adins.mss.base.login.DefaultLoginModel;
import com.adins.mss.base.mainmenu.MainMenuHelper;
import com.adins.mss.base.models.ChangePasswordRequestModel;
import com.adins.mss.base.util.Utility;
import com.adins.mss.constant.Global;
import com.adins.mss.dao.User;
import com.adins.mss.foundation.db.dataaccess.UserDataAccess;
import org.acra.ACRA;
/**
* Created by adityapurwa on 30/03/15.
*/
public class ChangePasswordFragment extends Fragment implements IShowError {
public static final String AS_ACTIVITY = "ChangePasswordActivity.AS_ACTIVITY";
private EditText currentPassword;
private EditText newPassword;
private EditText retypePassword;
private Button changePassword;
ErrorMessageHandler errorMessageHandler;
//bong 7 apr 15 not override
public static void onBackPressed() {
//EMPTY
}
@SuppressLint("NewApi")
@Override
public void onAttach(Context activity) {
super.onAttach(activity);
ACRA.getErrorReporter().putCustomData("LAST_CLASS_ACCESSED", getClass().getSimpleName());
getActivity().getActionBar().setTitle(getString(R.string.title_mn_changepassword));
getActivity().getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
errorMessageHandler = new ErrorMessageHandler(this);
}
@Override
public void onDestroyView() {
super.onDestroyView();
Utility.freeMemory();
}
@Override
public void onResume() {
super.onResume();
getActivity().getActionBar().setTitle(getString(R.string.title_mn_changepassword));
getActivity().getActionBar().removeAllTabs();
getActivity().getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
View actionBar = view.findViewById(R.id.actionbar);
final boolean asActivity = getArguments().getBoolean(AS_ACTIVITY);
if (actionBar != null && asActivity) {
actionBar.setVisibility(View.GONE);
}
String pwdExp = getActivity().getIntent().getStringExtra(DefaultLoginModel.PWD_EXP);
try {
if (pwdExp.equals("1")) {
errorMessageHandler.processError("",getActivity().getString(R.string.password_expired), ErrorMessageHandler.TOAST_TYPE);
}
} catch (Exception e) {
FireCrash.log(e);
}
currentPassword = (EditText) view.findViewById(R.id.currentPassword);
newPassword = (EditText) view.findViewById(R.id.newPassword);
retypePassword = (EditText) view.findViewById(R.id.retypePassword);
changePassword = (Button) view.findViewById(R.id.changePassword);
changePassword.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (currentPassword.getText() == null || currentPassword.getText().toString().equals("")) {
currentPassword.setError(getActivity().getString(R.string.current_password_mandatory));
}
if (newPassword.getText() == null ||
newPassword.getText().toString().equals("")) {
newPassword.setError(getActivity().getString(R.string.new_password_mandatory));
return;
}
if (!newPassword.getText().toString().equals(retypePassword.getText().toString())) {
errorMessageHandler.processError(""
,getActivity().getString(R.string.new_password_equal),
ErrorMessageHandler.TOAST_TYPE);
return;
}
if (newPassword.getText().toString().equals(currentPassword.getText().toString())) {
errorMessageHandler.processError(""
,getActivity().getString(R.string.new_password_different)
, ErrorMessageHandler.TOAST_TYPE);
}
ChangePasswordRequestModel request = new ChangePasswordRequestModel();
request.setUuid_user(GlobalData.getSharedGlobalData().getUser().getUuid_user());
request.setOld_password(currentPassword.getText().toString());
request.setNew_password(newPassword.getText().toString());
request.setAudit(GlobalData.getSharedGlobalData().getAuditData());
ChangePasswordApi api = new ChangePasswordApi(getActivity());
api.setCallback(new ChangePasswordApiCallback() {
@Override
public void onFailed(String message) {
try {
errorMessageHandler.processError(getActivity().getString(R.string.warning_capital)
,message
, ErrorMessageHandler.TOAST_TYPE);
} catch (Exception e) {
FireCrash.log(e);
errorMessageHandler.processError("","Change Password Error", ErrorMessageHandler.TOAST_TYPE);
}
}
@Override
public void onSuccess() {
Toast.makeText(getActivity(), getActivity().getString(R.string.password_changed), Toast.LENGTH_LONG).show();
if (!asActivity) {
User user = GlobalData.getSharedGlobalData().getUser();
user.setPassword(newPassword.getText().toString());
UserDataAccess.addOrReplace(getActivity(), user);
MainMenuHelper.doBackFragment(getActivity());
} else {
User user = GlobalData.getSharedGlobalData().getUser();
user.setPassword(newPassword.getText().toString());
UserDataAccess.addOrReplace(getActivity(), user);
goToSynchronize();
Global.syncIntent = null;
getActivity().finish();
}
clear();
}
});
api.execute(request);
}
});
}
private void clear() {
currentPassword.setText("");
newPassword.setText("");
retypePassword.setText("");
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_change_password, container, false);
}
public boolean checkRegex(EditText editText) {
String regex = "^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d]{8,}$";
String answer = editText.getText().toString().trim();
if ( !answer.isEmpty()) {
if (QuestionsValidator.regexIsMatch(answer, regex)) {
return true;
} else {
editText.setError(getActivity().getString(R.string.alpha_numeric_warning));
Toast.makeText(getActivity(), getActivity().getString(R.string.input_not_valid), Toast.LENGTH_SHORT).show();
return false;
}
}
return true;
}
private void goToSynchronize() {
if (Global.syncIntent != null) {
Intent syncIntent = Global.syncIntent;
startActivity(syncIntent);
}
}
@Override
public void showError(String errorSubject, String errorMsg, int notifType) {
if(notifType == ErrorMessageHandler.TOAST_TYPE){
if(errorSubject == null || errorSubject.equals(""))
Toast.makeText(getActivity(), errorMsg, Toast.LENGTH_SHORT).show();
else {
Toast.makeText(getActivity(), errorSubject+": "+errorMsg, Toast.LENGTH_SHORT).show();
}
}
}
}

View file

@ -0,0 +1,170 @@
package com.adins.mss.base;
import android.content.Context;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import android.view.View;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.Switch;
import android.widget.Toast;
import com.adins.mss.base.util.LocaleHelper;
import com.adins.mss.constant.Global;
import com.adins.mss.foundation.config.ConfigFileReader;
import com.adins.mss.foundation.security.storepreferences.ObscuredSharedPreferences;
import com.google.firebase.analytics.FirebaseAnalytics;
import java.util.Locale;
import java.util.Properties;
public class DeveloperOptionActivity extends AppCompatActivity {
private Button btnSave;
private Switch switchEncrypt;
private Switch switchDecrypt;
private Switch switchAccessToken;
private Switch switchDevMode;
private LinearLayout clientIdLayout;
private EditText edtClientId;
private boolean isEncrypt;
private boolean isDecrypt;
private boolean isAccessTokenEnable;
private boolean isByPassEnable;
private FirebaseAnalytics screenName;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
screenName = FirebaseAnalytics.getInstance(this);
setContentView(R.layout.new_developer_option_activity);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
toolbar.setTitleTextColor(getResources().getColor(R.color.fontColorWhite));
setSupportActionBar(toolbar);
initViews();
}
@Override
protected void onResume() {
super.onResume();
//Set Firebase screen name
screenName.setCurrentScreen(this, getString(R.string.screen_name_developer_option), null);
}
@Override
protected void attachBaseContext(Context newBase) {
Context context = newBase;
Locale locale;
try{
locale = new Locale(GlobalData.getSharedGlobalData().getLocale());
context = LocaleHelper.wrap(newBase, locale);
} catch (Exception e) {
locale = new Locale(LocaleHelper.ENGLSIH);
context = LocaleHelper.wrap(newBase, locale);
} finally {
super.attachBaseContext(context);
}
}
private void initViews() {
btnSave = (Button) findViewById(R.id.btnSave);
switchDecrypt = (Switch) findViewById(R.id.switchDecrypt);
switchEncrypt = (Switch) findViewById(R.id.switchEncrypt);
switchAccessToken = (Switch) findViewById(R.id.switchAccessToken);
switchDevMode = (Switch) findViewById(R.id.switchDevMode);
clientIdLayout = (LinearLayout) findViewById(R.id.layoutClientId);
edtClientId = (EditText) findViewById(R.id.edtClientId);
final ObscuredSharedPreferences sharedPref = ObscuredSharedPreferences.getPrefs(getApplicationContext(),
"GlobalData", Context.MODE_PRIVATE);
Properties prop = ConfigFileReader.propertiesFromFile(this, GlobalData.PROPERTY_FILENAME);
boolean encrypt = Boolean.parseBoolean(prop.getProperty(GlobalData.PROP_ENCRYPT, "false"));
boolean decrypt = Boolean.parseBoolean(prop.getProperty(GlobalData.PROP_DECRYPT, "false"));
String propClientId = prop.getProperty(GlobalData.PROP_CLIENT_ID, "android");
boolean byPassEnable = Boolean.parseBoolean(prop.getProperty(GlobalData.PROP_IS_BYPASS_DEVELOPER, "false"));
boolean hasEncrypt = sharedPref.getBoolean("IS_ENCRYPT", encrypt);
boolean hasDecrypt = sharedPref.getBoolean("IS_DECRYPT", decrypt);
boolean tokenFromPropValue = Boolean.parseBoolean(prop.getProperty(GlobalData.PROP_IS_REQUIRED_ACCESS_TOKEN, "false"));
boolean isTokenEnable = sharedPref.getBoolean("IS_ACCESS_TOKEN_ENABLE",false);
String firstSetting = sharedPref.getString("IS_DEV_FIRST_SETTING", Global.TRUE_STRING);
if(!isTokenEnable && tokenFromPropValue && Global.TRUE_STRING.equalsIgnoreCase(firstSetting)){
isTokenEnable = tokenFromPropValue;
}
final String clientId = sharedPref.getString("CLIENT_ID", propClientId);
isByPassEnable = sharedPref.getBoolean("IS_BYPASS_ENABLE", byPassEnable);
switchAccessToken.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
clientIdLayout.setVisibility(View.VISIBLE);
edtClientId.setText(clientId);
} else {
clientIdLayout.setVisibility(View.GONE);
}
}
});
if (hasEncrypt) {
switchEncrypt.setChecked(true);
}
if (hasDecrypt) {
switchDecrypt.setChecked(true);
}
if (isTokenEnable) {
switchAccessToken.setChecked(true);
clientIdLayout.setVisibility(View.VISIBLE);
edtClientId.setText(clientId);
} else {
clientIdLayout.setVisibility(View.GONE);
}
if (isByPassEnable) {
switchDevMode.setChecked(true);
}
btnSave.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ObscuredSharedPreferences.Editor sharedPrefEditor = sharedPref.edit();
isEncrypt = switchEncrypt.isChecked();
isDecrypt = switchDecrypt.isChecked();
isAccessTokenEnable = switchAccessToken.isChecked();
isByPassEnable = switchDevMode.isChecked();
if (isAccessTokenEnable && edtClientId.getText() != null && edtClientId.getText().length() > 0) {
String clientId = edtClientId.getText().toString();
sharedPrefEditor.putString("CLIENT_ID", clientId);
GlobalData.getSharedGlobalData().setClientId(clientId);
}
GlobalData.getSharedGlobalData().setEncrypt(isEncrypt);
GlobalData.getSharedGlobalData().setDecrypt(isDecrypt);
GlobalData.getSharedGlobalData().setRequiresAccessToken(isAccessTokenEnable);
GlobalData.getSharedGlobalData().setByPassDeveloper(isByPassEnable);
String isFirstSetting = sharedPref.getString("IS_DEV_FIRST_SETTING", Global.TRUE_STRING);
if(Global.TRUE_STRING.equalsIgnoreCase(isFirstSetting)){
sharedPrefEditor.putString("IS_DEV_FIRST_SETTING",Global.FALSE_STRING);
}
sharedPrefEditor.putBoolean("IS_ENCRYPT", isEncrypt);
sharedPrefEditor.putBoolean("IS_DECRYPT", isDecrypt);
sharedPrefEditor.putBoolean("IS_ACCESS_TOKEN_ENABLE", isAccessTokenEnable);
sharedPrefEditor.putBoolean("IS_BYPASS_ENABLE", isByPassEnable);
sharedPrefEditor.commit();
Toast.makeText(DeveloperOptionActivity.this, getString(R.string.options_saved), Toast.LENGTH_SHORT).show();
finish();
}
});
}
}

View file

@ -0,0 +1,70 @@
package com.adins.mss.base;
import android.content.Context;
import android.graphics.Color;
public class GlobalUI {
public static int color01 = 0;
public static int color02 = 0;
public static int color03 = 0;
public static int color04 = 0;
public static int color05 = 0;
public static int color06 = 0;
public static int color07 = 0;
public static int color08 = 0;
public GlobalUI() {
// TODO Auto-generated constructor stub
}
public static void setColor01(String hexaColor) {
color01 = Color.parseColor(hexaColor);
}
public static void setColor02(String hexaColor) {
color02 = Color.parseColor(hexaColor);
}
public static void setColor03(String hexaColor) {
color03 = Color.parseColor(hexaColor);
}
public static void setColor04(String hexaColor) {
color04 = Color.parseColor(hexaColor);
}
public static void setColor05(String hexaColor) {
color05 = Color.parseColor(hexaColor);
}
public static void setColor06(String hexaColor) {
color06 = Color.parseColor(hexaColor);
}
public static void setColor07(String hexaColor) {
color07 = Color.parseColor(hexaColor);
}
public static void setColor08(String hexaColor) {
color08 = Color.parseColor(hexaColor);
}
public static void setAllColor(String hexaColorArr[]) {
color01 = Color.parseColor(hexaColorArr[0]);
color02 = Color.parseColor(hexaColorArr[1]);
color03 = Color.parseColor(hexaColorArr[2]);
color04 = Color.parseColor(hexaColorArr[3]);
color05 = Color.parseColor(hexaColorArr[4]);
color06 = Color.parseColor(hexaColorArr[5]);
color07 = Color.parseColor(hexaColorArr[6]);
color08 = Color.parseColor(hexaColorArr[7]);
}
public static int getImage(Context context, String fileNamePath) {
// int id = context.getResources().getIdentifier("yourpackagename:drawable/" + StringGenerated, null, null);
int id = context.getResources().getIdentifier(fileNamePath, null, null);
return id;
}
}

View file

@ -0,0 +1,72 @@
package com.adins.mss.base;
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.PermissionChecker;
import androidx.fragment.app.Fragment;
import com.adins.mss.base.util.Utility;
public class LocationPermissionInformationFragment extends Fragment {
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_location_permission_information, container, false);
TextView turnOnLocPermission = view.findViewById(R.id.turn_on_text);
TextView gotoTermAndCondition = view.findViewById(R.id.termAndConditionText);
TextView gotoPrivacyAndPolicy = view.findViewById(R.id.PrivacyAndPolicyText);
turnOnLocPermission.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Utility.checkPermissionGranted(getActivity());
}
});
gotoTermAndCondition.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Uri uri = Uri.parse("http://app.ad-ins.com/mobileone/TermsConditions.html"); // missing 'http://' will cause crashed
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
}
});
gotoPrivacyAndPolicy.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Uri uri = Uri.parse("http://app.ad-ins.com/mobileone/KebijakanPrivasi.html"); // missing 'http://' will cause crashed
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
}
});
return view;
}
@Override
public void onResume() {
super.onResume();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (getActivity().checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED &&
getActivity().checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
getActivity().getSupportFragmentManager().popBackStack();
}
} else {
if (PermissionChecker.checkSelfPermission(getActivity(), Manifest.permission.ACCESS_FINE_LOCATION) == PermissionChecker.PERMISSION_GRANTED &&
PermissionChecker.checkSelfPermission(getActivity(), Manifest.permission.ACCESS_COARSE_LOCATION) == PermissionChecker.PERMISSION_GRANTED) {
getActivity().getSupportFragmentManager().popBackStack();
}
}
}
}

View file

@ -0,0 +1,587 @@
package com.adins.mss.base;
import android.Manifest;
import android.app.ProgressDialog;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.ColorStateList;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.core.content.PermissionChecker;
import androidx.fragment.app.Fragment;
import com.adins.mss.base.commons.Helper;
import com.adins.mss.base.commons.ModeledActivity;
import com.adins.mss.base.crashlytics.FireCrash;
import com.adins.mss.base.dynamictheme.DynamicTheme;
import com.adins.mss.base.dynamictheme.ThemeLoader;
import com.adins.mss.base.dynamictheme.ThemeUtility;
import com.adins.mss.base.login.DefaultLoginModel;
import com.adins.mss.base.login.LoginImpl;
import com.adins.mss.base.login.LoginModel;
import com.adins.mss.base.syncfile.DownloadParams;
import com.adins.mss.base.syncfile.FileDownloader;
import com.adins.mss.base.syncfile.FileSyncHelper;
import com.adins.mss.base.syncfile.ImportDbFromCsv;
import com.adins.mss.base.syncfile.ImportDbParams;
import com.adins.mss.base.util.LocaleHelper;
import com.adins.mss.base.util.Utility;
import com.adins.mss.constant.Global;
import com.adins.mss.dao.MobileDataFile;
import com.adins.mss.foundation.dialog.DialogManager;
import com.adins.mss.foundation.location.LocationTrackingManager;
import com.adins.mss.foundation.security.storepreferences.ObscuredSharedPreferences;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.crashlytics.FirebaseCrashlytics;
import com.google.firebase.remoteconfig.FirebaseRemoteConfig;
import com.google.firebase.remoteconfig.FirebaseRemoteConfigSettings;
import com.services.RefreshToken;
import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import static com.adins.mss.base.login.DefaultLoginModel.LOGIN_PREFERENCES;
/**
* Created by Aditya Purwa on 1/6/2015.
* Activity for login.
*/
public abstract class LoginActivity extends AppCompatActivity implements ModeledActivity<DefaultLoginModel> {
public static Context instance;
private List<MobileDataFile> data;
public static List<MobileDataFile> activeData;
public static int currentidx = 0;
public static MobileDataFile metadata;
//variable penampung
static String link, savePath, message;
static ProgressDialog pDialog;
private final String DevModeEnable = "$ADIMOBILEDEVMODEON$";
private final String DevModeDisable = "$ADIMOBILEDEVMODEOFF$";
private final String txtDevModeOn = "ENABLE DEVELOPER MODE";
private final String txtDevModeOff = "DISABLE DEVELOPER MODE";
private final String txtDevModeOnId = "AKTIFKAN MODE PENGEMBANGAN";
private final String txtDevModeOffId = "MATIKAN MODE PENGEMBANGAN";
public LocationTrackingManager manager;
protected ImageView logo;
private LoginModel dataContext;
private ObscuredSharedPreferences loginPreferences;
//bong 1 apr 15 add menu to serverLinkActivity
private Menu mainMenu;
private LoginImpl loginImpl;
protected ThemeLoader themeLoader;
private Toolbar toolbar;
private View loginHeader;
private Button loginButton;
private Fragment locPermissionFragment;
protected boolean hasLogged;
public void downloadFiles() {
currentidx++;
int i = currentidx;
metadata = data.get(i);
message = "Downloading file " + (i + 1) + " out of " + data.size() + " files.";
link = data.get(i).getFile_url();
savePath = GlobalData.getSharedGlobalData().getSavePath();
if (link != null && !link.isEmpty()) {
DownloadParams parameters = new DownloadParams(savePath, instance, message, metadata);
FileDownloader downloader = new FileDownloader(instance);
downloader.execute(parameters);
}
}
public static void importFiles() {
currentidx++;
int i = currentidx;
metadata = activeData.get(i);
message = "Importing file " + (i + 1) + " out of " + activeData.size() + " files.";
ImportDbParams importParams = new ImportDbParams(instance, message, metadata);
ImportDbFromCsv importer = new ImportDbFromCsv();
importer.execute(importParams);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_item, menu);
mainMenu = menu;
return true;
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
Global.IS_DEV = false;
enableMenuItem(false);
if (Global.IS_DEV) {
enableMenuItem(true);
}
return true;
}
private void enableMenuItem(boolean enable) {
if (enable) {
mainMenu.findItem(R.id.menuItem).setVisible(true);
mainMenu.findItem(R.id.serverLink).setVisible(true);
mainMenu.findItem(R.id.devOption).setVisible(true);
} else {
mainMenu.findItem(R.id.menuItem).setVisible(false);
mainMenu.findItem(R.id.serverLink).setVisible(false);
mainMenu.findItem(R.id.devOption).setVisible(false);
}
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_UP && KeyEvent.KEYCODE_MENU == keyCode) {
if(Global.IS_DEV)
mainMenu.performIdentifierAction(R.id.menuItem, 0);
return true;
}
return super.onKeyUp(keyCode, event);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
String title = item.getTitle().toString();
if (getString(R.string.lblServerLinkId).equals(title)) {
startActivity(new Intent(LoginActivity.this, ServerLinkActivity.class));
}
if (getString(R.string.lblDevOption).equals(title)) {
startActivity(new Intent(LoginActivity.this, DeveloperOptionActivity.class));
}
return super.onOptionsItemSelected(item);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//delete downloaded update file
File file = new File(getApplicationContext().getFilesDir(), "app.apk");
if(file.exists()) {
boolean result = file.delete();
if(!result){
Toast.makeText(this, "Cannot delete downloaded update file", Toast.LENGTH_SHORT).show();
}
}
if(hasLogged && GlobalData.getSharedGlobalData().getUser() != null)
return;//dipatch if has logged
LocaleHelper.onCreate(getApplicationContext());
setContentView(R.layout.new_login_activity);
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) {
getWindow().getDecorView().setImportantForAutofill(
View.IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS);
}
logo = (ImageView) findViewById(R.id.logoMobile);
loginHeader = findViewById(R.id.loginHeader);
toolbar = (Toolbar) findViewById(R.id.toolbar);
toolbar.setTitle("");
setSupportActionBar(toolbar);
loginImpl = new LoginImpl(this);
initialize();
}
@Override
public void onResume() {
super.onResume();
//Check location permission whether to show educational UI about permission or not
if (Global.ENABLE_LOC_PERMISSION_UI) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_DENIED &&
checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_DENIED) {
showLocationPermissionInformativeUI();
} else {
Utility.checkPermissionGranted(LoginActivity.this);
}
} else {
if (PermissionChecker.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PermissionChecker.PERMISSION_DENIED &&
PermissionChecker.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PermissionChecker.PERMISSION_DENIED) {
showLocationPermissionInformativeUI();
} else {
Utility.checkPermissionGranted(LoginActivity.this);
}
}
} else {
Utility.checkPermissionGranted(LoginActivity.this);
}
ObscuredSharedPreferences sharedPreferences = ObscuredSharedPreferences.getPrefs(this,
LOGIN_PREFERENCES, Context.MODE_PRIVATE);
if (sharedPreferences.contains("is_expired") && sharedPreferences.getBoolean("is_expired", false)) {
sharedPreferences.edit().putBoolean("is_expired", false).apply();
FileSyncHelper.senderID = 0;
FileSyncHelper.startFileSync(this);
return;
}
if (null != loginImpl) { // Penjagaan ketika masuk kembali ke APP (kejadian null saat di collection)
if (loginImpl.isRooted()) {
DialogManager.showRootAlert(this, getApplicationContext());
}
if (loginImpl.checkPlayServices(this)) {
// Then we're good to go!
}
}
DialogManager.showTimeProviderAlert(this);
if (Helper.isDevEnabled(this) && GlobalData.getSharedGlobalData().isDevEnabled() && !GlobalData.getSharedGlobalData().isByPassDeveloper()) {
DialogManager.showTurnOffDevMode(this);
}
if(hasLogged)
return;
}
@Override
public void onDestroy() {
super.onDestroy();
//prevent from activity destroyed before dialog closed
if(dataContext != null){
DefaultLoginModel defaultLoginModel = (DefaultLoginModel)dataContext;
defaultLoginModel.closeProgress();
}
if(hasLogged)
return;
if (loginImpl.getLocationManager() != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
return;
} else {
loginImpl.removeUpdateLocation();
}
} else {
loginImpl.removeUpdateLocation();
}
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
}
@Override
protected void attachBaseContext(Context newBase) {
Context context = newBase;
Locale locale;
try{
locale = new Locale(LocaleHelper.getLanguage(newBase));
context = LocaleHelper.wrap(newBase, locale);
} catch (Exception e) {
locale = new Locale(Locale.getDefault().getLanguage());
context = LocaleHelper.wrap(newBase, locale);
} finally {
super.attachBaseContext(context);
}
}
private void initialize() {
PackageInfo pInfo;
try {
new RefreshToken(getBaseContext()).onTokenRefresh();
} catch (Exception e){
FireCrash.log(e);
}
try {
pInfo = getPackageManager().getPackageInfo(getPackageName(), 0);
Global.APP_VERSION = pInfo.versionName;
Global.BUILD_VERSION = pInfo.versionCode;
} catch (NameNotFoundException e) {
if (Global.IS_DEV)
e.printStackTrace();
}
TextView tvAppVersion = (TextView) findViewById(R.id.contentVersion);
String versioning = getString(R.string.app_name) + " v." + Global.APP_VERSION;
tvAppVersion.setText(versioning);
loginImpl.initializePreferences();
loginPreferences = loginImpl.getLoginPreferences();
setModel(getNewDefaultLoginModel(this));
attachEventListener();
String language = LocaleHelper.getLanguage(this);
LocaleHelper.setLocale(this,language);
TextView androidId = (TextView) findViewById(R.id.androidId);
TextView divider = (TextView) findViewById(R.id.divider);
final String android_id;
if(Build.VERSION.SDK_INT > 28){
android_id = Settings.Secure.getString(this.getContentResolver(), Settings.Secure.ANDROID_ID);
androidId.setVisibility(View.VISIBLE);
divider.setVisibility(View.VISIBLE);
}else {
android_id = "";
androidId.setVisibility(View.GONE);
divider.setVisibility(View.GONE);
}
androidId.setText(android_id);
androidId.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("Clip", android_id);
clipboard.setPrimaryClip(clip);
Toast.makeText(getApplicationContext(),getString(R.string.copied),Toast.LENGTH_SHORT).show();
}
});
}
protected abstract DefaultLoginModel getNewDefaultLoginModel(Context context);
private void attachEventListener() {
loginButton = (Button) findViewById(R.id.btnLogin);
final CheckBox checkRememberMe = (CheckBox) findViewById(R.id.checkRememberMe);
final EditText editUserId = (EditText) findViewById(R.id.txtUser);
final EditText editPassword = (EditText) findViewById(R.id.txtPassword);
editUserId.setText(loginPreferences.getString(DefaultLoginModel.LOGIN_PREFERENCES_USERNAME, getString(R.string.txtempty)));
editPassword.setText(loginPreferences.getString(DefaultLoginModel.LOGIN_PREFERENCES_PASSWORD, getString(R.string.txtempty)));
checkRememberMe.setChecked(loginPreferences.getBoolean(DefaultLoginModel.LOGIN_PREFERENCES_REMEMBER_ME, false));
if (checkRememberMe.isChecked()) {
getModel().setRememberMe(true);
}
getModel().setUsername(editUserId.getText().toString());
getModel().setPassword(editPassword.getText().toString());
loginButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (loginButton.getText().equals(txtDevModeOn) || loginButton.getText().equals(txtDevModeOnId)) {
ObscuredSharedPreferences.Editor sharedPrefEditor = loginImpl.getSharedPref().edit();
sharedPrefEditor.putBoolean("IS_DEV", true);
sharedPrefEditor.commit();
Global.IS_DEV = true;
runOnUiThread(new Runnable() {
@Override
public void run() {
enableMenuItem(true);
editPassword.setText("");
}
});
} else if (loginButton.getText().equals(txtDevModeOff) || loginButton.getText().equals(txtDevModeOffId)) {
ObscuredSharedPreferences.Editor sharedPrefEditor = loginImpl.getSharedPref().edit();
sharedPrefEditor.putBoolean("IS_DEV", false);
sharedPrefEditor.commit();
Global.IS_DEV = false;
runOnUiThread(new Runnable() {
@Override
public void run() {
enableMenuItem(false);
editPassword.setText("");
}
});
} else {
getModel().login();
}
}
});
editUserId.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
//EMPTY
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
getModel().setUsername(s.toString());
}
@Override
public void afterTextChanged(Editable s) {
//EMPTY
}
});
editPassword.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
//EMPTY
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
getModel().setPassword(s.toString());
}
@Override
public void afterTextChanged(Editable s) {
String devMode = editPassword.getText().toString().trim();
if (devMode != null && devMode.length() > 0 && devMode.equals(DevModeEnable) && loginImpl.isCan_access_developer_mode()) {
runOnUiThread(new Runnable() {
@Override
public void run() {
loginButton.setText(getString(R.string.enableDevMode));
}
});
} else if (devMode != null && devMode.length() > 0 && devMode.equals(DevModeDisable)) {
runOnUiThread(new Runnable() {
@Override
public void run() {
loginButton.setText(getString(R.string.disableDevMode));
}
});
} else {
runOnUiThread(new Runnable() {
@Override
public void run() {
loginButton.setText(getString(R.string.btnLogin));
}
});
}
}
});
checkRememberMe.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
getModel().setRememberMe(isChecked);
}
});
}
@Override
public DefaultLoginModel getModel() {
return (DefaultLoginModel) dataContext;
}
@Override
public void setModel(DefaultLoginModel model) {
dataContext = model;
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case Utility.REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS: {
if (Utility.checkPermissionResult(LoginActivity.this, permissions, grantResults)) {
loginImpl.bindLocationListener();
if (Global.ENABLE_LOC_PERMISSION_UI) {
getSupportFragmentManager().popBackStack();
}
}
}
break;
default: {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
}
int color;
protected void applyColorTheme(DynamicTheme dynamicTheme){//apply color set to views
String commonBgColor = ThemeUtility.getColorItemValue(dynamicTheme,"bg_solid");
String btnNormalColor = ThemeUtility.getColorItemValue(dynamicTheme,"btn_bg_normal");
String btnPressedColor = ThemeUtility.getColorItemValue(dynamicTheme,"btn_bg_pressed");
color = Color.parseColor(commonBgColor);
ThemeUtility.setToolbarColor(toolbar,color);
ThemeUtility.setStatusBarColor(this,color);
ThemeUtility.setViewBackground(loginHeader,color);
ThemeUtility.setViewBackground(loginButton,color);
//create color state list for button states
int[][] btnstates = new int[][] {
new int[] { android.R.attr.state_pressed}, // pressed
new int[] {} // normal
};
int[] btncolorlist = new int[]{
Color.parseColor(btnPressedColor),
Color.parseColor(btnNormalColor)
};
ColorStateList btnColorStateList = new ColorStateList(btnstates,btncolorlist);
ThemeUtility.setViewBackground(loginButton,btnColorStateList);
}
protected void fetchConfig() {
final FirebaseRemoteConfig remoteConfig = FirebaseRemoteConfig.getInstance();
FirebaseRemoteConfigSettings remoteConfigSettings = new FirebaseRemoteConfigSettings.Builder()
.build();
defaultConfig = new HashMap<>();
defaultConfig.put("cipher_unsupported_device", Global.SQLITE_CIPHER_UNSUPPORTED);
remoteConfig.setConfigSettingsAsync(remoteConfigSettings);
remoteConfig.setDefaultsAsync(defaultConfig);
remoteConfig.fetch().addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
if (!task.isComplete() && !task.isSuccessful())
return;
remoteConfig.activate();
GlobalData.getSharedGlobalData().setRemoteConfig(remoteConfig);
String sqliteCipherException = remoteConfig.getString("cipher_unsupported_device");
Global.IS_DBENCRYPT = (!sqliteCipherException.contains(Build.MODEL)) && Global.IS_DBENCRYPT;
}
});
GlobalData.getSharedGlobalData().setRemoteConfig(remoteConfig);
}
public void showLocationPermissionInformativeUI(){
locPermissionFragment = new LocationPermissionInformationFragment();
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.login_activity,locPermissionFragment)
.addToBackStack(null)
.commit();
}
@Override
public void onBackPressed() {
if (Global.ENABLE_LOC_PERMISSION_UI && null != locPermissionFragment && locPermissionFragment.isVisible()) {
Toast.makeText(this ,R.string.allow_all_permission, Toast.LENGTH_SHORT).show();
} else {
finish();
}
}
public Map<String, Object> defaultConfig;
}

View file

@ -0,0 +1,106 @@
package com.adins.mss.base;
import android.Manifest;
import android.content.pm.PackageManager;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Build;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import android.widget.AdapterView;
import com.adins.mss.base.commons.AppInfo;
import com.adins.mss.base.crashlytics.FireCrash;
import com.adins.mss.base.util.Utility;
import com.adins.mss.foundation.dialog.DialogManager;
/**
* Created by kusnendi.muhamad on 03/08/2017.
*/
public abstract class MssFragmentActivity extends AppCompatActivity implements LocationListener,
AdapterView.OnItemClickListener, AppInfo {
protected LocationManager mLocation;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
bindLocationListener();
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mLocation != null) {
mLocation.removeUpdates(this);
if (mLocation != null) {
mLocation = null;
}
}
}
@Override
public void onResume() {
super.onResume();
//Check GPS Location Setting
// DialogManager.showGPSAlert(this);
if (mLocation == null) {
bindLocationListener();
}
}
@Override
public void onLocationChanged(Location location) {
if (location != null)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (location.isFromMockProvider())
DialogManager.showMockDialog(this);
}
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
@Override
public void onProviderEnabled(String provider) {
DialogManager.closeGPSAlert();
}
@Override
public void onProviderDisabled(String provider) {
DialogManager.showGPSAlert(this);
}
@Override
public void checkAppVersion(NewMainActivity activity) {
}
public void bindLocationListener() {
mLocation = (LocationManager) getSystemService(LOCATION_SERVICE);
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
Utility.checkPermissionGranted(this);
return;
} else {
if (mLocation.getAllProviders().contains(LocationManager.GPS_PROVIDER))
mLocation.requestLocationUpdates(LocationManager.GPS_PROVIDER, 15 * 1000L, 10f, this);
}
} else {
if (mLocation.getAllProviders().contains(LocationManager.GPS_PROVIDER))
mLocation.requestLocationUpdates(LocationManager.GPS_PROVIDER, 15 * 1000L, 10f, this);
}
} catch (IllegalArgumentException e) {
// TODO: handle exception
} catch (Exception e) {
FireCrash.log(e);
}
}
}

View file

@ -0,0 +1,132 @@
package com.adins.mss.base;
import android.content.Intent;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import com.adins.mss.base.api.ChangePasswordApi;
import com.adins.mss.base.api.ChangePasswordApiCallback;
import com.adins.mss.base.crashlytics.FireCrash;
import com.adins.mss.base.login.DefaultLoginModel;
import com.adins.mss.base.models.ChangePasswordRequestModel;
import com.adins.mss.base.syncfile.FileSyncHelper;
import com.adins.mss.constant.Global;
import com.adins.mss.dao.User;
import com.adins.mss.foundation.db.dataaccess.UserDataAccess;
import com.google.firebase.analytics.FirebaseAnalytics;
public class NewChangePasswordActivity extends AppCompatActivity {
private EditText currentPassword;
private EditText newPassword;
private EditText retypePassword;
private Button changePassword;
private FirebaseAnalytics screenName;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
screenName = FirebaseAnalytics.getInstance(this);
setContentView(R.layout.new_change_password_activity);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
toolbar.setTitleTextColor(getResources().getColor(R.color.fontColorWhite));
toolbar.setTitle(getResources().getString(R.string.title_mn_resetpassword));
setSupportActionBar(toolbar);
String pwdExp = this.getIntent().getStringExtra(DefaultLoginModel.PWD_EXP);
try {
if (pwdExp.equals("1")) {
Toast.makeText(NewChangePasswordActivity.this, getString(R.string.password_expired),
Toast.LENGTH_LONG).show();
}
} catch (Exception e) {
FireCrash.log(e);
// TODO: handle exception
}
currentPassword = (EditText) findViewById(R.id.currentPassword);
newPassword = (EditText) findViewById(R.id.newPassword);
retypePassword = (EditText) findViewById(R.id.retypePassword);
changePassword = (Button) findViewById(R.id.changePassword);
changePassword.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (newPassword.getText() == null ||
newPassword.getText().toString().equals("")) {
newPassword.setError(getString(R.string.new_password_mandatory));
return;
}
if (!newPassword.getText().toString().equals(retypePassword.getText().toString())) {
Toast.makeText(NewChangePasswordActivity.this, getString(R.string.new_password_equal), Toast.LENGTH_SHORT).show();
return;
}
ChangePasswordRequestModel request = new ChangePasswordRequestModel();
request.setUuid_user(GlobalData.getSharedGlobalData().getUser().getUuid_user());
request.setOld_password(currentPassword.getText().toString());
request.setNew_password(newPassword.getText().toString());
request.setAudit(GlobalData.getSharedGlobalData().getAuditData());
ChangePasswordApi api = new ChangePasswordApi(NewChangePasswordActivity.this);
api.setCallback(new ChangePasswordApiCallback() {
@Override
public void onFailed(String message) {
try {
Toast.makeText(NewChangePasswordActivity.this, getString(R.string.warning_capital) + ": " + message, Toast.LENGTH_LONG).show();
} catch (Exception e) {
FireCrash.log(e);
Toast.makeText(NewChangePasswordActivity.this, "Change Password Error", Toast.LENGTH_LONG).show();
}
}
@Override
public void onSuccess() {
Toast.makeText(NewChangePasswordActivity.this, getString(R.string.password_changed), Toast.LENGTH_LONG).show();
User user = GlobalData.getSharedGlobalData().getUser();
user.setPassword(newPassword.getText().toString());
UserDataAccess.addOrReplace(NewChangePasswordActivity.this, user);
// goToSynchronize();
Global.syncIntent = null;
finish();
clear();
}
});
api.execute(request);
}
});
}
@Override
protected void onResume() {
super.onResume();
screenName.setCurrentScreen(this, getString(R.string.screen_name_change_password), null);
}
private void clear() {
currentPassword.setText("");
newPassword.setText("");
retypePassword.setText("");
}
@Override
public void onBackPressed() {
//
}
private void goToSynchronize() {
if (Global.syncIntent != null) {
Intent syncIntent = Global.syncIntent;
startActivity(syncIntent);
}
}
}

View file

@ -0,0 +1,590 @@
package com.adins.mss.base;
import android.app.Activity;
import android.app.ProgressDialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Vibrator;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import android.util.AttributeSet;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.adins.mss.base.checkin.CheckInManager;
import com.adins.mss.base.crashlytics.FireCrash;
import com.adins.mss.base.dynamicform.Constant;
import com.adins.mss.base.util.EventBusHelper;
import com.adins.mss.base.util.LocaleHelper;
import com.adins.mss.constant.Global;
import com.adins.mss.dao.PrintDate;
import com.adins.mss.dao.PrintResult;
import com.adins.mss.dao.ReceiptVoucher;
import com.adins.mss.dao.TaskH;
import com.adins.mss.foundation.UserHelp.UserHelp;
import com.adins.mss.foundation.db.dataaccess.PrintDateDataAccess;
import com.adins.mss.foundation.db.dataaccess.PrintResultDataAccess;
import com.adins.mss.foundation.db.dataaccess.ReceiptVoucherDataAccess;
import com.adins.mss.foundation.db.dataaccess.TaskHDataAccess;
import com.adins.mss.foundation.dialog.NiftyDialogBuilder;
import com.adins.mss.foundation.location.UpdateMenuIcon;
import com.adins.mss.foundation.print.AB200MPrintManager;
import com.adins.mss.foundation.print.AbstractPrintManager;
import com.adins.mss.foundation.print.PrintManagerListener;
import com.adins.mss.foundation.print.rv.InputRVNumberActivity;
import com.adins.mss.foundation.print.rv.syncs.SyncRVRequest;
import com.adins.mss.foundation.print.rv.syncs.SyncRVResponse;
import com.adins.mss.foundation.print.rv.syncs.SyncRVTask;
import com.adins.mss.foundation.print.rv.syncs.SyncRvListener;
import com.androidquery.AQuery;
import org.acra.ACRA;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import zj.com.cn.bluetooth.sdk.DeviceListActivity;
import static com.adins.mss.constant.Global.SHOW_USERHELP_DELAY_DEFAULT;
/**
* Created by winy.firdasari on 30/01/2015.
*/
public class PrintActivity extends AppCompatActivity implements View.OnClickListener, PrintManagerListener {
public static final String TASKS = "PrintActivitiy.TASKS";
public static final String UUID_TASKH = "uuid_taskh";
public static final String COUNT_RV_NUMBER = "count_rv_number";
public static final String SOURCE_TASK = "source";
public static final int FROM_ACTIVITY_RV_NUMBER = 1;
// Intent request codes
private static final int REQUEST_CONNECT_DEVICE = 1;
protected static List<PrintResult> listOfPrint;
private AbstractPrintManager printManager;
private TextView lblPrinterStatus;
private Button btnConnect;
private Button btnPrint;
private String uuidTaskH;
private boolean isPrintDeposit;
private AQuery query;
private TaskH taskH;
private String printerName = "-";
private String source;
private BluetoothAdapter mBluetoothAdapter;
public static void setListOfPrint(List<PrintResult> listOfPrint) {
PrintActivity.listOfPrint = listOfPrint;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.print_activity);
Toolbar toolbar = (Toolbar) findViewById(com.adins.mss.base.R.id.toolbar);
toolbar.setTitle(getString(R.string.title_print));
toolbar.setTitleTextColor(getResources().getColor(com.adins.mss.base.R.color.fontColorWhite));
setSupportActionBar(toolbar);
ACRA.getErrorReporter().putCustomData("LAST_CLASS_ACCESSED", getClass().getSimpleName());
query = new AQuery(this);
lblPrinterStatus = (TextView) findViewById(R.id.lblPrinterStatus);
btnConnect = (Button) findViewById(R.id.btnConnect);
btnPrint = (Button) findViewById(R.id.btnPrint);
if (btnConnect != null) {
btnConnect.setOnClickListener(this);
btnPrint.setOnClickListener(this);
}
//bong 8 may 15 - get print result from db
String taskId = this.getIntent().getStringExtra("taskId");
isPrintDeposit = getIntent().getBooleanExtra("isPrintDeposit", false);
source = this.getIntent().getStringExtra("source");
taskH = TaskHDataAccess.getOneTaskHeader(getApplicationContext(), taskId);
List<PrintResult> results;
uuidTaskH = taskH != null ? taskH.getUuid_task_h() : taskId;
results = PrintResultDataAccess.getAll(getApplicationContext(), uuidTaskH);
if (results == null || results.isEmpty()) {
Toast.makeText(this, getString(R.string.printResultNull), Toast.LENGTH_SHORT).show();
this.finish();
return;
}
printManager = new AB200MPrintManager(this, results);
Constant.setListOfPrintItem(results);
updateUI(printManager.isConnected());
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (!mBluetoothAdapter.isEnabled()) {
Toast.makeText(this, getString(R.string.bt_off), Toast.LENGTH_SHORT).show();
}
}
@Override
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
parent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
if(!UserHelp.isActive)
UserHelp.showAllUserHelp(PrintActivity.this,
PrintActivity.this.getClass().getSimpleName());
}
}, SHOW_USERHELP_DELAY_DEFAULT);
}
});
return super.onCreateView(parent, name, context, attrs);
}
@Override
protected void attachBaseContext(Context newBase) {
Context context = newBase;
Locale locale;
try{
locale = new Locale(GlobalData.getSharedGlobalData().getLocale());
context = LocaleHelper.wrap(newBase, locale);
} catch (Exception e) {
locale = new Locale(LocaleHelper.ENGLSIH);
context = LocaleHelper.wrap(newBase, locale);
} finally {
super.attachBaseContext(context);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (printManager != null) {
if (printManager.isConnected()) {
try {
printManager.disconnect();
} catch (Exception ex) {
ex.printStackTrace();
}
}
printManager.releaseResources();
}
query = null;
printManager = null;
setListOfPrint(null);
}
@Override
public void onBackPressed() {
if (!query.id(R.id.btnConnect).getButton().isEnabled() ||
!query.id(R.id.btnPrint).getButton().isEnabled()) {
Toast.makeText(this, "cannot close, please wait...", Toast.LENGTH_SHORT).show();
} else {
super.onBackPressed();
}
}
protected void updateUI(boolean isConnected) {
if (btnConnect != null) {
if (isConnected) {
lblPrinterStatus.setText(getString(R.string.connected));
btnConnect.setText(getString(R.string.mnDisconnect));
btnPrint.setVisibility(View.VISIBLE);
} else {
lblPrinterStatus.setText(getString(R.string.not_connected));
btnConnect.setText(getString(R.string.mnConnect));
btnPrint.setVisibility(View.INVISIBLE);
}
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater menuInflater = getMenuInflater();
menuInflater.inflate(com.adins.mss.base.R.menu.main_menu, menu);
mainMenu = menu;
return true;
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
updateMenuIcon(Global.isGPS);
if(Global.ENABLE_USER_HELP &&
(Global.userHelpGuide.get(PrintActivity.this.getClass().getSimpleName())!=null) ||
Global.userHelpDummyGuide.get(PrintActivity.this.getClass().getSimpleName()) != null){
menu.findItem(com.adins.mss.base.R.id.mnGuide).setVisible(true);
}
return super.onPrepareOptionsMenu(menu);
}
private static Menu mainMenu;
public static void updateMenuIcon(boolean isGPS) {
UpdateMenuIcon uItem = new UpdateMenuIcon();
uItem.updateGPSIcon(mainMenu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == com.adins.mss.base.R.id.mnGPS && Global.LTM != null) {
if (Global.LTM.getIsConnected()) {
Global.LTM.removeLocationListener();
Global.LTM.connectLocationClient();
} else {
CheckInManager.startGPSTracking(getApplicationContext());
}
Animation a = AnimationUtils.loadAnimation(this, com.adins.mss.base.R.anim.gps_rotate);
findViewById(com.adins.mss.base.R.id.mnGPS).startAnimation(a);
}
if(id==R.id.mnGuide && !Global.BACKPRESS_RESTRICTION){
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
UserHelp.showAllUserHelp(PrintActivity.this, PrintActivity.this.getClass().getSimpleName());
}
}, SHOW_USERHELP_DELAY_DEFAULT);
}
return super.onOptionsItemSelected(item);
}
protected void connect() {
try {
printManager.connect();
} catch (Exception e) {
FireCrash.log(e);
e.printStackTrace();
Toast.makeText(this, getResources().getString(R.string.failed_to_connect), Toast.LENGTH_SHORT).show();
}
}
protected void connect(BluetoothDevice device) {
try {
printManager.connect();
} catch (Exception e) {
FireCrash.log(e);
e.printStackTrace();
Toast.makeText(this, getResources().getString(R.string.failed_to_connect), Toast.LENGTH_SHORT).show();
}
}
protected void disconnect() {
try {
printManager.disconnect();
} catch (Exception e) {
FireCrash.log(e);
e.printStackTrace();
}
}
@Override
public void onClick(View v) {
if (v == btnConnect) {
if (!btnConnect.getText().equals(getResources().getString(R.string.connecting))) {
if (printManager.isConnected()) {
disconnect();
} else {
Intent serverIntent = new Intent(PrintActivity.this, DeviceListActivity.class);
startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE);
}
}
} else {
new PrintAsync().execute();
}
}
@Override
public void onConnected(String deviceName) {
Vibrator v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
v.vibrate(400);
query.id(R.id.tv_device_name).text(deviceName);
query.id(R.id.btnConnect).enabled(true);
updateUI(printManager.isConnected());
Toast.makeText(this, getString(R.string.connected), Toast.LENGTH_SHORT).show();
}
@Override
public void onConnectFailed() {
onDisconnect();
showErrorDialog(ErrorType.PRINT_FAILED);
}
@Override
public void onConnecting() {
query.id(R.id.btnConnect).enabled(false).text(R.string.connecting);
}
@Override
public void onDisconnect() {
query.id(R.id.tv_device_name).text("-");
printerName = "-";
query.id(R.id.btnConnect).enabled(true);
updateUI(printManager.isConnected());
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == Activity.RESULT_OK && data != null) {
// Get the device MAC address
String address = data.getExtras().getString(
DeviceListActivity.EXTRA_DEVICE_ADDRESS);
// Get the BLuetoothDevice object
if (BluetoothAdapter.checkBluetoothAddress(address)) {
BluetoothDevice device = mBluetoothAdapter
.getRemoteDevice(address);
// Attempt to connect to the device
connect(device);
} else {
showErrorDialog(ErrorType.PRINT_FAILED);
}
} else {
finish();
}
}
private void syncRvNumber() {
String uuidUser = GlobalData.getSharedGlobalData().getUser().getUuid_user();
SyncRVRequest request = new SyncRVRequest();
request.setLastDtmCrt(ReceiptVoucherDataAccess.getLastDate(this, uuidUser));
SyncRVTask task = new SyncRVTask(this, request, new SyncRvListener() {
ProgressDialog dialog;
@Override
public void onProgress() {
dialog = ProgressDialog.show(PrintActivity.this, "Sync RV Number", getString(R.string.please_wait), true, false);
}
@Override
public void onError(SyncRVResponse response) {
if (dialog != null && dialog.isShowing()) {
dialog.dismiss();
}
if (response.getErrorMessage() != null) {
showErrorDialog(ErrorType.SYNC_RV_FAILED);
}
}
@Override
public void onSuccess(SyncRVResponse response) {
if (dialog != null && dialog.isShowing()) {
dialog.dismiss();
}
List<ReceiptVoucher> rvNumbers = response.getListReceiptVoucher();
if (rvNumbers != null && !rvNumbers.isEmpty()) {
try {
ReceiptVoucherDataAccess.addNewReceiptVoucher(PrintActivity.this,
GlobalData.getSharedGlobalData().getUser().getUuid_user(),
rvNumbers);
} catch (Exception e) {
FireCrash.log(e);
e.printStackTrace();
ACRA.getErrorReporter().putCustomData("errorRV", e.getMessage());
ACRA.getErrorReporter().handleSilentException(new Exception("Error: Insert RV Error. " + e.getMessage()));
Toast.makeText(PrintActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
openInputRVActivity();
}
});
task.execute();
}
private void openInputRVActivity() {
Intent intent = new Intent(PrintActivity.this, InputRVNumberActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.putExtra(UUID_TASKH, uuidTaskH);
intent.putExtra(SOURCE_TASK, source);
startActivityForResult(intent, FROM_ACTIVITY_RV_NUMBER);
}
private void showErrorDialog(ErrorType errorType) {
if (isPrintDeposit) return;
if (taskH != null && taskH.getPrint_count() != null && taskH.getPrint_count() > 0) {
return;
}
final NiftyDialogBuilder dialog = NiftyDialogBuilder.getInstance(this)
.isCancelable(true)
.isCancelableOnTouchOutside(false);
switch (errorType) {
case PRINT_FAILED:
dialog.withTitle(getString(R.string.connect_failed))
.withMessage(R.string.reconnect_message)
.withButton1Text(getString(R.string.try_again))
.setButton1Click(new View.OnClickListener() {
@Override
public void onClick(View v) {
dialog.dismiss();
Intent serverIntent = new Intent(PrintActivity.this, DeviceListActivity.class);
startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE);
}
})
.withButton2Text(getString(R.string.input_rv))
.setButton2Click(new View.OnClickListener() {
@Override
public void onClick(View v) {
dialog.dismiss();
syncRvNumber();
}
});
break;
case SYNC_RV_FAILED:
boolean isRvNumberEmpty = ReceiptVoucherDataAccess.getByStatus(this,
GlobalData.getSharedGlobalData().getUser().getUuid_user(),
ReceiptVoucherDataAccess.STATUS_NEW).isEmpty();
dialog.withTitle(getString(R.string.error_capital))
.withMessage(R.string.sync_rv_failed)
.withButton1Text(getString(R.string.try_again))
.setButton1Click(new View.OnClickListener() {
@Override
public void onClick(View v) {
dialog.dismiss();
syncRvNumber();
}
});
if (!isRvNumberEmpty) {
dialog.withButton2Text(getString(R.string.btnNext))
.setButton2Click(new View.OnClickListener() {
@Override
public void onClick(View v) {
dialog.dismiss();
openInputRVActivity();
}
});
} else {
dialog.withButton2Text(getString(R.string.btnClose))
.setButton2Click(new View.OnClickListener() {
@Override
public void onClick(View v) {
dialog.dismiss();
}
});
}
break;
}
dialog.show();
}
public enum ErrorType {
PRINT_FAILED,
SYNC_RV_FAILED
}
public class PrintAsync extends AsyncTask<Void, Void, Boolean> {
public PrintAsync() {
//EMPTY
}
@Override
protected void onPreExecute() {
super.onPreExecute();
query.id(R.id.btnPrint).enabled(false).text("Printing...");
query.id(R.id.btnConnect).enabled(false);
}
@Override
protected Boolean doInBackground(Void... params) {
try {
if (printManager.isConnected()) {
if (printerName.toLowerCase().contains("sato")) {
return printManager.printSato();
} else {
return printManager.print();
}
}
} catch (Exception e) {
FireCrash.log(e);
e.printStackTrace();
}
return false;
}
@Override
protected void onPostExecute(final Boolean aBoolean) {
super.onPostExecute(aBoolean);
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
if (aBoolean && printManager.isConnected() && printManager.isPrinterConnected()) {
if (GlobalData.getSharedGlobalData().getApplication() == null) {
NewMainActivity.InitializeGlobalDataIfError(getApplicationContext());
}
if (GlobalData.getSharedGlobalData().getApplication().equals("MC") && !isPrintDeposit) {
if (taskH != null) {
int count = taskH.getPrint_count() == null ? 0 : taskH.getPrint_count();
taskH.setPrint_count(count + 1);
TaskHDataAccess.addOrReplace(PrintActivity.this, taskH);
}
PrintDate taskSubmit = new PrintDate();
taskSubmit.setDtm_print(new Date());
taskSubmit.setUuid_task_h(uuidTaskH);
PrintDateDataAccess.addOrReplace(PrintActivity.this, taskSubmit);
EventBusHelper.post(taskSubmit);
}
query.id(R.id.btnPrint).enabled(true).text(R.string.print);
query.id(R.id.btnConnect).enabled(true);
return;
}
query.id(R.id.btnPrint).enabled(true).text(R.string.print);
query.id(R.id.btnConnect).enabled(true);
Toast.makeText(PrintActivity.this, "Device is not connected to the printer", Toast.LENGTH_SHORT).show();
try {
showErrorDialog(ErrorType.PRINT_FAILED);
printManager.disconnect();
} catch (Exception e) {
FireCrash.log(e);
e.printStackTrace();
}
}
}, 2000);
}
}
}

View file

@ -0,0 +1,91 @@
package com.adins.mss.base;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.os.AsyncTask;
import com.adins.mss.base.crashlytics.FireCrash;
import com.adins.mss.constant.Global;
import com.adins.mss.dao.PrintResult;
import com.adins.mss.dao.TaskH;
import com.adins.mss.foundation.db.dataaccess.PrintResultDataAccess;
import com.adins.mss.foundation.db.dataaccess.TaskHDataAccess;
import com.adins.mss.foundation.dialog.DialogManager;
import java.util.List;
/**
* Created by winy.firdasari on 30/01/2015.
*/
public class PrintTask extends AsyncTask<String, Void, List<PrintResult>> {
private ProgressDialog progressDialog;
private String errMessage = null;
private Activity activity;
private String messageWait;
private String messageEmpty;
private String taskId;
//inijiniunnij dasfasfs
public PrintTask(Activity activity) {
this.activity = activity;
this.messageWait = activity.getString(R.string.progressWait);
this.messageEmpty = activity.getString(R.string.msgNoPrintItem);
}
@Override
protected void onPreExecute() {
progressDialog = ProgressDialog.show(this.activity, "", this.messageWait, true);
}
@Override
protected List<PrintResult> doInBackground(String... params) {
List<PrintResult> result = null;
try {
String taskId = params[0];
this.taskId = taskId;
//bong 7 may 15 - asumsi data print result di lokal sudah siap untuk di-print
TaskH taskH = TaskHDataAccess.getOneTaskHeader(activity, taskId);
result = PrintResultDataAccess.getAll(activity, taskH.getUuid_task_h());
// FormManager formManager = new FormManager();
// result = formManager.getReadyPrintItem(activity, taskId);
} catch (Exception ex) {
if (Global.IS_DEV)
ex.printStackTrace();
try {
progressDialog.dismiss();
} catch (Exception e) {
FireCrash.log(e);
}
errMessage = ex.getMessage();
}
return result;
}
@Override
protected void onPostExecute(List<PrintResult> result) {
if (progressDialog.isShowing()) {
try {
progressDialog.dismiss();
} catch (Exception e) {
FireCrash.log(e);
}
}
if (errMessage != null) {
DialogManager.showAlert(activity, DialogManager.TYPE_ERROR, errMessage, activity.getString(R.string.test_label));
} else if (result == null || result.size() == 0) {
DialogManager.showAlert(activity, DialogManager.TYPE_INFO, messageEmpty, activity.getString(R.string.test_label));
} else {
PrintActivity.setListOfPrint(result);
Intent i = new Intent(this.activity, PrintActivity.class);
this.activity.startActivity(i);
}
}
}

View file

@ -0,0 +1,105 @@
package com.adins.mss.base;
import android.content.Context;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import com.adins.mss.base.crashlytics.FireCrash;
import com.adins.mss.base.util.LocaleHelper;
import com.adins.mss.foundation.dialog.DialogManager;
import com.adins.mss.foundation.security.storepreferences.ObscuredSharedPreferences;
import com.google.firebase.analytics.FirebaseAnalytics;
import org.acra.ACRA;
import java.util.Locale;
public class ServerLinkActivity extends AppCompatActivity implements OnClickListener {
private FirebaseAnalytics screenName;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.new_server_link_activity);
screenName = FirebaseAnalytics.getInstance(this);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
toolbar.setTitleTextColor(getResources().getColor(R.color.fontColorWhite));
setSupportActionBar(toolbar);
ACRA.getErrorReporter().putCustomData("LAST_CLASS_ACCESSED", getClass().getSimpleName());
EditText txtServerLink = (EditText) findViewById(R.id.txtServerLink);
txtServerLink.setText(GlobalData.getSharedGlobalData().getUrlMain());
Button btnLogin = (Button) findViewById(R.id.btnSaveLink);
btnLogin.setOnClickListener(this);
}
@Override
public void onBackPressed() {
ServerLinkActivity.this.finish();
}
@Override
protected void onResume() {
super.onResume();
//Set Firebase screen name
screenName.setCurrentScreen(this, getString(R.string.screen_name_server_link), null);
try {
DialogManager.showGPSAlert(this);
} catch (Exception e) {
FireCrash.log(e);
}
}
@Override
protected void attachBaseContext(Context newBase) {
Context context = newBase;
Locale locale;
try{
locale = new Locale(GlobalData.getSharedGlobalData().getLocale());
context = LocaleHelper.wrap(newBase, locale);
} catch (Exception e) {
locale = new Locale(LocaleHelper.ENGLSIH);
context = LocaleHelper.wrap(newBase, locale);
} finally {
super.attachBaseContext(context);
}
}
@Override
public void onClick(View v) {
Button btn = (Button) v;
int id = btn.getId();
if (R.id.btnSaveLink == id) {
EditText txtServerLink = (EditText) findViewById(R.id.txtServerLink);
String serverLink = txtServerLink.getText().toString().trim();
GlobalData.getSharedGlobalData().setUrlMain(serverLink);
GlobalData.getSharedGlobalData().reloadUrl(this.getApplicationContext());
//Gigin ~ set URL Header Di Global.URL_HEADER tanpa "m/"
ObscuredSharedPreferences sharedPref = ObscuredSharedPreferences.getPrefs(getApplicationContext(),
"GlobalData", Context.MODE_PRIVATE);
ObscuredSharedPreferences.Editor sharedPrefEditor = sharedPref.edit();
sharedPrefEditor.putString("URL_HEADER", serverLink);
sharedPrefEditor.commit();
ServerLinkActivity.this.finish();
}
}
}

View file

@ -0,0 +1,109 @@
package com.adins.mss.base;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import com.adins.mss.base.dynamictheme.DynamicTheme;
import com.adins.mss.base.dynamictheme.ThemeLoader;
import com.adins.mss.base.synchronize.ProgressListener;
import com.adins.mss.base.synchronize.SynchronizeView;
import com.adins.mss.base.util.LocaleHelper;
import org.acra.ACRA;
import java.util.Locale;
/**
* Created by winy.firdasari on 05/01/2015.
*/
public abstract class SynchronizeActivity extends AppCompatActivity implements ProgressListener, ThemeLoader.ColorSetLoaderCallback {
public Activity activity = this;
private SynchronizeView views;
public SynchronizeActivity() {
}
@Override
public void onBackPressed() {
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.new_synchronize_activity);
ACRA.getErrorReporter().putCustomData("LAST_CLASS_ACCESSED", getClass().getSimpleName());
views = new SynchronizeView(activity, getIntentMainMenu(), this);
loadColorSet();
}
@Override
protected void attachBaseContext(Context newBase) {
Context context = newBase;
Locale locale;
try{
locale = new Locale(GlobalData.getSharedGlobalData().getLocale());
context = LocaleHelper.wrap(newBase, locale);
} catch (Exception e) {
locale = new Locale(LocaleHelper.ENGLSIH);
context = LocaleHelper.wrap(newBase, locale);
} finally {
super.attachBaseContext(context);
}
}
private void loadColorSet(){
ThemeLoader themeLoader = new ThemeLoader(this);
themeLoader.loadSavedColorSet(this);
}
protected abstract Intent getIntentMainMenu();
@Override
public void onUpdatedValue(float progress) {
views.progressUpdated(progress);
}
@Override
public void onSyncScheme(boolean value) {
views.isSyncScheme = value;
}
@Override
public void onSyncQuestion(boolean value) {
views.isSyncQuestionSet = value;
}
@Override
public void onSyncLookup(boolean value) {
views.isSyncLookup = value;
}
@Override
public void onSyncHoliday(boolean value) {
views.isSyncHoliday = value;
}
@Override
public void onSyncPaymentChannel(boolean value) {
views.isSyncPaymentChannel = value;
}
@Override
public void onHasLoaded(DynamicTheme dynamicTheme) {
views.initialize();
if(dynamicTheme != null && !dynamicTheme.getThemeItemList().isEmpty())
views.applyColorTheme(dynamicTheme);
views.publish();
}
@Override
public void onHasLoaded(DynamicTheme dynamicTheme, boolean needUpdate) {
//EMPTY
}
}

View file

@ -0,0 +1,65 @@
package com.adins.mss.base.about;
import android.content.Context;
import android.os.Build;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import com.adins.mss.base.R;
import com.mikepenz.aboutlibraries.entity.Library;
import java.util.ArrayList;
import java.util.List;
/**
* @author gigin.ginanjar
*/
public class AboutArrayAdapter extends ArrayAdapter<Library> {
private Context mContext;
private List<Library> libraries;
/**
* Inisialisasi About Array Adapter
*
* @param context - Context
* @param libraries - List of Libary
*/
public AboutArrayAdapter(Context context, List<Library> libraries) {
super(context, R.layout.new_about_library_log, libraries);
this.mContext = context;
if (libraries != null)
this.libraries = libraries;
else
this.libraries = new ArrayList<Library>();
}
@Override
public int getCount() {
return libraries.size();
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.new_about_library_log, parent, false);
}
((TextView) convertView.findViewById(R.id.libraryname)).setText(libraries.get(position).getLibraryName());
((TextView) convertView.findViewById(R.id.librarycreator)).setText(libraries.get(position).getAuthor());
((TextView) convertView.findViewById(R.id.libraryversion)).setText(libraries.get(position).getLibraryVersion());
// ((TextView)convertView.findViewById(R.id.librarylicense)).setText(libraries.get(position).getLicense().getLicenseName());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
((TextView) convertView.findViewById(R.id.description)).setText(Html.fromHtml(libraries.get(position).getLibraryDescription(), Html.FROM_HTML_MODE_LEGACY));
} else {
((TextView) convertView.findViewById(R.id.description)).setText(Html.fromHtml(libraries.get(position).getLibraryDescription()));
}
return convertView;
}
}

View file

@ -0,0 +1,411 @@
package com.adins.mss.base.about.activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.core.content.FileProvider;
import androidx.viewpager.widget.ViewPager;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import com.adins.mss.base.GlobalData;
import com.adins.mss.base.R;
import com.adins.mss.base.about.AboutArrayAdapter;
import com.adins.mss.base.about.activity.JsonVersionResponse.ListValue;
import com.adins.mss.base.crashlytics.FireCrash;
import com.adins.mss.base.update.DownloadUpdate;
import com.adins.mss.base.util.GsonHelper;
import com.adins.mss.base.util.Utility;
import com.adins.mss.constant.Global;
import com.adins.mss.foundation.UserHelp.UserHelp;
import com.adins.mss.foundation.db.dataaccess.GeneralParameterDataAccess;
import com.adins.mss.foundation.dialog.NiftyDialogBuilder;
import com.adins.mss.foundation.formatter.Tool;
import com.adins.mss.foundation.http.HttpConnectionResult;
import com.adins.mss.foundation.http.HttpCryptedConnection;
import com.adins.mss.foundation.http.MssRequestType;
import com.androidquery.AQuery;
import com.github.jjobes.slidedatetimepicker.SlidingTabLayout;
import com.google.firebase.analytics.FirebaseAnalytics;
import com.google.firebase.perf.FirebasePerformance;
import com.google.firebase.perf.metrics.HttpMetric;
import com.mikepenz.aboutlibraries.Libs;
import com.mikepenz.aboutlibraries.entity.Library;
import org.acra.ACRA;
import java.io.File;
import java.util.ArrayList;
import static com.adins.mss.constant.Global.SHOW_USERHELP_DELAY_DEFAULT;
/**
* @author gigin.ginanjar
*/
public class AboutActivity extends Fragment {
private static ArrayList<Library> library;
private static int flag;
public Libs libs;
AQuery query;
TextView appDesc;
TextView appName;
ListView listAbout;
private SlidingTabLayout mSlidingTabLayout;
private ViewPager mViewPager;
private View view;
private FirebaseAnalytics screenName;
/**
* @param changeLog
* @param flag
*/
public static void setChangeLog(ArrayList<Library> changeLog, int flag) {
library = new ArrayList<>();
library = changeLog;
AboutActivity.flag = flag;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
screenName = FirebaseAnalytics.getInstance(getActivity());
if (view != null) {
ViewGroup parent = (ViewGroup) view.getParent();
if (parent != null)
parent.removeView(view);
}
try {
view = inflater.inflate(R.layout.new_about_info_tab, container, false);
} catch (Exception e) {
FireCrash.log(e);
}
return view;
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ACRA.getErrorReporter().putCustomData("LAST_CLASS_ACCESSED", getClass().getSimpleName());
query = new AQuery(getActivity());
libs = new Libs(getActivity(), Libs.toStringArray(R.string.class.getFields()));
String app_description = "";
String app_name = "";
Button btnUpdate = (Button) view.findViewById(R.id.btnCheckUpdate);
btnUpdate.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
cekVersion();
}
});
//for LeadIn
if (flag == 0) {
app_description = libs.getInternLibraries().get(0).getLibraryDescription();
app_name = libs.getInternLibraries().get(0).getLibraryName();
} else if (flag == 1) {
app_description = libs.getInternLibraries().get(1).getLibraryDescription();
app_name = libs.getInternLibraries().get(1).getLibraryName();
} else if (flag == 2) {
app_description = libs.getInternLibraries().get(2).getLibraryDescription();
app_name = libs.getInternLibraries().get(2).getLibraryName();
}
query.id(R.id.appDesc).text(app_description);
PackageInfo pInfo;
try {
pInfo = getActivity().getPackageManager().getPackageInfo(getActivity().getPackageName(), 0);
app_name = pInfo.versionName;
Global.APP_VERSION = pInfo.versionName;
Global.BUILD_VERSION = pInfo.versionCode;
} catch (NameNotFoundException e) {
if (Global.IS_DEV)
e.printStackTrace();
}
try {
app_name = library.get(0).getLibraryName() + " v." + Global.APP_VERSION;
} catch (Exception e) {
FireCrash.log(e);
try {
pInfo = getActivity().getPackageManager().getPackageInfo(getActivity().getPackageName(), 0);
app_name = pInfo.versionName;
Global.APP_VERSION = pInfo.versionName;
} catch (NameNotFoundException e1) {
if (Global.IS_DEV)
e1.printStackTrace();
}
}
query.id(R.id.lblApp).text(app_name);
AboutArrayAdapter adapter = new AboutArrayAdapter(getContext(), library);
query.id(android.R.id.list).adapter(adapter);
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
UserHelp.showAllUserHelp(AboutActivity.this.getActivity(),AboutActivity.this.getClass().getSimpleName());
}
}, SHOW_USERHELP_DELAY_DEFAULT);
}
@Override
public void onResume() {
super.onResume();
//Set Firebase screen name
screenName.setCurrentScreen(getActivity(), getString(R.string.screen_name_about), null);
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
setHasOptionsMenu(true);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if(item.getItemId() == R.id.mnGuide && !Global.BACKPRESS_RESTRICTION){
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
UserHelp.showAllUserHelp(getParentFragment().getActivity(), getParentFragment().getClass().getSimpleName());
}
}, SHOW_USERHELP_DELAY_DEFAULT);
}
return super.onOptionsItemSelected(item);
}
public void checkForUpdate(View view) {
cekVersion();
}
public void cekVersion() {
new AsyncTask<Void, Void, JsonVersionResponse>() {
String taskId = null;
private ProgressDialog progressDialog;
private String errMessage = null;
@Override
protected void onPreExecute() {
progressDialog = ProgressDialog.show(getActivity(), "", getString(R.string.progressWait), true);
}
@Override
protected JsonVersionResponse doInBackground(Void... params) {
JsonVersionResponse result = null;
if (Tool.isInternetconnected(getActivity())) {
MssRequestType request = new MssRequestType();
request.setAudit(GlobalData.getSharedGlobalData().getAuditData());
String json = GsonHelper.toJson(request);
String url = GlobalData.getSharedGlobalData().getURL_CHECK_UPDATE();
boolean encrypt = GlobalData.getSharedGlobalData().isEncrypt();
boolean decrypt = GlobalData.getSharedGlobalData().isDecrypt();
HttpCryptedConnection httpConn = new HttpCryptedConnection(getActivity(), encrypt, decrypt);
HttpConnectionResult serverResult = null;
//Firebase Performance Trace HTTP Request
HttpMetric networkMetric =
FirebasePerformance.getInstance().newHttpMetric(url, FirebasePerformance.HttpMethod.POST);
Utility.metricStart(networkMetric, json);
try {
serverResult = httpConn.requestToServer(url, json, Global.DEFAULTCONNECTIONTIMEOUT);
Utility.metricStop(networkMetric, serverResult);
} catch (Exception e) {
FireCrash.log(e);
if (Global.IS_DEV)
e.printStackTrace();
try {
progressDialog.dismiss();
} catch (Exception e1) {
FireCrash.log(e1);
}
errMessage = e.getMessage();
}
try {
if(serverResult == null){
return null;
}
else {
if (serverResult.isOK()) {
String resultString = serverResult.getResult();
JsonVersionResponse response = GsonHelper.fromJson(resultString, JsonVersionResponse.class);
if (response.getStatus().getCode() == 0) {
result = response;
} else {
errMessage = resultString;
}
}
}
}catch (Exception e) {
FireCrash.log(e);
}
} else {
errMessage = getString(R.string.no_internet_connection);
}
return result;
}
@Override
protected void onPostExecute(JsonVersionResponse result) {
if (progressDialog.isShowing()) {
try {
progressDialog.dismiss();
} catch (Exception e) {
FireCrash.log(e);
}
}
if (errMessage != null && !errMessage.isEmpty()) {
if(GlobalData.isRequireRelogin()){
return;
}
Toast.makeText(getActivity(), errMessage, Toast.LENGTH_SHORT).show();
} else {
if (result != null && result.getListValues() != null) {
boolean softUpdate = false;
boolean forceUpdate = false;
String listVersion = "";
String otaLink = "";
for (ListValue kv : result.getListValues()) {
if (kv.getKey().equals(Global.GS_BUILD_NUMBER)) {
listVersion = kv.getValue();
} else if (kv.getKey().equals(Global.GS_URL_DOWNLOAD)) {
otaLink = kv.getValue();
}
}
int serverVersion = 0;
try {
serverVersion = Integer.valueOf(listVersion);
} catch (NumberFormatException nfe) {
FireCrash.log(nfe);
}
String[] versions = Tool.split(listVersion, Global.DELIMETER_DATA);
String thisVersion = Tool.split(Global.APP_VERSION, "-")[0];
String lastVersionFromServer = null;
for (int i = 0; i < versions.length; i++) {
lastVersionFromServer = versions[i];
if (thisVersion.equals(lastVersionFromServer)) {
softUpdate = true;
}
}
if (!softUpdate) {
forceUpdate = true;
} else {
if (thisVersion.equals(lastVersionFromServer))
softUpdate = false;
}
String uuidUser = GlobalData.getSharedGlobalData().getUser().getUuid_user();
Boolean loginEnabled = GeneralParameterDataAccess.getOne(getActivity(), uuidUser, Global.GS_VERS_LOGIN).getGs_value().equals("1");
int versionLocal = 0;
try {
versionLocal = getActivity().getPackageManager().getPackageInfo(getActivity().getPackageName(), 0).versionCode;
} catch (NameNotFoundException e) {
if (Global.IS_DEV)
e.printStackTrace();
}
if (forceUpdate) {
showAskForceUpdateDialog(otaLink);
return;
} else if (softUpdate) {
showAskForUpdateDialog(otaLink);
return;
} else {
Toast.makeText(getActivity(), getString(R.string.msgNoNewVersion), Toast.LENGTH_SHORT).show();
}
if (serverVersion > versionLocal) {
if (loginEnabled) {
showAskForUpdateDialog(otaLink);
} else {
showAskForceUpdateDialog(otaLink);
}
} else {
Toast.makeText(getActivity(), getString(R.string.msgNoNewVersion), Toast.LENGTH_SHORT).show();
}
}
}
}
}.execute();
}
private void showAskForceUpdateDialog(final String otaLink) {
final NiftyDialogBuilder builder = NiftyDialogBuilder.getInstance(getActivity());
builder.withTitle("Server")
.withMessage(getString(R.string.critical_update))
.withButton1Text(getString(R.string.update))
.setButton1Click(new View.OnClickListener() {
@Override
public void onClick(View v) {
openUpdate(otaLink);
}
});
builder.isCancelable(false);
builder.show();
}
private void showAskForUpdateDialog(final String otaLink) {
final NiftyDialogBuilder builder = NiftyDialogBuilder.getInstance(getActivity());
builder.withTitle("Server").isCancelableOnTouchOutside(false).isCancelable(false)
.withMessage(getString(R.string.update_available))
.withButton1Text(getString(R.string.later))
.setButton1Click(new View.OnClickListener() {
@Override
public void onClick(View v) {
builder.dismiss();
}
})
.withButton2Text(getString(R.string.update))
.setButton2Click(new View.OnClickListener() {
@Override
public void onClick(View v) {
builder.dismiss();
openUpdate(otaLink);
}
}).show();
}
private void openUpdate(String otaLink) {
try {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
File file = new File(getContext().getFilesDir(), "app.apk");
if (file.exists()) {
Uri apkURI = FileProvider.getUriForFile(
getContext(), getContext().getPackageName() + ".provider", file);
intent.setDataAndType(apkURI, "application/vnd.android.package-archive");
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(intent);
} else {
DownloadUpdate downloadUpdate = new DownloadUpdate(getContext());
downloadUpdate.execute(otaLink);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

View file

@ -0,0 +1,184 @@
package com.adins.mss.base.about.activity;
import android.content.Context;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.core.content.ContextCompat;
import androidx.viewpager.widget.ViewPager;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import com.adins.mss.base.R;
import com.adins.mss.base.crashlytics.FireCrash;
import com.adins.mss.base.todolist.form.TaskList_Fragment;
import com.adins.mss.base.util.Utility;
import com.adins.mss.foundation.dialog.NiftyDialogBuilder;
import com.github.jjobes.slidedatetimepicker.SlidingTabLayout;
//import static com.google.android.gms.internal.a.R;
/**
* Created by gigin.ginanjar on 15/08/2016.
*/
public class AboutInfoTab extends Fragment {
public static boolean isMenuClicked = false;
private static Menu mainMenu;
View view;
private Context mContext;
private ViewPager mViewPager;
private ViewPagerAdapter mViewPagerAdapter;
private SlidingTabLayout mSlidingTabLayout;
private boolean isError = false;
private String message;
private Fragment activeFragment;
@Override
public void onAttach(Context activity) {
super.onAttach(activity);
mContext = activity;
try {
isError = getArguments().getBoolean(TaskList_Fragment.BUND_KEY_ISERROR, false);
message = getArguments().getString(TaskList_Fragment.BUND_KEY_MESSAGE, "");
} catch (Exception e) {
FireCrash.log(e);
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
view = inflater.inflate(R.layout.about_info_tab, container, false);
//2017/09/07 | Nendi: add Title on toolbar
getActivity().findViewById(com.adins.mss.base.R.id.search).setVisibility(View.GONE);
getActivity().findViewById(R.id.spinner).setVisibility(View.GONE);
getActivity().setTitle(getString(R.string.title_mn_about));
setupViews(view);
initViewPager();
initTabs();
setHasOptionsMenu(true);
return view;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
return super.onOptionsItemSelected(item);
}
private void initTabs() {
// Set intial date on date tab
updateAppTab();
// Set initial time on time tab
updateDeviceTab();
}
@Override
public void onDestroyView() {
try {
mainMenu.findItem(R.id.menuMore).setVisible(false);
} catch (Exception e) {
FireCrash.log(e);
}
super.onDestroyView();
Utility.freeMemory();
}
private void updateAppTab() {
mSlidingTabLayout.setTabText(1, getResources().getString(R.string.tabDevice));
}
private void updateDeviceTab() {
mSlidingTabLayout.setTabText(0, getResources().getString(R.string.tabApplication));
}
private void initViewPager() {
mViewPagerAdapter = new ViewPagerAdapter(getChildFragmentManager());
mViewPager.setAdapter(mViewPagerAdapter);
// Setting this custom layout for each tab ensures that the tabs will
// fill all available horizontal space.
mSlidingTabLayout.setCustomTabView(R.layout.custom_tab_tasklist, R.id.tabTextTaskList);
mSlidingTabLayout.setSelectedIndicatorColors(ContextCompat.getColor(getActivity(), R.color.tv_white),
ContextCompat.getColor(getActivity(), R.color.tv_white));
mSlidingTabLayout.setDividerColors(ContextCompat.getColor(getActivity(), R.color.tv_white),
ContextCompat.getColor(getActivity(), R.color.tv_white));
mSlidingTabLayout.setViewPager(mViewPager);
if (isError) {
isError = false;
NiftyDialogBuilder dialogBuilder = NiftyDialogBuilder.getInstance(getActivity());
if (!dialogBuilder.isShowing()) {
dialogBuilder.withTitle(getResources().getString(R.string.warning_capital)).
withMessage(message).isCancelable(true).show();
}
}
}
private void setupViews(View v) {
mViewPager = (ViewPager) v.findViewById(R.id.pager);
mSlidingTabLayout = (SlidingTabLayout) v.findViewById(R.id.slidingTabLayout);
}
@Override
public void onResume() {
super.onResume();
// isMenuClicked = false;
// getActivity().getActionBar().setTitle(getString(R.string.title_mn_about));
Utility.freeMemory();
// MainMenuActivity.setDrawerPosition(getString(R.string.title_mn_about));
// if(view!=null) {
// initViewPager();
// initTabs();
// }
getActivity().findViewById(com.adins.mss.base.R.id.search).setVisibility(View.GONE);
getActivity().findViewById(R.id.spinner).setVisibility(View.GONE);
getActivity().setTitle(getString(R.string.title_mn_about));
}
@Override
public void onSaveInstanceState(Bundle outState) {
}
private class ViewPagerAdapter extends FragmentPagerAdapter {
public ViewPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
Fragment fragment = null;
switch (position) {
case 0:
fragment = new AboutActivity();
break;
case 1:
fragment = new DeviceInfoFragment();
break;
default:
break;
}
return fragment;
}
@Override
public int getCount() {
return 2;
}
}
}

View file

@ -0,0 +1,172 @@
package com.adins.mss.base.about.activity;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.BatteryManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.StatFs;
import android.provider.Settings;
import androidx.fragment.app.Fragment;
import androidx.viewpager.widget.ViewPager;
import android.text.format.DateFormat;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import com.adins.mss.base.R;
import com.adins.mss.base.networkmonitor.NetworkMonitorDataUsage;
import com.adins.mss.constant.Global;
import com.adins.mss.foundation.formatter.Tool;
import com.androidquery.AQuery;
import com.github.jjobes.slidedatetimepicker.SlidingTabLayout;
import com.mikepenz.aboutlibraries.Libs;
import com.mikepenz.aboutlibraries.entity.Library;
import org.acra.ACRA;
import java.io.File;
import java.util.ArrayList;
import java.util.Calendar;
import static com.pax.ippi.impl.NeptuneUser.getApplicationContext;
/**
* @author gigin.ginanjar
*/
public class DeviceInfoFragment extends Fragment {
public static boolean backEnabled = true;
private static ArrayList<Library> library;
private static int flag;
public Libs libs;
AQuery query;
int battery;
long appSize;
String txtMobileData;
NetworkMonitorDataUsage monitorDU = new NetworkMonitorDataUsage();
private SlidingTabLayout mSlidingTabLayout;
private ViewPager mViewPager;
/**
* @param changeLog
* @param flag
*/
public static void setChangeLog(ArrayList<Library> changeLog, int flag) {
library = new ArrayList<Library>();
library = changeLog;
DeviceInfoFragment.flag = flag;
}
public static long getAvailableInternalMemorySize() {
File path = Environment.getDataDirectory();
StatFs stat = new StatFs(path.getPath());
long blockSize = 0;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) {
blockSize = stat.getBlockSizeLong();
} else {
blockSize = stat.getBlockSize();
}
long availableBlocks = 0;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) {
availableBlocks = stat.getAvailableBlocksLong();
} else {
availableBlocks = stat.getAvailableBlocks();
}
return (availableBlocks * blockSize) / 1048576;
}
public int getBatteryLevel() {
//int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent batteryStatus = getActivity().registerReceiver(null, ifilter);
int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
// int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
// int plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0);
int battery = (level * 100) / scale;
// int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);\
return battery;
}
public long getAppSize() {
try {
appSize = new File(getActivity().getPackageManager().getApplicationInfo(getActivity().getPackageName(), 0).publicSourceDir).length();
} catch (NameNotFoundException e) {
ACRA.getErrorReporter().putCustomData("errorNameNotFound", e.getMessage());
ACRA.getErrorReporter().putCustomData("errorNameNotFound", DateFormat.format("yyyy.MM.dd G \'at\' HH:mm:ss z", Calendar.getInstance().getTime()).toString());
ACRA.getErrorReporter().handleSilentException(new Exception("Exception saat set getNameFile"));
e.printStackTrace();
}
return appSize / 1048576;
}
public String getMobileDataUsage() {
String mobileData = Long.toString(monitorDU.getDataThisDay(getContext()) / 1024);
int size = 0;
for (int i = 0; i < mobileData.length(); i++) {
size += 1;
}
if (size <= 3)
txtMobileData = mobileData + " KB";
else if (size > 3 && size <= 6)
txtMobileData = mobileData.substring(0, mobileData.length() - 3) + " MB";
else
txtMobileData = mobileData.substring(0, mobileData.length() - 3) + " GB";
return txtMobileData;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
View view = inflater.inflate(R.layout.new_device_info_tab, container, false);
TextView txtOsVer = (TextView) view.findViewById(R.id.txtOsVer);
TextView txtAppVer = (TextView) view.findViewById((R.id.txtAppVer));
TextView txtBattery = (TextView) view.findViewById((R.id.txtBattery));
// TextView txtDataUsage = (TextView) view.findViewById((R.id.txtDataUsage));
TextView txtMobileDataUsage = (TextView) view.findViewById((R.id.txtMobileDataUsage));
TextView txtMemoryAvailable = (TextView) view.findViewById((R.id.txtMemoryAvailable));
TextView txtAndroidID = (TextView) view.findViewById((R.id.txtAndroidId));
ACRA.getErrorReporter().putCustomData("LAST_CLASS_ACCESSED", getClass().getSimpleName());
txtAppVer.setText(getString(R.string.appVer) + " : " + Global.APP_VERSION);
txtOsVer.setText(getString(R.string.androidVersion) + " : " + Build.VERSION.RELEASE);
txtBattery.setText(getString(R.string.batteryPercen) + " : " + getBatteryLevel() + " %");
// olivia : UPDATE - Application Storage dihilangkan
// txtDataUsage.setText(getString(R.string.appStorage) + " : " + Tool.separateThousand((double) getAppSize())+ " MB");
txtMobileDataUsage.setText(getString(R.string.dataUsage) + " : " + getMobileDataUsage());
txtMemoryAvailable.setText(getString(R.string.deviceStorage) + " : " + Tool.separateThousand(getAvailableInternalMemorySize()) + " MB");
final String android_id;
if(Build.VERSION.SDK_INT > 28){
android_id = Settings.Secure.getString(getContext().getContentResolver(), Settings.Secure.ANDROID_ID);
txtAndroidID.setVisibility(View.VISIBLE);
}else {
android_id = "";
txtAndroidID.setVisibility(View.GONE);
}
txtAndroidID.setText(getString(R.string.androidID)+" : "+android_id);
txtAndroidID.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
ClipboardManager clipboard = (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("Clip", android_id);
clipboard.setPrimaryClip(clip);
Toast.makeText(getContext(),getString(R.string.copied),Toast.LENGTH_SHORT).show();
}
});
return view;
}
}

View file

@ -0,0 +1,43 @@
package com.adins.mss.base.about.activity;
import com.adins.mss.foundation.http.MssResponseType;
import com.google.gson.annotations.SerializedName;
import java.util.List;
public class JsonVersionResponse extends MssResponseType {
@SerializedName("listValue")
List<ListValue> listValue;
public List<ListValue> getListValues() {
return this.listValue;
}
public void setListValues(List<ListValue> listValue) {
this.listValue = listValue;
}
public class ListValue {
@SerializedName("key")
String key;
@SerializedName("value")
String value;
public String getKey() {
return this.key;
}
public void setKey(String key) {
this.key = key;
}
public String getValue() {
return this.value;
}
public void setValue(String value) {
this.value = value;
}
}
}

View file

@ -0,0 +1,142 @@
package com.adins.mss.base.api;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.os.AsyncTask;
import com.adins.mss.base.GlobalData;
import com.adins.mss.base.R;
import com.adins.mss.base.crashlytics.FireCrash;
import com.adins.mss.base.models.ChangePasswordRequestModel;
import com.adins.mss.base.models.ChangePasswordResponseModel;
import com.adins.mss.base.util.GsonHelper;
import com.adins.mss.base.util.Utility;
import com.adins.mss.constant.Global;
import com.adins.mss.foundation.formatter.Tool;
import com.adins.mss.foundation.http.HttpConnectionResult;
import com.adins.mss.foundation.http.HttpCryptedConnection;
import com.adins.mss.foundation.http.net.HttpClient;
import com.google.firebase.perf.FirebasePerformance;
import com.google.firebase.perf.metrics.HttpMetric;
/**
* Created by adityapurwa on 30/03/15.
*/
public class ChangePasswordApi {
private final Context context;
private final Activity activity;
private final HttpClient http;
private ChangePasswordApiCallback callback;
public ChangePasswordApi(Activity context) {
this.activity = context;
this.context = context;
this.http = new HttpClient(context);
}
public ChangePasswordApiCallback getCallback() {
return callback;
}
public void setCallback(ChangePasswordApiCallback callback) {
this.callback = callback;
}
public void execute(final ChangePasswordRequestModel request) {
new AsyncTask<Void, Void, ChangePasswordResponseModel>() {
private ProgressDialog progressDialog;
private String errMessage;
@Override
protected void onPreExecute() {
progressDialog = ProgressDialog.show(context, "", context.getString(R.string.contact_server), true);
}
@Override
protected ChangePasswordResponseModel doInBackground(Void... params) {
try {
if (Tool.isInternetconnected(context)) {
String json = GsonHelper.toJson(request);
boolean encrypt = GlobalData.getSharedGlobalData().isEncrypt();
boolean decrypt = GlobalData.getSharedGlobalData().isDecrypt();
HttpCryptedConnection httpConn = new HttpCryptedConnection(activity, encrypt, decrypt);
HttpConnectionResult serverResult = null;
String url = GlobalData.getSharedGlobalData().getURL_CHANGEPASSWORD();
//Firebase Performance Trace HTTP Request
HttpMetric networkMetric =
FirebasePerformance.getInstance().newHttpMetric(url, FirebasePerformance.HttpMethod.POST);
Utility.metricStart(networkMetric, json);
try {
serverResult = httpConn.requestToServer(url, json, Global.DEFAULTCONNECTIONTIMEOUT);
Utility.metricStop(networkMetric, serverResult);
} catch (Exception e) {
errMessage = e.getMessage();
FireCrash.log(e);
}
String body = null;
if (serverResult != null && serverResult.isOK()) {
body = serverResult.getResult();
ChangePasswordResponseModel result = null;
try {
result = GsonHelper.fromJson(body, ChangePasswordResponseModel.class);
return result;
} catch (Exception e) {
FireCrash.log(e);
errMessage = e.getMessage();
return null;
}
} else {
if(serverResult != null && serverResult.getResult() != null)
errMessage = serverResult.getResult();
return null;
}
} else {
errMessage = context.getString(R.string.no_internet_connection);
return null;
}
} catch (Exception e) {
FireCrash.log(e);
if (Global.IS_DEV)
e.printStackTrace();
return null;
}
}
@Override
protected void onPostExecute(ChangePasswordResponseModel result) {
super.onPostExecute(result);
if (progressDialog.isShowing()) {
try {
progressDialog.dismiss();
} catch (Exception e) {
FireCrash.log(e);
}
}
if (result == null) {
if (errMessage != null && getCallback() != null){
getCallback().onFailed(errMessage);
}else{
getCallback().onFailed(context.getString(R.string.error_unknown));
}
return;
}
if (result.getStatus().getCode() == 0) {
if (getCallback() != null) getCallback().onSuccess();
} else {
if (getCallback() != null) {
if (result.getStatus().getMessage() != null)
getCallback().onFailed(result.getStatus().getMessage());
else
getCallback().onFailed(String.valueOf(result.getStatus().getCode()));
}
}
}
}.execute();
}
}

View file

@ -0,0 +1,10 @@
package com.adins.mss.base.api;
/**
* Created by adityapurwa on 30/03/15.
*/
public interface ChangePasswordApiCallback {
void onFailed(String message);
void onSuccess();
}

View file

@ -0,0 +1,68 @@
package com.adins.mss.base.api;
import android.content.Context;
import com.adins.mss.base.GlobalData;
import com.adins.mss.base.crashlytics.FireCrash;
import com.adins.mss.base.models.CheckResubmitRequest;
import com.adins.mss.base.util.GsonHelper;
import com.adins.mss.base.util.Utility;
import com.adins.mss.constant.Global;
import com.adins.mss.foundation.http.HttpConnectionResult;
import com.adins.mss.foundation.http.HttpCryptedConnection;
import com.adins.mss.foundation.http.MssResponseType;
import com.google.firebase.perf.FirebasePerformance;
import com.google.firebase.perf.metrics.HttpMetric;
import java.io.IOException;
/**
* Created by eric.sn on 4/5/2017.
*/
public class CheckResubmitApi {
private final Context context;
public CheckResubmitApi(Context context) {
this.context = context;
}
public MssResponseType request(String uuidtaskH) throws IOException {
CheckResubmitRequest request = new CheckResubmitRequest();
request.setUuidTaskH(uuidtaskH);
request.setAudit(GlobalData.getSharedGlobalData().getAuditData());
String requestJson = GsonHelper.toJson(request);
String url = GlobalData.getSharedGlobalData().getURL_CHECK_RESUBMIT();
boolean encrypt = GlobalData.getSharedGlobalData().isEncrypt();
boolean decrypt = GlobalData.getSharedGlobalData().isDecrypt();
HttpCryptedConnection httpConn = new HttpCryptedConnection(context, encrypt, decrypt);
HttpConnectionResult serverResult = null;
HttpMetric networkMetric =
FirebasePerformance.getInstance().newHttpMetric(url, FirebasePerformance.HttpMethod.POST);
Utility.metricStart(networkMetric, requestJson);
try {
serverResult = httpConn.requestToServer(url, requestJson, Global.DEFAULTCONNECTIONTIMEOUT);
Utility.metricStop(networkMetric, serverResult);
} catch (Exception e) {
FireCrash.log(e);
e.printStackTrace();
}
String responseJson = "";
if (serverResult != null && serverResult.isOK()) {
try {
responseJson = serverResult.getResult();
} catch (Exception e) {
FireCrash.log(e);
}
}
// bong 21 mei 15 - add or replace to local database
MssResponseType checkResubmitResp = GsonHelper.fromJson(responseJson, MssResponseType.class);
return checkResubmitResp;
}
}

View file

@ -0,0 +1,772 @@
package com.adins.mss.base.authentication;
import android.content.Context;
import android.util.Log;
import com.adins.mss.base.AppContext;
import com.adins.mss.base.GlobalData;
import com.adins.mss.base.NewMainActivity;
import com.adins.mss.base.R;
import com.adins.mss.base.crashlytics.FireCrash;
import com.adins.mss.base.dynamictheme.DynamicTheme;
import com.adins.mss.base.dynamictheme.ThemeLoader;
import com.adins.mss.base.timeline.TimelineManager;
import com.adins.mss.base.util.GenericAsyncTask;
import com.adins.mss.base.util.GenericAsyncTask.GenericTaskInterface;
import com.adins.mss.base.util.GsonHelper;
import com.adins.mss.base.util.LocaleHelper;
import com.adins.mss.base.util.UserSession;
import com.adins.mss.base.util.Utility;
import com.adins.mss.constant.Global;
import com.adins.mss.dao.GeneralParameter;
import com.adins.mss.dao.Menu;
import com.adins.mss.dao.Timeline;
import com.adins.mss.dao.User;
import com.adins.mss.foundation.UserHelp.UserHelp;
import com.adins.mss.foundation.db.dataaccess.GeneralParameterDataAccess;
import com.adins.mss.foundation.db.dataaccess.MenuDataAccess;
import com.adins.mss.foundation.db.dataaccess.TimelineDataAccess;
import com.adins.mss.foundation.db.dataaccess.UserDataAccess;
import com.adins.mss.foundation.formatter.Formatter;
import com.adins.mss.foundation.formatter.Tool;
import com.adins.mss.foundation.http.AuditDataType;
import com.adins.mss.foundation.http.AuditDataTypeGenerator;
import com.adins.mss.foundation.http.HttpConnectionResult;
import com.adins.mss.foundation.http.HttpCryptedConnection;
import com.adins.mss.foundation.http.MssRequestType;
import com.adins.mss.foundation.http.MssResponseType;
import com.adins.mss.foundation.http.MssResponseType.Status;
import com.adins.mss.foundation.security.storepreferences.ObscuredSharedPreferences;
import com.google.firebase.perf.FirebasePerformance;
import com.google.firebase.perf.metrics.HttpMetric;
import org.acra.ACRA;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* <p> Used to authenticate user, using HttpCryptedConnection to send user credential to server, and receive server response
*
* @author glen.iglesias
*/
public class Authentication implements GenericTaskInterface {
public static final String LOGIN_PREFERENCES = "login_preferences";
public static final String LOGIN_PREFERENCES_APPLICATION_CLEANSING = "login_preferences.APPLICATION_CLEANSING";
public static final String SHARED_PREF = "sharedPreferencesAuthentication";
public static final String SHARED_PREF_KEY_FRESH_INSTALL = "sharedPreferencesKey_freshInstall";
public static final String SHARED_PREF_KEY_DB_SAVED = "sharedPreferencesKey_dbsaved";
private static final int LOGIN_SUCCESS = 1;
private static final int FORCE_UPDATE = 2;
private static final int FORCE_CLEANSING = 3;
private static final int LOGIN_FAIL = 0;
private static final String TASK_OBJ_KEY_CONN = "task_object_key_connection";
private static final String TASK_OBJ_KEY_URL = "task_object_key_url";
private static final String TASK_OBJ_KEY_JSON = "task_object_key_json";
private static final String TASK_OBJ_KEY_DELEGATE = "task_object_key_delegate";
private static final String TASK_OBJ_KEY_CONTEXT = "task_object_key_context";
private static final String TASK_OBJ_KEY_RESULT = "task_object_key_result";
private static final String TASK_OBJ_KEY_BUILD_NUMBER = "task_object_key_build_number";
private static final String TASK_OBJ_KEY_LOGIN_ID = "task_object_key_login_id";
//Debug
public static boolean enableSimulatedResult = false;
public static int simulatedResult = 1;
protected static String temp_loginID;
protected static String temp_password;
private String url;
private AuthenticationHandler handler;
private boolean encrypt;
private boolean decrypt;
/**
* Default constructor. Will fetch data from GlobalData if any
*/
public Authentication() {
GlobalData globData = GlobalData.getSharedGlobalData();
url = globData.getURL_LOGIN();
encrypt = globData.isEncrypt();
decrypt = globData.isDecrypt();
}
/**
* Send user credential over HTTP to server. Will replace stored AuditDataType in GlobalData with newly
* generated one. Will also store successfully logged in user data in GlobalData
* <br>Connection will run on async task
*
* @param context
* @param url target URL
* @param username username to be authenticated
* @param password password to be authenticated
* @param encrypt flag to encrypt sent data to server
* @param decrypt flag to decrypt server response message
*/
public static void authenticateOnBackground(Context context, String url, String username, String password, boolean encrypt, boolean decrypt, int buildNumber, AuthenticationHandler handler) {
Map<String, String> listImei = AuditDataTypeGenerator.getListImeiFromDevice(context);
GlobalData.getSharedGlobalData().setImei(listImei.get(MssRequestType.UN_KEY_IMEI));
if (listImei.get(MssRequestType.UN_KEY_IMEI2) != null) {
GlobalData.getSharedGlobalData().setImei2(listImei.get(MssRequestType.UN_KEY_IMEI2));
}
//generate and store new AuditDataType
String language = LocaleHelper.getLanguage(context);
LocaleHelper.setLocale(context,language);
GlobalData.getSharedGlobalData().setLocale(language);
AuditDataType auditDataType = AuditDataTypeGenerator.generateAuditDataType(username);
storeAuditData(auditDataType);
//freshInstall
boolean needUpdateVersion = UserSession.needUpdateVersion();
boolean isFresh = isFreshInstall(context);
boolean isFreshInstall = needUpdateVersion || isFresh;
HttpCryptedConnection httpConn = new HttpCryptedConnection(context, encrypt, decrypt);
temp_loginID = username;
temp_password = password;
String json = getStringForSending(username, password, isFreshInstall);
if (Global.IS_DEV)
System.out.println(json);
//use asynctask instead
HashMap<String, Object> params = new HashMap<>();
params.put(TASK_OBJ_KEY_CONN, httpConn);
params.put(TASK_OBJ_KEY_URL, url);
params.put(TASK_OBJ_KEY_JSON, json);
params.put(TASK_OBJ_KEY_DELEGATE, handler);
params.put(TASK_OBJ_KEY_CONTEXT, context);
params.put(TASK_OBJ_KEY_BUILD_NUMBER, buildNumber);
params.put(TASK_OBJ_KEY_LOGIN_ID, username);
GenericAsyncTask task = new GenericAsyncTask(new Authentication());
task.setAdditionalObject(params);
task.execute();
}
/**
* Send user credential over HTTP to server using GlobalData settings, like url, encryption, and decryption
* <br>Connection will run on async task
*
* @param username username to be authenticated
* @param password password to be authenticated
*/
public static void authenticateOnBackground(Context context, String username, String password, int buildNumber, AuthenticationHandler handler) {
GlobalData globData = GlobalData.getSharedGlobalData();
String url = globData.getURL_LOGIN();
boolean enc = globData.isEncrypt();
boolean dec = globData.isDecrypt();
authenticateOnBackground(context, url, username, password, enc, dec, buildNumber, handler);
}
/**
* Define a way to interpret server response and translate into AuthenticationResultBean
* <p>Should you choose a different way, override this method
*
* @param result a string received from server
* @return an AuthenticationResultBean as standard result
*/
protected static AuthenticationResultBean mapResultToBean(String result, String login_id_tenant) {
//Default, treat result as JSON with format of LoginUserResponse
LoginUserResponse responseBean;
try {
responseBean = GsonHelper.fromJson(result, LoginUserResponse.class);
} catch (Exception e) {
FireCrash.log(e);
if (Global.IS_DEV)
e.printStackTrace();
return null;
}
Status resultStatus = responseBean.getStatus();
List<GeneralParameter> generalParameterss = responseBean.getListGeneralParameter();
//add tenant id to general parameters
GeneralParameter gP = new GeneralParameter();
if (!generalParameterss.isEmpty()) {
gP.setUuid_general_parameter(Tool.getUUID());
gP.setGs_code(Global.GS_TENANT_ID);
gP.setGs_value(login_id_tenant);
generalParameterss.add(0, gP);
}
//20 Jan 2015, adapt to backend system
AuthenticationResultBean resultBean = new AuthenticationResultBean();
if (resultStatus.getCode() == 0) { //success login
User user = responseBean.getUser();
resultBean.setActiveUser(true);
String loginGenParam = getGeneralParameter(Global.GS_VERS_LOGIN, generalParameterss);
if(loginGenParam != null && loginGenParam.equals("1")){
resultBean.setForceUpdate(true);
}
resultBean.setGeneralParameters(responseBean.getListGeneralParameter());
resultBean.setListMenu(responseBean.getListMenu());
resultBean.setLastUpdateVersion(getGeneralParameter(Global.GS_BUILD_NUMBER, generalParameterss));
resultBean.setLoginResult(LOGIN_SUCCESS);
resultBean.setNeedUpdatePassword(user.getChg_pwd().equals(Global.TRUE_STRING));
resultBean.setOtaLink(getGeneralParameter(Global.GS_URL_DOWNLOAD, generalParameterss));
resultBean.setUserInfo(user);
} else { //login failed
resultBean.setLoginResult(LOGIN_FAIL);
resultBean.setMessage(resultStatus.getMessage());
}
return resultBean;
}
private static String getGeneralParameter(String name, List<GeneralParameter> generalParameterss) {
for (GeneralParameter param : generalParameterss) {
if (param.getGs_code().equals(name)) {
return param.getGs_value();
}
}
return null;
}
/**
* Override to implement different method of storing AuditData
*
* @param auditData
*/
protected static void storeAuditData(AuditDataType auditData) {
GlobalData.getSharedGlobalData().setAuditData(auditData);
}
/**
* Override this method to provide other format of String to send to server
* <p>Default is using AuthenticationModel as the format
*
* @param username username the user input
* @param password password the user input
* @param isFreshInstall true if this application never had any user login yet
* @return string of data (JSON or not) to send to server
* @see MssRequestType
*/
protected static String getStringForSending(String username, String password, boolean isFreshInstall) {
//Initiate audit data
AuditDataType audit = AuditDataTypeGenerator.generateActiveUserAuditData();
GlobalData.getSharedGlobalData().setAuditData(audit);
String strFlagFreshInstall = isFreshInstall ? "1" : "0";
//Create LoginUserRequest
LoginUserRequest data = new LoginUserRequest();
data.setAudit(audit);
data.setUsername(username);
data.setPassword(password);
data.setFlagFreshInstall(strFlagFreshInstall);
data.setFcmTokenId(Global.Token);
//GlobalData.imei were set from method authenticate
data.addImeiAndroidIdToUnstructured();
String json = Formatter.getJsonFromObject(data);
return json;
}
/**
* Set as no longer a fresh install to application preferences. On next call to authenticate(), method would give
* false to isFreshInstall
*
* @param context
*/
public static void setAsNonFreshInstall(Context context) {
ObscuredSharedPreferences pref = ObscuredSharedPreferences.getPrefs(context, SHARED_PREF, Context.MODE_PRIVATE);
pref.edit().putString(SHARED_PREF_KEY_FRESH_INSTALL, "false").apply();
}
/**
* Set as a fresh install to application preferences. On next call to authenticate(), method would give
* true to isFreshInstall
*
* @param context
*/
public static void setAsFreshInstall(Context context) {
ObscuredSharedPreferences pref = ObscuredSharedPreferences.getPrefs(context, SHARED_PREF, Context.MODE_PRIVATE);
pref.edit().putString(SHARED_PREF_KEY_FRESH_INSTALL, "true").apply();
}
/**
* Method to check if application is first-time running after installation and has device's IMEI not registered yet
* based on whether the sharedPreference exist or not.
* <p/>
* Override this method to implement other method of determining if it's a fresh install
*
* @param context
* @return true if it is a fresh install application, false if it is not
*/
public static boolean isFreshInstall(Context context) {
ObscuredSharedPreferences pref = ObscuredSharedPreferences.getPrefs(context, SHARED_PREF, Context.MODE_PRIVATE);
String isFreshInstall = pref.getString(SHARED_PREF_KEY_FRESH_INSTALL, "true"); //if no preference exist, meaning it's a new install
return Boolean.parseBoolean(isFreshInstall);
}
/**
* Call this method and pass parameters object to save it to database
* <p/>
* <p>Override this method to change how application save the parameters
*/
protected static void saveServerParameters(Context context, List<GeneralParameter> generalParameters) {
for (GeneralParameter gp : generalParameters) {
String temp_uuid_user = gp.getUuid_user();
User user = UserDataAccess.getOne(context, temp_uuid_user);
gp.setUser(user);
GeneralParameterDataAccess.addOrReplace(context, gp);
}
//save to GlobalData
GlobalData.getSharedGlobalData().loadGeneralParameters(generalParameters);
}
protected static void saveUserInfo(Context context, User authenticatedUser) {
String temp_uuid_user = authenticatedUser.getUuid_user();
User user = UserDataAccess.getOne(context, temp_uuid_user);
if (user != null) {
user.setLogin_id(temp_loginID);
user.setPassword(temp_password);
if (Global.IS_LOGIN) {
user.setBranch_name(authenticatedUser.getBranch_name());
Global.IS_LOGIN = false;
}
//olivia : update jam start dan end tracking
user.setIs_tracking(authenticatedUser.getIs_tracking());
if (authenticatedUser.getIs_tracking() != null && authenticatedUser.getIs_tracking().equals("1")) {
user.setStart_time(authenticatedUser.getStart_time());
user.setEnd_time(authenticatedUser.getEnd_time());
}
if(authenticatedUser.getTracking_days() != null && !authenticatedUser.getTracking_days().equals("")){
user.setTracking_days(authenticatedUser.getTracking_days());
}
user.setCash_on_hand(authenticatedUser.getCash_on_hand());
user.setCash_limit(authenticatedUser.getCash_limit());
UserDataAccess.addOrReplace(context, user);
// save to GlobalData
GlobalData.getSharedGlobalData().setUser(user);
Global.user = user;
} else {
authenticatedUser.setLogin_id(temp_loginID);
authenticatedUser.setPassword(temp_password);
UserDataAccess.addOrReplace(context, authenticatedUser);
// save to GlobalData
GlobalData.getSharedGlobalData().setUser(authenticatedUser);
Global.user = authenticatedUser;
}
}
protected static void saveMenu(Context context, List<Menu> listMenu) {
for (Menu menu : listMenu) {
String temp_uuid_user = menu.getUuid_user();
User user = UserDataAccess.getOne(context, temp_uuid_user);
menu.setUser(user);
MenuDataAccess.addOrReplace(context, menu);
}
}
protected static void updateTimeline(Context context) {
// 2017/10/09 | olivia : hapus timeline yang sudah lebih dari range di general parameter
int range = GlobalData.getSharedGlobalData().getKeepTimelineInDays();
List<Timeline> timelines = TimelineManager.getAllTimeline(context);
for (Timeline timeline : timelines) {
if (timeline.getDtm_crt().before(Tool.getIncrementDate(-range)))
TimelineDataAccess.delete(context, timeline);
}
}
/**
* Send user credential over HTTP to server
* <br>Will run connection on background thread
*
* @param context
* @param username
* @param password
*/
public void authenticateOnBackground(Context context, String username, String password, int buildNumber) {
authenticateOnBackground(context, url, username, password, encrypt, decrypt, buildNumber, handler);
}
//=== Generic Task Interface Callbacks ===//
@Override
public void onPreExecute(GenericAsyncTask task) {
Log.d("Log","OnPreExecute");//must override this
}
@Override
public String doInBackground(GenericAsyncTask task, String... args) {
HttpCryptedConnection httpConn = (HttpCryptedConnection) task.getAdditionalObject().get(TASK_OBJ_KEY_CONN);
String json = (String) task.getAdditionalObject().get(TASK_OBJ_KEY_JSON);
String url = (String) task.getAdditionalObject().get(TASK_OBJ_KEY_URL);
HttpConnectionResult result = null;
//Firebase Performance Trace HTTP Request
HttpMetric networkMetric =
FirebasePerformance.getInstance().newHttpMetric(url, FirebasePerformance.HttpMethod.POST);
Utility.metricStart(networkMetric, json);
try {
result = httpConn.requestToServer(url, json, Global.DEFAULTCONNECTIONTIMEOUT);
Utility.metricStop(networkMetric, result);
} catch (Exception e) {
FireCrash.log(e);
if (Global.IS_DEV)
e.printStackTrace();
}
if (result != null) task.getAdditionalObject().put(TASK_OBJ_KEY_RESULT, result);
return "";
}
@Override
public void onPostExecute(GenericAsyncTask task, String result,
String errMsg) {
HttpConnectionResult connResult = (HttpConnectionResult) task.getAdditionalObject().get(TASK_OBJ_KEY_RESULT);
AuthenticationHandler handler = (AuthenticationHandler) task.getAdditionalObject().get(TASK_OBJ_KEY_DELEGATE);
Context context = (Context) task.getAdditionalObject().get(TASK_OBJ_KEY_CONTEXT);
String loginId = (String) task.getAdditionalObject().get(TASK_OBJ_KEY_LOGIN_ID);
if (handler != null) {
if (connResult != null && connResult.isOK()) {
//success
//convert to bean
AuthenticationResultBean authResultBean = mapResultToBean(connResult.getResult(), loginId);
//bong 30 mar 15 - penjagaan jika belum log in di router tapi sudah dapat ip
try {
if (authResultBean == null) {
String error = context.getString(R.string.request_error) + ".\n" + context.getString(R.string.connection_failed_wifi);
connResult.setResult(error);
handler.onConnectionFail(null, connResult);
return;
}
} catch (Exception e) {
FireCrash.log(e);
if (Global.IS_DEV)
e.printStackTrace();
handler.onConnectionFail(null, connResult);
return;
}
MssResponseType responseFromServer = GsonHelper.fromJson(connResult.getResult(), MssResponseType.class);
if (responseFromServer.getStatus().getCode() == Global.STATUS_CODE_APPL_CLEANSING) {
NewMainActivity.Force_Uninstall = true;
handler.onInactiveUser(null);
ObscuredSharedPreferences.Editor editor = ObscuredSharedPreferences.getPrefs(context, LOGIN_PREFERENCES, Context.MODE_PRIVATE).edit();
editor.putString(LOGIN_PREFERENCES_APPLICATION_CLEANSING, "uninstall");
editor.apply();
return;
} else {
try {
ObscuredSharedPreferences.Editor editor = ObscuredSharedPreferences.getPrefs(context, LOGIN_PREFERENCES, Context.MODE_PRIVATE).edit();
editor.remove(LOGIN_PREFERENCES_APPLICATION_CLEANSING);
editor.apply();
} catch (Exception e) {
FireCrash.log(e);
}
}
String message = authResultBean.getMessage();
String otaLink = authResultBean.getOtaLink();
//get login status
int status = 0;
status = authResultBean.getLoginResult();
//debug
if (enableSimulatedResult) {
status = simulatedResult;
}
if (status != LOGIN_FAIL) { //Login authorized
boolean needUpdatePassword = authResultBean.needUpdatePassword();
boolean pwdExp = "1".equals(authResultBean.getUserInfo().getPwd_exp());
boolean forceUpdate = authResultBean.isForceUpdate();
boolean isActiveUser = authResultBean.isActiveUser();
List<GeneralParameter> generalParameters = authResultBean.getGeneralParameters();
List<Menu> listMenu = authResultBean.getListMenu();
User authenticatedUser = authResultBean.getUserInfo();
// bong - 30 jan 15 need set uuid_user to general parameter
for (GeneralParameter generalParameter : generalParameters) {
if (generalParameter.getUuid_user() == null || "".equals(generalParameter.getUuid_user()))
generalParameter.setUuid_user(authResultBean.getUserInfo().getUuid_user());
}
for (Menu menu : listMenu) {
if (menu.getUuid_user() == null || "".equals(menu.getUuid_user()))
menu.setUuid_user(authResultBean.getUserInfo().getUuid_user());
}
boolean softUpdate = false;
String listVersion = authResultBean.getLastUpdateVersion();
String[] versions = Tool.split(listVersion, Global.DELIMETER_DATA);
String thisVersion = Tool.split(Global.APP_VERSION, "-")[0];
String lastVersionFromServer = null;
for (int i = 0; i < versions.length; i++) {
lastVersionFromServer = versions[i];
if (thisVersion.equals(lastVersionFromServer)) {
softUpdate = true;
}
}
if (!softUpdate) {
forceUpdate = true;
} else {
if (thisVersion.equals(lastVersionFromServer))
softUpdate = false;
}
GeneralParameterDataAccess.clean(context);
MenuDataAccess.clean(context);
saveUserInfo(context, authenticatedUser);
saveServerParameters(context, generalParameters);
saveMenu(context, listMenu);
updateTimeline(context);
//GIGIN ~ SAVE USER INFO TO ACRA
ACRA.getErrorReporter().putCustomData("UUID_USER", GlobalData.getSharedGlobalData().getUser().getUuid_user());
ACRA.getErrorReporter().putCustomData("LOGIN_ID", GlobalData.getSharedGlobalData().getUser().getLogin_id());
ACRA.getErrorReporter().putCustomData("JOB_DESCRIPTION", GlobalData.getSharedGlobalData().getUser().getJob_description());
ACRA.getErrorReporter().putCustomData("BRANCH_NAME", GlobalData.getSharedGlobalData().getUser().getBranch_name());
ACRA.getErrorReporter().putCustomData("TENANT_ID", GlobalData.getSharedGlobalData().getTenant());
// bong 11 march 15 - stored callerId in audit
GlobalData.getSharedGlobalData().getAuditData().setCallerId(authResultBean.getUserInfo().getUuid_user());
if (!isActiveUser) {
status = FORCE_CLEANSING;
} else if (forceUpdate /*&& latestVersion > buildNumber*/) {
status = FORCE_UPDATE;
}
//proceed with status
if (status == LOGIN_SUCCESS) {
//Set as non fresh install
setAsNonFreshInstall(context);
// simpan app version ketika sukses auth
UserSession.setAppVersion(AppContext.getInstance().getVersionCode());
//Store successfully logged in user's data to GlobalData
authenticatedUser.setLogin_id(temp_loginID);
authenticatedUser.setPassword(temp_password);
String temp_uuid_user = authenticatedUser.getUuid_user();
User user = UserDataAccess.getOne(context, temp_uuid_user);
if (user != null) {
user.setLogin_id(temp_loginID);
user.setPassword(temp_password);
GlobalData.getSharedGlobalData().setUser(user);
} else {
GlobalData.getSharedGlobalData().setUser(authenticatedUser);
}
checkIsEnableLoyalty(generalParameters);
if(Global.ENABLE_USER_HELP) {
fetchUserHelp(context, generalParameters);
}
//load theme
fetchTheme(context,generalParameters,handler,null, otaLink,
needUpdatePassword, pwdExp, softUpdate,
message, GlobalData.getSharedGlobalData().getUser());
} else if (status == FORCE_UPDATE) {
handler.onForceUpdate(null, message, otaLink);
} else if (status == FORCE_CLEANSING) {
handler.onInactiveUser(null);
}
} else { //login unauthorized
handler.onLoginFail(null, message);
}
} else { //fail
handler.onConnectionFail(null, connResult);
}
}
}
private void fetchTheme(Context context, List<GeneralParameter> generalParameters,
final AuthenticationHandler handler, final Authentication auth, final String otaLink,
final boolean needUpdatePassword, final boolean pwdExp,
final boolean softUpdate, final String message, final User user) {
String themeConfigUrl = null;
if(GlobalData.getSharedGlobalData().getApplication().equalsIgnoreCase(Global.APPLICATION_SURVEY)){
themeConfigUrl = getGeneralParameter(Global.GS_THEME_CONFIG_SURVEY,generalParameters);
}
else if(GlobalData.getSharedGlobalData().getApplication().equalsIgnoreCase(Global.APPLICATION_COLLECTION)){
themeConfigUrl = getGeneralParameter(Global.GS_THEME_CONFIG_COLLECTION,generalParameters);
}
else if(GlobalData.getSharedGlobalData().getApplication().equalsIgnoreCase(Global.APPLICATION_ORDER)){
themeConfigUrl = getGeneralParameter(Global.GS_THEME_CONFIG_ORDER,generalParameters);
}
ThemeLoader themeLoader = new ThemeLoader(context);
themeLoader.loadThemeFromUrl(themeConfigUrl, new ThemeLoader.ColorSetLoaderCallback() {
@Override
public void onHasLoaded(DynamicTheme dynamicTheme) {
throw new UnsupportedOperationException();
}
@Override
public void onHasLoaded(DynamicTheme dynamicTheme, boolean needUpdate) {
handler.onLoginSuccess(auth, otaLink, needUpdatePassword, pwdExp, softUpdate, message, user);
}
});
}
private void fetchUserHelp(Context context, List<GeneralParameter> generalParameters){
String userHelpUrl = null;
String commonUserHelpUrl = getGeneralParameter(Global.GS_COMMON_USERHELP_LINK, generalParameters);
if(GlobalData.getSharedGlobalData().getApplication().equalsIgnoreCase(Global.APPLICATION_SURVEY)){
userHelpUrl = getGeneralParameter(Global.GS_MS_USERHELP_LINK,generalParameters);
}
else if(GlobalData.getSharedGlobalData().getApplication().equalsIgnoreCase(Global.APPLICATION_COLLECTION)){
userHelpUrl = getGeneralParameter(Global.GS_MC_USERHELP_LINK,generalParameters);
}
else if(GlobalData.getSharedGlobalData().getApplication().equalsIgnoreCase(Global.APPLICATION_ORDER)){
userHelpUrl = getGeneralParameter(Global.GS_MO_USERHELP_LINK,generalParameters);
}
UserHelp.initializeUserHelp(context, commonUserHelpUrl, userHelpUrl);
}
private void checkIsEnableLoyalty(List<GeneralParameter> generalParameters){
String gsCode = null;
String svyJob = null;
if(GlobalData.getSharedGlobalData().getApplication().equalsIgnoreCase(Global.APPLICATION_SURVEY)){
gsCode = Global.GS_MS_LOYALTY_ENABLED;
svyJob = getGeneralParameter("MS_JOBSVY",generalParameters);
}
else if(GlobalData.getSharedGlobalData().getApplication().equalsIgnoreCase(Global.APPLICATION_COLLECTION)){
gsCode = Global.GS_MC_LOYALTY_ENABLED;
}
String value = getGeneralParameter(gsCode,generalParameters);
if(value == null){
Global.LOYALTI_ENABLED = false;
return;
}
if(value.equals("1")){
Global.LOYALTI_ENABLED = true;
if(svyJob != null && !svyJob.isEmpty()){
svyJob = svyJob.trim();
String[] svyJobParts = svyJob.split(";");
Global.SLA_LOYALTI_JOB = svyJobParts[0].split(",");
}
}
else {
Global.LOYALTI_ENABLED = false;
}
}
//=== Setter and Getter ===//
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public AuthenticationHandler getHandler() {
return handler;
}
public void setHandler(AuthenticationHandler handler) {
this.handler = handler;
}
public boolean isEncrypt() {
return encrypt;
}
public void setEncrypt(boolean encrypt) {
this.encrypt = encrypt;
}
public boolean isDecrypt() {
return decrypt;
}
public void setDecrypt(boolean decrypt) {
this.decrypt = decrypt;
}
public static String getTemp_loginID() {
return temp_loginID;
}
/**
* Callback interface to handle authentication result. When authentication is done, user info need to be registered
* to GlobalData manually
*
* @author glen.iglesias
*/
public interface AuthenticationHandler {
/**
* Called when connection fail
*
* @param auth
* @param result HttpConnectionResult containing error code and reason phrase
*/
void onConnectionFail(Authentication auth, HttpConnectionResult result);
/**
* Called when server reject credential sent by application
*
* @param auth
* @param message message returned by the server
*/
void onLoginFail(Authentication auth, String message);
/**
* Called when server force application update
*
* @param auth
* @param message message returned by the server
* @param otaLink OTA link to download update
*/
void onForceUpdate(Authentication auth, String message, String otaLink);
/**
* Called when server report sent credential belong to inactive user
*
* @param auth
*/
void onInactiveUser(Authentication auth);
/**
* Called when server has successfully authenticate credential.
* Authenticated user need to be registered to GlobalData manually
*
* @param auth
* @param otaLink OTA link to download update, or null if no update needed
* @param needUpdatePassword flag if password change is needed
* @param message message returned by the server
* @param authenticatedUser user info which successfully authenticated with server
*/
void onLoginSuccess(Authentication auth, String otaLink, boolean needUpdatePassword, boolean pwdExp, boolean needUpdateApplication, String message, User authenticatedUser);
}
}

View file

@ -0,0 +1,25 @@
package com.adins.mss.base.authentication;
import com.adins.mss.base.BaseCommunicationModel;
/**
* JSON Format for authentication
*
* @author glen.iglesias
* @see LoginUserRequest
* @deprecated as of 17 Dec 2014, as BaseCommunicationModel
*/
public class AuthenticationModel extends BaseCommunicationModel {
protected String password;
protected boolean isFreshInstall;
// public AuthenticationModel(String userId, String password, boolean useDefault, Object additionalData, boolean freshInstall){
public AuthenticationModel(String userId, String password, boolean useDefault, boolean freshInstall) {
super(useDefault);
this.userId = userId;
this.password = password;
this.isFreshInstall = freshInstall;
}
}

View file

@ -0,0 +1,141 @@
package com.adins.mss.base.authentication;
import com.adins.mss.dao.GeneralParameter;
import com.adins.mss.dao.Menu;
import com.adins.mss.dao.User;
import java.util.List;
public class AuthenticationResultBean {
private String message;
private String otaLink;
private String lastUpdateVersion;
// private String job;
private int loginResult;
private boolean isActiveUser;
private boolean forceUpdate;
private boolean needUpdatePassword;
private User userInfo;
private List<GeneralParameter> generalParameters;
private List<Menu> listMenu;
public AuthenticationResultBean() {
// TODO Auto-generated constructor stub
}
public List<Menu> getListMenu() {
return listMenu;
}
public void setListMenu(List<Menu> listMenu) {
this.listMenu = listMenu;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getOtaLink() {
return otaLink;
}
public void setOtaLink(String otaLink) {
this.otaLink = otaLink;
}
public String getLastUpdateVersion() {
return lastUpdateVersion;
}
public void setLastUpdateVersion(String lastUpdateVersion) {
this.lastUpdateVersion = lastUpdateVersion;
}
// public String getJob() {
// return job;
// }
//
//
//
// public void setJob(String job) {
// this.job = job;
// }
public int getLoginResult() {
return loginResult;
}
public void setLoginResult(int loginResult) {
this.loginResult = loginResult;
}
public boolean isActiveUser() {
return isActiveUser;
}
public void setActiveUser(boolean isActiveUser) {
this.isActiveUser = isActiveUser;
}
public boolean isForceUpdate() {
return forceUpdate;
}
public void setForceUpdate(boolean forceUpdate) {
this.forceUpdate = forceUpdate;
}
public boolean needUpdatePassword() {
return needUpdatePassword;
}
public void setNeedUpdatePassword(boolean needUpdatePassword) {
this.needUpdatePassword = needUpdatePassword;
}
public List<GeneralParameter> getGeneralParameters() {
return generalParameters;
}
public void setGeneralParameters(List<GeneralParameter> generalParameters) {
this.generalParameters = generalParameters;
}
public User getUserInfo() {
return userInfo;
}
public void setUserInfo(User userInfo) {
this.userInfo = userInfo;
}
}

View file

@ -0,0 +1,276 @@
package com.adins.mss.base.authentication;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import androidx.fragment.app.FragmentActivity;
import androidx.core.content.FileProvider;
import android.view.View;
import android.widget.Toast;
import com.adins.mss.base.GlobalData;
import com.adins.mss.base.R;
import com.adins.mss.base.syncfile.FileSyncHelper;
import com.adins.mss.base.update.DownloadUpdate;
import com.adins.mss.base.util.GsonHelper;
import com.adins.mss.base.util.Utility;
import com.adins.mss.constant.Global;
import com.adins.mss.dao.User;
import com.adins.mss.foundation.dialog.DialogManager;
import com.adins.mss.foundation.dialog.NiftyDialogBuilder;
import com.adins.mss.foundation.http.HttpConnectionResult;
import com.adins.mss.foundation.http.MssResponseType;
import com.adins.mss.foundation.notification.Notification;
import java.io.File;
public class AuthenticationTask implements Authentication.AuthenticationHandler {
public static final String LOGIN_PREFERENES = "login_preferences";
public static onImportSuccess importSuccess;
private ProgressDialog progressDialog;
private FragmentActivity mContext;
private Activity activity;
public AuthenticationTask(FragmentActivity context) {
this.mContext = context;
Notification.getSharedNotification().clearNotifAll(mContext);
progressDialog = ProgressDialog.show(
mContext, "",
context.getString(R.string.authentication_user), true);
String username = GlobalData.getSharedGlobalData().getUser().getLogin_id();
String password = GlobalData.getSharedGlobalData().getUser().getPassword();
PackageInfo pInfo = null;
try {
pInfo = mContext.getPackageManager().getPackageInfo(mContext.getPackageName(), 0);
} catch (NameNotFoundException e) {
if (Global.IS_DEV)
e.printStackTrace();
}
int buildNumber = 0;
if(pInfo != null) {
Global.BUILD_VERSION = pInfo.versionCode;
Global.APP_VERSION = pInfo.versionName;
buildNumber = Global.BUILD_VERSION;
}
Authentication.authenticateOnBackground(
mContext,
username,
password,
buildNumber,
this);
}
public AuthenticationTask(Activity context) {
this.activity = context;
Notification.getSharedNotification().clearNotifAll(mContext);
progressDialog = ProgressDialog.show(
mContext, "",
context.getString(R.string.authentication_user), true);
String username = GlobalData.getSharedGlobalData().getUser().getLogin_id();
String password = GlobalData.getSharedGlobalData().getUser().getPassword();
PackageInfo pInfo = null;
try {
pInfo = mContext.getPackageManager().getPackageInfo(mContext.getPackageName(), 0);
} catch (NameNotFoundException e) {
if (Global.IS_DEV)
e.printStackTrace();
}
int buildNumber = 0;
if(pInfo != null){
Global.BUILD_VERSION = pInfo.versionCode;
Global.APP_VERSION = pInfo.versionName;
buildNumber = Global.BUILD_VERSION;
}
Authentication.authenticateOnBackground(
mContext,
username,
password,
buildNumber,
this);
}
@Override
public void onConnectionFail(Authentication authentication,
HttpConnectionResult httpConnectionResult) {
closeProgress();
//bong 6 may 15 - penjagaan message yang ditampilkan saat login dengan inactive user
if (authentication == null) {
if (httpConnectionResult != null) {
Toast.makeText(
mContext,
httpConnectionResult.getResult(),
Toast.LENGTH_LONG
).show();
} else {
Toast.makeText(
mContext,
mContext.getString(R.string.connection_failed),
Toast.LENGTH_LONG
).show();
}
return;
}
//bong 7 may 15 - penjagaan jika belum login ke wifi
MssResponseType responseFromServer = null;
if (httpConnectionResult.isOK()) {
responseFromServer = GsonHelper.fromJson(httpConnectionResult.getResult(), MssResponseType.class);
if (responseFromServer.getStatus().getCode() == Global.STATUS_CODE_APPL_CLEANSING) {
Toast.makeText(
mContext,
responseFromServer.getStatus().getMessage(),
Toast.LENGTH_LONG
).show();
return;
}
}
Toast.makeText(
mContext,
mContext.getString(R.string.connection_failed_http) + httpConnectionResult.getStatusCode()
+ mContext.getString(R.string.divider_vertical_bar) + httpConnectionResult.getResult(),
Toast.LENGTH_LONG
).show();
}
@Override
public void onLoginFail(Authentication auth, String message) {
closeProgress();
if (message.contains("Password") || message.contains("password") || message.contains("IMEI") || message.contains("imei")) {
DialogManager.showForceExitAlert(mContext, mContext.getString(R.string.password_not_match));
} else
Toast.makeText(mContext, message, Toast.LENGTH_LONG).show();
}
@Override
public void onForceUpdate(Authentication auth, String message,
String otaLink) {
closeProgress();
showForceUpdateDialog(otaLink);
}
private void showForceUpdateDialog(final String otaLink) {
NiftyDialogBuilder builder = NiftyDialogBuilder.getInstance(mContext)
.withTitle(mContext.getString(R.string.server))
.withMessage(mContext.getString(R.string.critical_update))
.withButton1Text(mContext.getString(R.string.update)).setButton1Click(
new View.OnClickListener() {
@Override
public void onClick(View v) {
openUpdate(otaLink);
}
}
).isCancelable(false)
.isCancelableOnTouchOutside(false);
builder.show();
}
private void openUpdate(String otaLink) {
try {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
File file = new File(mContext.getApplicationContext().getFilesDir(), "app.apk");
if (file.exists()) {
Uri apkURI = FileProvider.getUriForFile(
mContext.getApplicationContext(), mContext.getPackageName() + ".provider", file);
intent.setDataAndType(apkURI, "application/vnd.android.package-archive");
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
mContext.startActivity(intent);
} else {
DownloadUpdate downloadUpdate = new DownloadUpdate(mContext);
downloadUpdate.execute(otaLink);
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onInactiveUser(Authentication auth) {
closeProgress();
DialogManager.UninstallerHandler(mContext);
}
@Override
public void onLoginSuccess(Authentication authentication, String otaLink,
boolean needUpdatePassword, boolean pwdExp,
boolean needUpdateApplication, String message,
User authenticatedUser) {
closeProgress();
importSuccess = new onImportSuccess();
if (needUpdateApplication) {
showAskForUpdateDialog(otaLink);
return;
}
GlobalData.getSharedGlobalData().setUser(authenticatedUser);
FileSyncHelper.senderID = 1;
FileSyncHelper.startFileSync(mContext);
}
private void goToSynchronize() {
Utility.stopAllServices(mContext);
Intent syncIntent = Global.syncIntent;
mContext.startActivity(syncIntent);
mContext.finish();
}
private void showAskForUpdateDialog(final String otaLink) {
final NiftyDialogBuilder builder = NiftyDialogBuilder.getInstance(mContext);
builder.withTitle(mContext.getString(R.string.server)).isCancelableOnTouchOutside(false).isCancelable(false)
.withMessage(mContext.getString(R.string.update_available))
.withButton1Text(mContext.getString(R.string.later))
.setButton1Click(new View.OnClickListener() {
@Override
public void onClick(View v) {
builder.dismiss();
FileSyncHelper.senderID = 1;
FileSyncHelper.startFileSync(mContext);
}
})
.withButton2Text(mContext.getString(R.string.update))
.setButton2Click(new View.OnClickListener() {
@Override
public void onClick(View v) {
builder.dismiss();
openUpdate(otaLink);
}
}).show();
}
private void closeProgress() {
if (progressDialog != null && progressDialog.isShowing()) {
progressDialog.dismiss();
}
}
public class onImportSuccess extends Handler {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Bundle data = msg.getData();
boolean success = data.getBoolean("importSuccess", false);
if (success) {
goToSynchronize();
}
}
}
}

View file

@ -0,0 +1,33 @@
package com.adins.mss.base.authentication;
import com.adins.mss.dao.GeneralParameter;
import com.adins.mss.dao.User;
import com.adins.mss.foundation.http.MssResponseType;
import com.google.gson.annotations.SerializedName;
import java.util.List;
public class JsonLogin extends MssResponseType {
@SerializedName("user")
private User user;
@SerializedName("listGeneralParameter")
private List<GeneralParameter> listGeneralParameter;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public List<GeneralParameter> getListGeneralParameter() {
return listGeneralParameter;
}
public void setListGeneralParameter(List<GeneralParameter> listGeneralParameter) {
this.listGeneralParameter = listGeneralParameter;
}
}

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