diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml new file mode 100644 index 0000000..4aa03bd --- /dev/null +++ b/.github/workflows/gradle.yml @@ -0,0 +1,21 @@ +name: Java CI + +on: + push: + branches: + - main + - develop + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + - name: Set up JDK 17 + uses: actions/setup-java@v1 + with: + java-version: 17 + - name: Build with Gradle + run: ./gradlew build diff --git a/.gitignore b/.gitignore index fd45b12..46712b1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,11 @@ *.iml .gradle /local.properties -/.idea/caches/build_file_checksums.ser -/.idea/libraries -/.idea/modules.xml -/.idea/workspace.xml +/.idea .DS_Store /build /captures .externalNativeBuild +sign.jks +signing.properties +lint-baseline.xml \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 5495239..7f5bbc0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,37 +1,35 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' - -apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-kapt'//kapt 插件 +apply from: "${rootDir}/common.gradle" android { + namespace "com.wrbug.developerhelper" lintOptions { checkReleaseBuilds false abortOnError false } - signingConfigs { - releaseConfig - } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + if (isSignFileValid()) { + signingConfigs { + releaseConfig + } } - compileSdkVersion 28 + compileSdk 34 defaultConfig { applicationId "com.wrbug.developerhelper" - minSdkVersion 21 - targetSdkVersion 28 + minSdkVersion 23 + targetSdkVersion 34 versionCode 100040 versionName "1.0.4" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" ndk { - abiFilters "armeabi", "armeabi-v7a", "arm64-v8a" + abiFilters "arm64-v8a","x86_64" } } buildTypes { debug { versionNameSuffix "-debug" - if (signingConfigs.releaseConfig != null) { + if (isSignFileValid()) { signingConfig signingConfigs.releaseConfig } } @@ -40,47 +38,40 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } - dataBinding { - enabled = true - } - } dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation 'androidx.appcompat:appcompat:1.0.2' - implementation 'androidx.constraintlayout:constraintlayout:1.1.3' + implementation 'androidx.appcompat:appcompat:1.7.0' + implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + implementation 'net.dongliu:apk-parser:2.6.10' testImplementation 'junit:junit:4.12' + implementation "androidx.work:work-runtime-ktx:2.9.1" androidTestImplementation 'com.android.support.test:runner:1.0.2' - implementation 'com.google.code.gson:gson:2.8.5' + implementation 'com.google.code.gson:gson:2.10' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' - implementation 'androidx.legacy:legacy-support-v4:1.0.0' - implementation 'androidx.recyclerview:recyclerview:1.0.0' - implementation 'com.google.android.material:material:1.1.0-alpha02' - implementation 'com.github.yhaolpz:FloatWindow:1.0.9' - implementation project(':basecommon') kapt "com.android.databinding:compiler:3.1.4" implementation 'com.elvishew:xlog:1.6.1' implementation 'org.jetbrains.anko:anko-commons:0.10.8' - //dagger2 - implementation 'com.google.dagger:dagger:2.16' + implementation 'com.airbnb.android:lottie:6.5.2' // https://mvnrepository.com/artifact/dom4j/dom4j implementation 'dom4j:dom4j:1.6.1' + implementation 'com.drakeet.multitype:multitype:4.3.0' implementation 'com.evrencoskun.library:tableview:0.8.8' + implementation 'com.yanzhenjie.recyclerview:x:1.3.2' implementation 'gdut.bsx:share2:0.9.3' - kapt 'com.google.dagger:dagger-compiler:2.16' + implementation 'de.blox:graphview:0.5.0' + implementation 'com.github.bumptech.glide:glide:4.16.0' + implementation "com.google.android.material:material:1.12.0" + implementation 'com.github.vipulasri:timelineview:1.1.5' + implementation project(':ipc') implementation project(':commonutil') implementation project(':mmkv') - implementation project(':xposedmodule') - implementation project(':commonwidget') - implementation 'de.blox:graphview:0.5.0' } kapt { generateStubs = true } - Properties props = new Properties() def propFile = file('../signing.properties') if (propFile.canRead()) { @@ -100,3 +91,7 @@ if (propFile.canRead()) { println 'release build not found signing file' android.buildTypes.release.signingConfig = null } + +def isSignFileValid() { + return file('../signing.properties').canRead() +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 25f34e2..a03a058 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,14 +1,24 @@ + xmlns:tools="http://schemas.android.com/tools"> - - + + + + + + + + + @@ -32,6 +48,7 @@ @@ -45,19 +62,29 @@ - - - + android:name=".ui.activity.sharedpreferencesedit.SharedPreferenceEditActivity" + android:exported="false" /> + + + + - - - \ No newline at end of file diff --git a/app/src/main/assets/zip.dex b/app/src/main/assets/zip.dex deleted file mode 100644 index 8cb90dd..0000000 Binary files a/app/src/main/assets/zip.dex and /dev/null differ diff --git a/app/src/main/java/com/wrbug/developerhelper/BaseModule.kt b/app/src/main/java/com/wrbug/developerhelper/BaseModule.kt new file mode 100644 index 0000000..7f045cc --- /dev/null +++ b/app/src/main/java/com/wrbug/developerhelper/BaseModule.kt @@ -0,0 +1,12 @@ +package com.wrbug.developerhelper + +import android.app.Application +import com.wrbug.developerhelper.commonutil.CommonUtils +import com.wrbug.developerhelper.mmkv.manager.MMKVManager + +object BaseModule { + fun init(application: Application) { + MMKVManager.register(application) + CommonUtils.register(application) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/DeveloperApplication.kt b/app/src/main/java/com/wrbug/developerhelper/DeveloperApplication.kt index b76ab66..76b99d7 100644 --- a/app/src/main/java/com/wrbug/developerhelper/DeveloperApplication.kt +++ b/app/src/main/java/com/wrbug/developerhelper/DeveloperApplication.kt @@ -1,79 +1,87 @@ package com.wrbug.developerhelper -import android.os.Handler -import android.os.Looper +import android.app.Activity +import android.content.Context +import android.os.Bundle +import android.os.Process import com.elvishew.xlog.LogConfiguration import com.elvishew.xlog.LogLevel import com.elvishew.xlog.XLog import com.elvishew.xlog.internal.DefaultsFactory -import com.wrbug.developerhelper.basecommon.BaseApp -import com.wrbug.developerhelper.commonutil.CommonUtils -import com.wrbug.developerhelper.mmkv.manager.MMKVManager -import java.io.File -import java.io.FileOutputStream -import kotlin.concurrent.thread -import com.wrbug.developerhelper.commonwidget.flexibletoast.FlexibleToast - +import com.wrbug.developerhelper.base.BaseApp +import com.wrbug.developerhelper.commonutil.ProcessUtil +import com.wrbug.developerhelper.commonutil.print +import com.wrbug.developerhelper.ipcserver.IpcManager +import com.wrbug.developerhelper.ui.activity.main.MainActivity +import com.wrbug.developerhelper.util.AppStatusRegister class DeveloperApplication : BaseApp() { - // 全局的 handler 对象 - private val appHandler = Handler() - // 全局的 Toast 对象 - private val flexibleToast: FlexibleToast by lazy { - FlexibleToast(this) - } - private val builder: FlexibleToast.Builder by lazy { - FlexibleToast.Builder(this).setGravity(FlexibleToast.GRAVITY_BOTTOM) - } - companion object { + private lateinit var instance: DeveloperApplication fun getInstance(): DeveloperApplication { return instance } } + override fun attachBaseContext(base: Context?) { + super.attachBaseContext(base) + instance = this + } + override fun onCreate() { super.onCreate() - registerModule() - instance = this XLog.init( - LogConfiguration.Builder().logLevel(LogLevel.ALL).tag("developerHelper.print-->").build(), + LogConfiguration.Builder().logLevel(LogLevel.ALL).tag("developerHelper.print-->") + .build(), DefaultsFactory.createPrinter() ) - releaseAssetsFile() + registerIpcServer() + BaseModule.init(this) + registerLifecycle() + AppStatusRegister.init(this) } - private fun registerModule() { - MMKVManager.register(this) - CommonUtils.register(this) + private fun registerIpcServer() { + val name = ProcessUtil.readProcName(Process.myPid()) + if (name != "$packageName:floatWindow") { + "ignore registerIpcServer : $name".print() + return + } + IpcManager.init() + "registerIpcServer: ${Process.myPid()}".print() } - private fun releaseAssetsFile() { - thread { - val inputStream = BaseApp.instance.assets.open("zip.dex") - val file = File(BaseApp.instance.cacheDir, "zip.dex") - if (file.exists().not()) { - file.createNewFile() + private fun registerLifecycle() { + registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks { + private var count = 0 + override fun onActivityPaused(activity: Activity) { + } - val fileOutputStream = FileOutputStream(file) - fileOutputStream.write(inputStream.readBytes()) - fileOutputStream.flush() - fileOutputStream.close() - } - } + override fun onActivityResumed(activity: Activity) { + } - fun showToast(builder: FlexibleToast.Builder) { - if (Looper.myLooper() !== Looper.getMainLooper()) { - appHandler.post { flexibleToast.toastShow(builder) } - } else { - flexibleToast.toastShow(builder) - } - } + override fun onActivityStarted(activity: Activity) { + count++ + } + + override fun onActivityDestroyed(activity: Activity) { + } + + override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) { + } + + override fun onActivityStopped(activity: Activity) { + count-- + if (count == 0 && activity is MainActivity) { + activity.finish() + } + } + + override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) { + } - override fun showToast(msg: String) { - builder.setSecondText(msg) - showToast(builder) + }) } } diff --git a/app/src/main/java/com/wrbug/developerhelper/base/AndroidExt.kt b/app/src/main/java/com/wrbug/developerhelper/base/AndroidExt.kt new file mode 100644 index 0000000..8b0d58b --- /dev/null +++ b/app/src/main/java/com/wrbug/developerhelper/base/AndroidExt.kt @@ -0,0 +1,21 @@ +package com.wrbug.developerhelper.base + +import android.app.ActivityManager +import android.content.Context +import android.content.pm.PackageInfo +import android.os.Build + +inline val PackageInfo.versionCodeLong: Long + get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + longVersionCode + } else { + versionCode.toLong() + } + + +@Suppress("DEPRECATION") +fun isServiceRunning(context: Context, serviceClass: Class<*>): Boolean { + val manager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager + return manager.getRunningServices(Integer.MAX_VALUE) + .any { it.service.className == serviceClass.name } +} diff --git a/app/src/main/java/com/wrbug/developerhelper/base/AppCompatActivityExt.kt b/app/src/main/java/com/wrbug/developerhelper/base/AppCompatActivityExt.kt new file mode 100644 index 0000000..989ec51 --- /dev/null +++ b/app/src/main/java/com/wrbug/developerhelper/base/AppCompatActivityExt.kt @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2017 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.wrbug.developerhelper.base + +import android.Manifest +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.net.Uri +import android.os.Build +import android.os.Environment +import android.provider.Settings +import androidx.annotation.IdRes +import androidx.appcompat.app.ActionBar +import androidx.appcompat.app.AlertDialog +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.widget.Toolbar +import androidx.fragment.app.FragmentManager +import androidx.fragment.app.FragmentTransaction +import com.wrbug.developerhelper.R +import com.wrbug.developerhelper.base.activityresultcallback.ActResultRequest +import com.wrbug.developerhelper.base.activityresultcallback.ActivityResultCallback +import com.wrbug.developerhelper.constant.ReceiverConstant + +/** + * Various extension functions for AppCompatActivity. + */ + + +fun AppCompatActivity.setupActionBar(@IdRes toolbarId: Int, action: ActionBar.() -> Unit = {}) { + setupActionBar(findViewById(toolbarId), action) +} + +fun AppCompatActivity.setupActionBar(toolbar: Toolbar, action: ActionBar.() -> Unit = {}) { + setSupportActionBar(toolbar) + supportActionBar?.run { + setDisplayHomeAsUpEnabled(false) + action() + } +} + +/** + * Runs a FragmentTransaction, then calls commit(). + */ +private inline fun FragmentManager.transact(action: FragmentTransaction.() -> Unit) { + beginTransaction().apply { + action() + }.commit() +} + +fun AppCompatActivity.startActivityForResult(intent: Intent, callback: ActivityResultCallback) { + ActResultRequest(this).startForResult(intent, callback) +} + +fun AppCompatActivity.startActivityForResultOk( + intent: Intent, action: Intent?.() -> Unit +) { + ActResultRequest(this).startForResult(intent, object : ActivityResultCallback() { + override fun onActivityResultOk(data: Intent?) { + action(data) + } + }) +} + +fun Context.requestStoragePermission(callback: () -> Unit) { + if (this !is BaseActivity) { + return + } + when { + Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> { + if (!Environment.isExternalStorageManager()) { + AlertDialog.Builder(this).setTitle(R.string.notice) + .setMessage(getString(R.string.request_write_sdcard_notice)) + .setNegativeButton(R.string.cancel, null).setPositiveButton( + R.string.ok + ) { _, _ -> + try { + val intent = + Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION) + intent.addCategory("android.intent.category.DEFAULT") + intent.data = Uri.parse(String.format("package:%s", packageName)) + startActivity(intent) + } catch (e: Exception) { + val intent = Intent() + intent.action = Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION + startActivity(intent) + } + }.create().show() + } else { + callback() + } + } + + else -> { + requestPermission( + arrayOf( + Manifest.permission.WRITE_EXTERNAL_STORAGE, + Manifest.permission.READ_EXTERNAL_STORAGE + ), object : BaseActivity.PermissionCallback() { + override fun granted() { + callback() + } + }) + } + } +} + +fun Context.registerReceiverComp( + broadcastReceiver: BroadcastReceiver, + intentFilter: IntentFilter, + flags: Int = AppCompatActivity.RECEIVER_NOT_EXPORTED +) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + registerReceiver(broadcastReceiver, intentFilter, flags) + } else { + registerReceiver(broadcastReceiver, intentFilter) + } +} + +fun Context.sendBroadcastComp(action: String, intentCallback: (Intent) -> Unit = {}) { + val intent = Intent(action) + intentCallback(intent) + intent.setPackage(packageName) + sendBroadcast(intent) +} \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/base/BaseActivity.kt b/app/src/main/java/com/wrbug/developerhelper/base/BaseActivity.kt new file mode 100644 index 0000000..2894673 --- /dev/null +++ b/app/src/main/java/com/wrbug/developerhelper/base/BaseActivity.kt @@ -0,0 +1,220 @@ +package com.wrbug.developerhelper.base + +import android.content.DialogInterface +import android.content.pm.PackageManager +import android.os.Bundle +import android.view.View +import androidx.annotation.StringRes +import androidx.appcompat.app.AlertDialog +import androidx.appcompat.app.AppCompatActivity +import androidx.viewbinding.ViewBinding +import com.google.android.material.snackbar.Snackbar +import io.reactivex.rxjava3.disposables.CompositeDisposable +import java.util.ArrayList + +abstract class BaseActivity : AppCompatActivity() { + + private lateinit var toastRootView: View + protected lateinit var context: BaseActivity + private var mPermissionCallback: PermissionCallback? = null + protected lateinit var disposable: CompositeDisposable + protected val createTime by lazy { + System.currentTimeMillis() + } + + companion object { + private const val PERMISSION_REQUEST_CODE = 0xAADF1 + } + + override fun onCreate(savedInstanceState: Bundle?) { + context = this + super.onCreate(savedInstanceState) + disposable = CompositeDisposable() + } + + override fun setContentView(layoutResID: Int) { + setContentView(layoutInflater.inflate(layoutResID, null)) + } + + override fun onDestroy() { + super.onDestroy() + disposable.dispose() + } + + override fun setContentView(view: View?) { + view?.let { + toastRootView = view + } + super.setContentView(view) + } + + fun showSnack(msg: String) { + Snackbar.make(toastRootView, msg, Snackbar.LENGTH_SHORT).show() + } + + fun showSnack(id: Int) { + Snackbar.make(toastRootView, id, Snackbar.LENGTH_SHORT).show() + } + + fun requestPermission(permissions: Array, callback: PermissionCallback) { + val list = ArrayList() + for (permission in permissions) { + val hasPermission = checkSelfPermission(permission) + if (hasPermission != PackageManager.PERMISSION_GRANTED) { + list.add(permission) + } + } + if (list.isEmpty()) { + callback.granted() + return + } + mPermissionCallback = callback + requestPermissions(list.toTypedArray(), PERMISSION_REQUEST_CODE) + } + + fun showDialog( + @StringRes title: Int, + @StringRes msg: Int, + @StringRes positiveText: Int, + positiveListener: DialogInterface.OnClickListener + ) { + showDialog(title, msg, positiveText, 0, positiveListener, null) + } + + fun showDialog( + title: String, + msg: String, + positiveText: String, + positiveListener: DialogInterface.OnClickListener + ) { + showDialog(title, msg, positiveText, null, positiveListener, null) + } + + fun showDialog( + @StringRes title: Int, + @StringRes msg: Int, + @StringRes positiveText: Int, + @StringRes negativeText: Int, + onPositiveClick: DialogInterface.(Int) -> Unit, + onNegativeClick: DialogInterface.(Int) -> Unit? = { + + } + ) { + showDialog( + title, + msg, + positiveText, + negativeText, + { dialog, which -> dialog.onPositiveClick(which) }, + { dialog, which -> dialog.onNegativeClick(which) }) + } + + fun showDialog( + @StringRes title: Int, + msg: String, + @StringRes positiveText: Int, + @StringRes negativeText: Int, + onPositiveClick: DialogInterface.(Int) -> Unit, + onNegativeClick: DialogInterface.(Int) -> Unit? = { + + } + ) { + showDialog( + title, + msg, + positiveText, + negativeText, + { dialog, which -> dialog.onPositiveClick(which) }, + { dialog, which -> dialog.onNegativeClick(which) }) + } + + private fun showDialog( + @StringRes title: Int, + @StringRes msg: Int, + @StringRes positiveText: Int, + @StringRes negativeText: Int, + positiveListener: DialogInterface.OnClickListener, + negativeListener: DialogInterface.OnClickListener? + ) { + showDialog( + getString(title), + getString(msg), + getString(positiveText), + if (negativeText == 0) null else getString(negativeText), + positiveListener, + negativeListener + ) + } + + private fun showDialog( + @StringRes title: Int, + msg: String, + @StringRes positiveText: Int, + @StringRes negativeText: Int, + positiveListener: DialogInterface.OnClickListener, + negativeListener: DialogInterface.OnClickListener? + ) { + showDialog( + getString(title), + msg, + getString(positiveText), + if (negativeText == 0) null else getString(negativeText), + positiveListener, + negativeListener + ) + } + + fun showDialog( + title: String, + msg: String, + positiveText: String, + negativeText: String?, + positiveListener: DialogInterface.OnClickListener, + negativeListener: DialogInterface.OnClickListener? + ) { + val builder = AlertDialog.Builder(this).setMessage(msg).setTitle(title) + .setPositiveButton(positiveText, positiveListener) + if (negativeText.isNullOrEmpty().not()) { + builder.setNegativeButton(negativeText, negativeListener) + } + builder.show() + } + + fun hasPermission(permissions: String): Boolean { + return checkSelfPermission(permissions) == PackageManager.PERMISSION_GRANTED + } + + override fun onRequestPermissionsResult( + requestCode: Int, permissions: Array, grantResults: IntArray + ) { + if (requestCode == PERMISSION_REQUEST_CODE) { + val list = ArrayList() + for (i in grantResults.indices) { + if (grantResults[i] != PackageManager.PERMISSION_GRANTED) { + list.add(permissions[i]) + } + } + if (list.isEmpty()) { + mPermissionCallback?.granted() + return + } + mPermissionCallback?.denied(list) + return + } + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + } + + abstract class PermissionCallback { + + abstract fun granted() + + open fun denied(permissions: List) { + + } + } + + protected fun T.inject(): T { + setContentView(this.root) + return this + } +} \ No newline at end of file diff --git a/basecommon/src/main/java/com/wrbug/developerhelper/basecommon/BaseApp.kt b/app/src/main/java/com/wrbug/developerhelper/base/BaseApp.kt similarity index 59% rename from basecommon/src/main/java/com/wrbug/developerhelper/basecommon/BaseApp.kt rename to app/src/main/java/com/wrbug/developerhelper/base/BaseApp.kt index 3239594..86221e7 100644 --- a/basecommon/src/main/java/com/wrbug/developerhelper/basecommon/BaseApp.kt +++ b/app/src/main/java/com/wrbug/developerhelper/base/BaseApp.kt @@ -1,4 +1,4 @@ -package com.wrbug.developerhelper.basecommon +package com.wrbug.developerhelper.base import android.app.Application @@ -12,11 +12,4 @@ abstract class BaseApp : Application() { instance = this } - - abstract fun showToast(msg: String) - - fun showToast(id: Int){ - showToast(getString(id)) - } - } \ No newline at end of file diff --git a/basecommon/src/main/java/com/wrbug/developerhelper/basecommon/BaseFragment.kt b/app/src/main/java/com/wrbug/developerhelper/base/BaseFragment.kt similarity index 64% rename from basecommon/src/main/java/com/wrbug/developerhelper/basecommon/BaseFragment.kt rename to app/src/main/java/com/wrbug/developerhelper/base/BaseFragment.kt index 894989c..e7d2360 100644 --- a/basecommon/src/main/java/com/wrbug/developerhelper/basecommon/BaseFragment.kt +++ b/app/src/main/java/com/wrbug/developerhelper/base/BaseFragment.kt @@ -1,4 +1,4 @@ -package com.wrbug.developerhelper.basecommon +package com.wrbug.developerhelper.base import androidx.fragment.app.Fragment diff --git a/basecommon/src/main/java/com/wrbug/developerhelper/basecommon/BaseVMActivity.kt b/app/src/main/java/com/wrbug/developerhelper/base/BaseVMActivity.kt similarity index 92% rename from basecommon/src/main/java/com/wrbug/developerhelper/basecommon/BaseVMActivity.kt rename to app/src/main/java/com/wrbug/developerhelper/base/BaseVMActivity.kt index fb50ab6..51bff35 100644 --- a/basecommon/src/main/java/com/wrbug/developerhelper/basecommon/BaseVMActivity.kt +++ b/app/src/main/java/com/wrbug/developerhelper/base/BaseVMActivity.kt @@ -1,4 +1,4 @@ -package com.wrbug.developerhelper.basecommon +package com.wrbug.developerhelper.base import android.os.Bundle diff --git a/basecommon/src/main/java/com/wrbug/developerhelper/basecommon/BaseViewModel.kt b/app/src/main/java/com/wrbug/developerhelper/base/BaseViewModel.kt similarity index 90% rename from basecommon/src/main/java/com/wrbug/developerhelper/basecommon/BaseViewModel.kt rename to app/src/main/java/com/wrbug/developerhelper/base/BaseViewModel.kt index 3928d9a..a0e716a 100644 --- a/basecommon/src/main/java/com/wrbug/developerhelper/basecommon/BaseViewModel.kt +++ b/app/src/main/java/com/wrbug/developerhelper/base/BaseViewModel.kt @@ -1,4 +1,4 @@ -package com.wrbug.developerhelper.basecommon +package com.wrbug.developerhelper.base import androidx.lifecycle.LifecycleObserver import androidx.lifecycle.MutableLiveData diff --git a/app/src/main/java/com/wrbug/developerhelper/base/ExtraKey.kt b/app/src/main/java/com/wrbug/developerhelper/base/ExtraKey.kt new file mode 100644 index 0000000..bec3ba2 --- /dev/null +++ b/app/src/main/java/com/wrbug/developerhelper/base/ExtraKey.kt @@ -0,0 +1,13 @@ +package com.wrbug.developerhelper.base + +object ExtraKey { + + const val PACKAGE_NAME = "packageName" + const val DATA = "data" + const val SELECTED = "selected" + const val KEY_1 = "key1" + const val KEY_2 = "key2" + const val KEY_3 = "key3" + const val KEY_4 = "key4" + const val KEY_5 = "key5" +} \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/base/activityresultcallback/ActResultRequest.kt b/app/src/main/java/com/wrbug/developerhelper/base/activityresultcallback/ActResultRequest.kt new file mode 100644 index 0000000..8301892 --- /dev/null +++ b/app/src/main/java/com/wrbug/developerhelper/base/activityresultcallback/ActResultRequest.kt @@ -0,0 +1,55 @@ +package com.wrbug.developerhelper.base.activityresultcallback + +import android.content.Intent +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentManager + +class ActResultRequest { + private val mFragment: OnActResultEventDispatcherFragment + + constructor(activity: AppCompatActivity) { + mFragment = getEventDispatchFragment(activity) + } + + constructor(fragment: Fragment) { + mFragment = getEventDispatchFragment(fragment) + } + + + private fun getEventDispatchFragment(activity: AppCompatActivity): OnActResultEventDispatcherFragment { + val fragmentManager = activity.supportFragmentManager + return this.addFragment(fragmentManager) + } + + private fun getEventDispatchFragment(fragment: Fragment): OnActResultEventDispatcherFragment { + val fragmentManager = fragment.childFragmentManager + return this.addFragment(fragmentManager) + } + + private fun addFragment(fragmentManager: FragmentManager): OnActResultEventDispatcherFragment { + var fragment: OnActResultEventDispatcherFragment? = + this.findEventDispatchFragment(fragmentManager) + if (fragment == null) { + fragment = OnActResultEventDispatcherFragment() + fragmentManager.beginTransaction().add(fragment, "on_act_result_event_dispatcher") + .commitAllowingStateLoss() + fragmentManager.executePendingTransactions() + } + + return fragment + } + + private fun findEventDispatchFragment(manager: FragmentManager): OnActResultEventDispatcherFragment? { + return manager.findFragmentByTag("on_act_result_event_dispatcher") as OnActResultEventDispatcherFragment? + } + + fun startForResult(intent: Intent, callback: ActivityResultCallback) { + this.mFragment.startForResult(intent, null, callback) + } + + fun startForResult(intent: Intent, options: Bundle, callback: ActivityResultCallback) { + this.mFragment.startForResult(intent, options, callback) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/base/activityresultcallback/ActivityResultCallback.kt b/app/src/main/java/com/wrbug/developerhelper/base/activityresultcallback/ActivityResultCallback.kt new file mode 100644 index 0000000..42ef08b --- /dev/null +++ b/app/src/main/java/com/wrbug/developerhelper/base/activityresultcallback/ActivityResultCallback.kt @@ -0,0 +1,22 @@ +package com.wrbug.developerhelper.base.activityresultcallback + +import android.app.Activity +import android.content.Intent + +abstract class ActivityResultCallback { + + fun dispatchActivityResult(resultCode: Int, data: Intent?) { + onActivityResult(resultCode, data) + if (resultCode == Activity.RESULT_OK) { + onActivityResultOk(data) + } + } + + protected open fun onActivityResult(resultCode: Int, data: Intent?) { + + } + + protected open fun onActivityResultOk(data: Intent?) { + + } +} diff --git a/app/src/main/java/com/wrbug/developerhelper/base/activityresultcallback/OnActResultEventDispatcherFragment.kt b/app/src/main/java/com/wrbug/developerhelper/base/activityresultcallback/OnActResultEventDispatcherFragment.kt new file mode 100644 index 0000000..b44f5ba --- /dev/null +++ b/app/src/main/java/com/wrbug/developerhelper/base/activityresultcallback/OnActResultEventDispatcherFragment.kt @@ -0,0 +1,24 @@ +package com.wrbug.developerhelper.base.activityresultcallback + +import android.content.Intent +import android.os.Bundle +import android.util.SparseArray +import androidx.fragment.app.Fragment + +class OnActResultEventDispatcherFragment : Fragment() { + val TAG = "on_act_result_event_dispatcher" + private val mCallbacks = SparseArray() + + fun startForResult(intent: Intent, options: Bundle?, callback: ActivityResultCallback) { + val requestCode = callback.hashCode() and 0xffff + this.mCallbacks.put(requestCode, callback) + this.startActivityForResult(intent, requestCode, options) + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + val callback = this.mCallbacks.get(requestCode) as ActivityResultCallback + this.mCallbacks.remove(requestCode) + callback.dispatchActivityResult(resultCode, data) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/base/entry/HierarchyNode.kt b/app/src/main/java/com/wrbug/developerhelper/base/entry/HierarchyNode.kt new file mode 100644 index 0000000..0356af7 --- /dev/null +++ b/app/src/main/java/com/wrbug/developerhelper/base/entry/HierarchyNode.kt @@ -0,0 +1,33 @@ +package com.wrbug.developerhelper.base.entry + +import android.graphics.Rect +import android.os.Parcelable +import kotlinx.parcelize.Parcelize +import java.io.Serializable + +@Suppress("NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS") +@Parcelize +data class HierarchyNode( + var id: Long = -1L, + var screenBounds: Rect? = null, + var parentBounds: Rect? = null, + var checkable: Boolean = false, + var checked: Boolean = false, + var widget: String = "", + var clickable: Boolean = false, + var contentDesc: String = "", + var enabled: Boolean = false, + var focusable: Boolean = false, + var focused: Boolean = false, + var index: String = "", + var longClickable: Boolean = false, + var packageName: String = "", + var password: Boolean = false, + var scrollable: Boolean = false, + var selected: Boolean = false, + var text: String = "", + var resourceId: String = "", + var idHex: String? = null, + var parentId: Long = -1L, + var childId: ArrayList = arrayListOf() +): Parcelable, Serializable \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/constant/ReceiverConstant.kt b/app/src/main/java/com/wrbug/developerhelper/constant/ReceiverConstant.kt index 77f8af7..70b5b3d 100644 --- a/app/src/main/java/com/wrbug/developerhelper/constant/ReceiverConstant.kt +++ b/app/src/main/java/com/wrbug/developerhelper/constant/ReceiverConstant.kt @@ -5,5 +5,7 @@ object ReceiverConstant { const val ACTION_SET_FLOAT_BUTTON_VISIBLE = "ACTION_SET_FLOAT_BUTTON_VISIBLE" const val ACTION_ADB_WIFI_CLICKED = "ACTION_ADB_WIFI_CLICKED" const val ACTION_FINISH_HIERACHY_Activity = "ACTION_FINISH_HIERACHY_Activity" - const val ACTION_ACCESSIBILITY_SERVICE_STATUS_CHANGED = "ACTION_ACCESSIBILITY_SERVICE_STATUS_CHANGED" + const val ACTION_ACCESSIBILITY_SERVICE_STATUS_CHANGED = + "ACTION_ACCESSIBILITY_SERVICE_STATUS_CHANGED" + const val ACTION_DELAY_START_APP = "ACTION_DELAY_START_APP" } \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/ipcserver/FileProcessDataImpl.kt b/app/src/main/java/com/wrbug/developerhelper/ipcserver/FileProcessDataImpl.kt new file mode 100644 index 0000000..8371bf1 --- /dev/null +++ b/app/src/main/java/com/wrbug/developerhelper/ipcserver/FileProcessDataImpl.kt @@ -0,0 +1,31 @@ +package com.wrbug.developerhelper.ipcserver + +import com.wrbug.developerhelper.base.BaseApp +import com.wrbug.developerhelper.commonutil.Base64 +import com.wrbug.developerhelper.ipc.processshare.TcpUrl +import com.wrbug.developerhelper.ipcserver.annotation.Controller + + +/** + * + * class: FileProcessDataImpl.kt + * author: wrbug + * date: 2020-05-19 + * description: + * + */ +class FileProcessDataImpl { + @Controller(TcpUrl.FileProcessDataUrl.GET_DATA_FINDER_ZIP_FILE) + fun getDataFinderZipFile(): String { + val data = BaseApp.instance.assets.open("datafinder-web/web-static.zip").readBytes() + val base64 = Base64.encodeAsString(data) + return base64 + } + + @Controller(TcpUrl.FileProcessDataUrl.GET_DUMP_SO_ZIP_FILE) + fun getDumpSoZipFile(): String { + val data = BaseApp.instance.assets.open("nativeDump.zip").readBytes() + val base64 = Base64.encodeAsString(data) + return base64 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/ipcserver/IpcManager.kt b/app/src/main/java/com/wrbug/developerhelper/ipcserver/IpcManager.kt new file mode 100644 index 0000000..75a3920 --- /dev/null +++ b/app/src/main/java/com/wrbug/developerhelper/ipcserver/IpcManager.kt @@ -0,0 +1,41 @@ +package com.wrbug.developerhelper.ipcserver + +import com.wrbug.developerhelper.ipc.processshare.tcp.MessageHandler +import com.wrbug.developerhelper.ipc.processshare.tcp.TcpManager +import com.wrbug.developerhelper.ipcserver.annotation.Controller + +object IpcManager { + private val map = HashMap() + + fun init() { + registerController(FileProcessDataImpl()) + TcpManager.messageHandler = object : MessageHandler { + override fun handle(action: String, message: String): String { + if (!map.containsKey(action)) { + return "" + } + val info = map[action] ?: return "" + info.method.run { + return try { + if (parameterTypes.isEmpty()) { + invoke(info.obj)?.toString() ?: "" + } else { + invoke(info.obj, message)?.toString() ?: "" + } + } catch (t: Throwable) { + "" + } + } + } + + } + TcpManager.startServer() + } + + private fun registerController(obj: Any) { + obj.javaClass.declaredMethods.forEach { + val router = it.getAnnotation(Controller::class.java)?.value ?: return@forEach + map[router] = MethodInfo(obj, it) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/ipcserver/MethodInfo.kt b/app/src/main/java/com/wrbug/developerhelper/ipcserver/MethodInfo.kt new file mode 100644 index 0000000..f37777c --- /dev/null +++ b/app/src/main/java/com/wrbug/developerhelper/ipcserver/MethodInfo.kt @@ -0,0 +1,5 @@ +package com.wrbug.developerhelper.ipcserver + +import java.lang.reflect.Method + +internal class MethodInfo(val obj: Any, val method: Method) diff --git a/app/src/main/java/com/wrbug/developerhelper/ipcserver/annotation/Controller.kt b/app/src/main/java/com/wrbug/developerhelper/ipcserver/annotation/Controller.kt new file mode 100644 index 0000000..a231ded --- /dev/null +++ b/app/src/main/java/com/wrbug/developerhelper/ipcserver/annotation/Controller.kt @@ -0,0 +1,14 @@ +package com.wrbug.developerhelper.ipcserver.annotation + + +/** + * + * class: Controller.kt + * author: wrbug + * date: 2020-05-15 + * description: + * + */ +@Target(AnnotationTarget.FUNCTION) +@Retention(AnnotationRetention.RUNTIME) +annotation class Controller(val value: String) \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/model/entity/BackupAppData.kt b/app/src/main/java/com/wrbug/developerhelper/model/entity/BackupAppData.kt new file mode 100644 index 0000000..3c1ac5b --- /dev/null +++ b/app/src/main/java/com/wrbug/developerhelper/model/entity/BackupAppData.kt @@ -0,0 +1,13 @@ +package com.wrbug.developerhelper.model.entity + +import kotlinx.parcelize.Parcelize +import java.io.File +import java.io.Serializable + +data class BackupAppData( + val appName: String, + val packageName: String, + val rootDir: File, + val backupMap: HashMap, + val iconPath: File? +) : Serializable diff --git a/app/src/main/java/com/wrbug/developerhelper/model/entity/BackupAppInfo.kt b/app/src/main/java/com/wrbug/developerhelper/model/entity/BackupAppInfo.kt new file mode 100644 index 0000000..91db0d0 --- /dev/null +++ b/app/src/main/java/com/wrbug/developerhelper/model/entity/BackupAppInfo.kt @@ -0,0 +1,12 @@ +package com.wrbug.developerhelper.model.entity + +import com.google.gson.annotations.SerializedName + +data class BackupAppInfo( + @SerializedName("appName") + var appName: String = "", + @SerializedName("packageName") + var packageName: String = "", + @SerializedName("backupMap") + val backupMap: HashMap = hashMapOf() +) diff --git a/app/src/main/java/com/wrbug/developerhelper/model/entity/BackupAppItemInfo.kt b/app/src/main/java/com/wrbug/developerhelper/model/entity/BackupAppItemInfo.kt new file mode 100644 index 0000000..1c4fff7 --- /dev/null +++ b/app/src/main/java/com/wrbug/developerhelper/model/entity/BackupAppItemInfo.kt @@ -0,0 +1,34 @@ +package com.wrbug.developerhelper.model.entity + +import android.os.Parcelable +import com.google.gson.annotations.SerializedName +import kotlinx.parcelize.Parcelize +import java.io.Serializable + +@Parcelize +data class BackupAppItemInfo( + @SerializedName("backupFile") + var backupFile: String = "", + @SerializedName("backupApk") + var backupApk: Boolean = false, + @SerializedName("backupData") + var backupData: Boolean = false, + @SerializedName("backupAndroidData") + var backupAndroidData: Boolean = false, + @SerializedName("apkFile") + var apkFile: String = "", + @SerializedName("versionName") + var versionName: String = "", + @SerializedName("versionCode") + var versionCode: Long = 0, + @SerializedName("packageName") + var packageName: String = "", + @SerializedName("time") + var time: Long = 0, + @SerializedName("dataFile") + var dataFile: String = "", + @SerializedName("androidDataFile") + var androidDataFile: String = "", + @SerializedName("memo") + var memo: String = "" +) : Parcelable, Serializable diff --git a/app/src/main/java/com/wrbug/developerhelper/model/entity/HierarchyNode.kt b/app/src/main/java/com/wrbug/developerhelper/model/entity/HierarchyNode.kt deleted file mode 100644 index 8f11257..0000000 --- a/app/src/main/java/com/wrbug/developerhelper/model/entity/HierarchyNode.kt +++ /dev/null @@ -1,100 +0,0 @@ -package com.wrbug.developerhelper.model.entity - -import android.graphics.Rect -import android.os.Parcel -import android.os.Parcelable -import com.google.gson.reflect.TypeToken -import com.wrbug.developerhelper.commonutil.JsonHelper -import java.io.Serializable - -@Suppress("NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS") -class HierarchyNode() : Parcelable, Serializable { - var id: Long = -1L - var screenBounds: Rect? = null - var parentBounds: Rect? = null - var checkable: Boolean = false - var checked: Boolean = false - var widget: String = "" - var clickable: Boolean = false - var contentDesc: String = "" - var enabled: Boolean = false - var focusable: Boolean = false - var focused: Boolean = false - var index: String = "" - var longClickable: Boolean = false - var packageName: String = "" - var password: Boolean = false - var scrollable: Boolean = false - var selected: Boolean = false - var text: String = "" - var resourceId: String = "" - var idHex: String? = null - var parentId: Long = -1L - var childId: ArrayList = arrayListOf() - - constructor(parcel: Parcel) : this() { - id = parcel.readLong() - screenBounds = parcel.readParcelable(Rect::class.java.classLoader) - parentBounds = parcel.readParcelable(Rect::class.java.classLoader) - checkable = parcel.readByte() != 0.toByte() - checked = parcel.readByte() != 0.toByte() - widget = parcel.readString() - clickable = parcel.readByte() != 0.toByte() - contentDesc = parcel.readString() - enabled = parcel.readByte() != 0.toByte() - focusable = parcel.readByte() != 0.toByte() - focused = parcel.readByte() != 0.toByte() - index = parcel.readString() - longClickable = parcel.readByte() != 0.toByte() - packageName = parcel.readString() - password = parcel.readByte() != 0.toByte() - scrollable = parcel.readByte() != 0.toByte() - selected = parcel.readByte() != 0.toByte() - text = parcel.readString() - resourceId = parcel.readString() - idHex = parcel.readString() - parentId = parcel.readLong() - childId = JsonHelper.fromJson(parcel.readString(), object : TypeToken>() {}.type) ?: - arrayListOf() - } - - override fun writeToParcel(parcel: Parcel, flags: Int) { - parcel.writeLong(id) - parcel.writeParcelable(screenBounds, flags) - parcel.writeParcelable(parentBounds, flags) - parcel.writeByte(if (checkable) 1 else 0) - parcel.writeByte(if (checked) 1 else 0) - parcel.writeString(widget) - parcel.writeByte(if (clickable) 1 else 0) - parcel.writeString(contentDesc) - parcel.writeByte(if (enabled) 1 else 0) - parcel.writeByte(if (focusable) 1 else 0) - parcel.writeByte(if (focused) 1 else 0) - parcel.writeString(index) - parcel.writeByte(if (longClickable) 1 else 0) - parcel.writeString(packageName) - parcel.writeByte(if (password) 1 else 0) - parcel.writeByte(if (scrollable) 1 else 0) - parcel.writeByte(if (selected) 1 else 0) - parcel.writeString(text) - parcel.writeString(resourceId) - parcel.writeString(idHex) - parcel.writeLong(parentId) - parcel.writeString(JsonHelper.toJson(childId)) - } - - override fun describeContents(): Int { - return 0 - } - - companion object CREATOR : Parcelable.Creator { - override fun createFromParcel(parcel: Parcel): HierarchyNode { - return HierarchyNode(parcel) - } - - override fun newArray(size: Int): Array { - return arrayOfNulls(size) - } - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/model/entity/SharedPreferenceItemInfo.kt b/app/src/main/java/com/wrbug/developerhelper/model/entity/SharedPreferenceItemInfo.kt index 6bf2927..49859ce 100644 --- a/app/src/main/java/com/wrbug/developerhelper/model/entity/SharedPreferenceItemInfo.kt +++ b/app/src/main/java/com/wrbug/developerhelper/model/entity/SharedPreferenceItemInfo.kt @@ -10,7 +10,7 @@ class SharedPreferenceItemInfo { fun isValueValid(): Boolean { - when (type.toLowerCase()) { + when (type.lowercase()) { "string" -> { return JsonHelper.fromJson(value) == null || JsonHelper.fromJson(newValue) != null } diff --git a/app/src/main/java/com/wrbug/developerhelper/service/AccessibilityManager.kt b/app/src/main/java/com/wrbug/developerhelper/service/AccessibilityManager.kt index 418707e..3fd6bf5 100644 --- a/app/src/main/java/com/wrbug/developerhelper/service/AccessibilityManager.kt +++ b/app/src/main/java/com/wrbug/developerhelper/service/AccessibilityManager.kt @@ -4,32 +4,23 @@ import android.content.Context import android.content.Intent import android.provider.Settings import com.wrbug.developerhelper.R -import com.wrbug.developerhelper.basecommon.showToast -import com.wrbug.developerhelper.commonutil.shell.Callback import com.wrbug.developerhelper.commonutil.shell.ShellManager +import com.wrbug.developerhelper.util.getString +import io.reactivex.rxjava3.core.Single +import org.jetbrains.anko.toast object AccessibilityManager { - fun startService(context: Context?, callback: Callback? = null) { - context?.run { - ShellManager.openAccessibilityService(object : Callback { - override fun onSuccess(data: Boolean) { - if (!data) { - showToast(getString(R.string.please_open_accessbility_service)) - startAccessibilitySetting(context) - } - callback?.onSuccess(data) - } - - override fun onFailed(msg: String) { - callback?.onFailed(msg) - startAccessibilitySetting(context) - } - - }) + fun startService(context: Context?): Single { + return ShellManager.openAccessibilityService().doOnSuccess { + if (!it) { + context?.toast(getString(R.string.please_open_accessbility_service)) + } } } - fun startAccessibilitySetting(context: Context) { + + fun startAccessibilitySetting(context: Context?) { + context ?: return val intent = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) context.startActivity(intent) diff --git a/app/src/main/java/com/wrbug/developerhelper/service/DeveloperHelperAccessibilityService.kt b/app/src/main/java/com/wrbug/developerhelper/service/DeveloperHelperAccessibilityService.kt index e9cdee1..7c8e99f 100644 --- a/app/src/main/java/com/wrbug/developerhelper/service/DeveloperHelperAccessibilityService.kt +++ b/app/src/main/java/com/wrbug/developerhelper/service/DeveloperHelperAccessibilityService.kt @@ -1,27 +1,27 @@ package com.wrbug.developerhelper.service import android.accessibilityservice.AccessibilityService +import android.annotation.SuppressLint import android.content.BroadcastReceiver +import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.IntentFilter import android.graphics.Rect +import android.os.Build import android.provider.Settings import android.text.TextUtils import android.view.accessibility.AccessibilityEvent import android.view.accessibility.AccessibilityNodeInfo -import com.wrbug.developerhelper.R -import com.wrbug.developerhelper.basecommon.BaseApp -import com.wrbug.developerhelper.basecommon.showToast +import com.wrbug.developerhelper.base.BaseApp +import com.wrbug.developerhelper.base.entry.HierarchyNode +import com.wrbug.developerhelper.base.registerReceiverComp import com.wrbug.developerhelper.commonutil.AppInfoManager +import com.wrbug.developerhelper.commonutil.UiUtils import com.wrbug.developerhelper.commonutil.entity.ApkInfo import com.wrbug.developerhelper.commonutil.entity.TopActivityInfo import com.wrbug.developerhelper.constant.ReceiverConstant -import com.wrbug.developerhelper.model.entity.HierarchyNode -import com.wrbug.developerhelper.commonutil.shell.Callback -import com.wrbug.developerhelper.commonutil.shell.ShellManager import com.wrbug.developerhelper.ui.activity.hierachy.HierarchyActivity -import com.wrbug.developerhelper.commonutil.UiUtils class DeveloperHelperAccessibilityService : AccessibilityService() { @@ -29,18 +29,20 @@ class DeveloperHelperAccessibilityService : AccessibilityService() { private var nodeId = 0L private var currentAppInfo: ApkInfo? = null private var topActivity: TopActivityInfo? = null + private val activityMap = hashMapOf() companion object { internal var serviceRunning = false fun isAccessibilitySettingsOn(): Boolean { var accessibilityEnabled = 0 - val service = "com.wrbug.developerhelper/" + DeveloperHelperAccessibilityService::class.java.canonicalName + val service = + "com.wrbug.developerhelper/" + DeveloperHelperAccessibilityService::class.java.canonicalName try { accessibilityEnabled = Settings.Secure.getInt( BaseApp.instance.applicationContext.contentResolver, android.provider.Settings.Secure.ACCESSIBILITY_ENABLED ) - } catch (e: Settings.SettingNotFoundException) { + } catch (_: Settings.SettingNotFoundException) { } val mStringColonSplitter = TextUtils.SimpleStringSplitter(':') @@ -63,7 +65,6 @@ class DeveloperHelperAccessibilityService : AccessibilityService() { return false } - //todo java.lang.RuntimeException:android.os.TransactionTooLargeException val nodeMap: HashMap = hashMapOf() } @@ -71,7 +72,22 @@ class DeveloperHelperAccessibilityService : AccessibilityService() { } override fun onAccessibilityEvent(event: AccessibilityEvent?) { - + if (event?.eventType != AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED + || event.className.isNullOrEmpty() + || event.className?.startsWith("android.") == true + || event.packageName.isNullOrEmpty() + ) { + return + } + runCatching { + val info = packageManager.getActivityInfo( + ComponentName( + event.packageName.toString(), + event.className.toString() + ), 0 + ) + activityMap[info.packageName] = info.name + } } fun readNode(): ArrayList { @@ -87,6 +103,7 @@ class DeveloperHelperAccessibilityService : AccessibilityService() { return hierarchyNodes } + private fun getDecorViewNode(node: AccessibilityNodeInfo): AccessibilityNodeInfo? { for (index in 0 until node.childCount) { val child = node.getChild(index) @@ -101,16 +118,18 @@ class DeveloperHelperAccessibilityService : AccessibilityService() { return null } + @SuppressLint("UnspecifiedRegisterReceiverFlag") override fun onCreate() { super.onCreate() val filter = IntentFilter() filter.addAction(ReceiverConstant.ACTION_HIERARCHY_VIEW) - registerReceiver(receiver, filter) + registerReceiverComp(receiver, filter) sendStatusBroadcast(true) serviceRunning = true nodeMap.clear() } + override fun onDestroy() { super.onDestroy() nodeMap.clear() @@ -140,7 +159,7 @@ class DeveloperHelperAccessibilityService : AccessibilityService() { } for (index in 0 until accessibilityNodeInfo.childCount) { val child = accessibilityNodeInfo.getChild(index) - if (child.isVisibleToUser.not()) { + if (child?.isVisibleToUser != true) { continue } val screenRect = Rect() @@ -202,17 +221,11 @@ class DeveloperHelperAccessibilityService : AccessibilityService() { inner class DeveloperHelperAccessibilityReceiver : BroadcastReceiver() { - override fun onReceive(context: Context?, data: Intent?) { - showToast(getString(R.string.getting_app_info)) - ShellManager.getTopActivity(object : Callback { - - override fun onSuccess(data: TopActivityInfo?) { - topActivity = data - val nodesInfo = readNode() - HierarchyActivity.start(context, currentAppInfo, nodesInfo, topActivity) - } - }) - + override fun onReceive(context: Context, data: Intent?) { + val nodesInfo = readNode() + currentAppInfo?.topActivity = + activityMap[currentAppInfo?.packageInfo?.packageName].orEmpty() + HierarchyActivity.start(context, currentAppInfo, nodesInfo) } } diff --git a/app/src/main/java/com/wrbug/developerhelper/service/FloatWindowService.kt b/app/src/main/java/com/wrbug/developerhelper/service/FloatWindowService.kt deleted file mode 100644 index ba11265..0000000 --- a/app/src/main/java/com/wrbug/developerhelper/service/FloatWindowService.kt +++ /dev/null @@ -1,189 +0,0 @@ -package com.wrbug.developerhelper.service - -import android.app.* -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent -import android.content.IntentFilter -import android.os.Build -import android.os.IBinder -import android.view.LayoutInflater -import android.widget.RemoteViews -import androidx.core.app.NotificationCompat -import com.wrbug.developerhelper.R -import com.wrbug.developerhelper.basecommon.showToast -import com.wrbug.developerhelper.constant.ReceiverConstant -import com.wrbug.developerhelper.commonutil.shell.Callback -import com.wrbug.developerhelper.commonutil.shell.ShellManager -import com.wrbug.developerhelper.ui.activity.main.MainActivity -import com.yhao.floatwindow.FloatWindow -import com.yhao.floatwindow.Screen - - -class FloatWindowService : Service() { - - companion object { - const val FLOAT_BUTTON = "floatButton" - private const val CHANNEL_ID = "DEMON" - fun start(context: Context) { - context.startService(Intent(context, FloatWindowService::class.java)) - } - - fun stop(context: Context) { - context.stopService(Intent(context, FloatWindowService::class.java)) - } - - fun setFloatButtonVisible(context: Context, visible: Boolean) { - val intent = Intent(ReceiverConstant.ACTION_SET_FLOAT_BUTTON_VISIBLE) - intent.putExtra("visible", visible) - context.sendBroadcast(intent) - } - } - - private val floatCustomView: RemoteViews by lazy { - RemoteViews(packageName, R.layout.view_float_custom).apply { - setOnClickPendingIntent( - R.id.adbWifiContainer, PendingIntent.getBroadcast( - applicationContext, 0, - Intent(ReceiverConstant.ACTION_ADB_WIFI_CLICKED), PendingIntent.FLAG_UPDATE_CURRENT - ) - ) - } - } - private lateinit var notification: Notification - private val receiver = Receiver() - override fun onCreate() { - super.onCreate() - initReceiver() - LayoutInflater.from(this).inflate(R.layout.layout_float_window_button, null)?.let { it -> - it.setOnClickListener { - if (!DeveloperHelperAccessibilityService.isAccessibilitySettingsOn()) { - AccessibilityManager.startService(this, object : Callback { - override fun onSuccess(data: Boolean) { - if (data) { - it.postDelayed({ - sendBroadcast(Intent(ReceiverConstant.ACTION_HIERARCHY_VIEW)) - }, 500) - } - } - - }) - return@setOnClickListener - } - sendBroadcast(Intent(ReceiverConstant.ACTION_HIERARCHY_VIEW)) - } - FloatWindow - .with(applicationContext) - .setView(it) - .setWidth(Screen.width, 0.1f) //设置控件宽高 - .setHeight(Screen.width, 0.1f) - .setY(Screen.height, 0.3f) - .setTag(FLOAT_BUTTON) - .setDesktopShow(true) //桌面显示 - .build() - - - } - } - - private fun initNotification() { - val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - val channel = - NotificationChannel(CHANNEL_ID, getString(R.string.demon_process), NotificationManager.IMPORTANCE_LOW) - channel.enableLights(true) - channel.setShowBadge(true) - notificationManager.createNotificationChannel(channel) - } - val intent = Intent(this, MainActivity::class.java) - intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK - val pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT) - val builder = NotificationCompat.Builder(this, CHANNEL_ID) - .setAutoCancel(false) - .setContentIntent(pendingIntent) - .setContentTitle(getString(R.string.app_name)) - .setContentText(getString(R.string.demon_process_content)) - .setSmallIcon(R.drawable.ic_launcher_notify) - .setVibrate(null) - notification = builder.build() - notification.flags = Notification.FLAG_ONGOING_EVENT or Notification.FLAG_NO_CLEAR or - Notification.FLAG_FOREGROUND_SERVICE - updateNotification() - } - - private fun updateNotification() { - startForeground(0x10000, notification) - } - - private fun updateNotificationContent(text: String) { - floatCustomView.setTextViewText(R.id.contentTv, text) - updateNotification() - } - - private fun updateNotificationWifi(id: Int) { - floatCustomView.setImageViewResource(R.id.adbWifiIv, id) - updateNotification() - } - - private fun showFloatButton() { - FloatWindow.get(FLOAT_BUTTON).show() - } - - private fun hideFloatButton() { - FloatWindow.get(FLOAT_BUTTON).hide() - } - - private fun initReceiver() { - val filter = IntentFilter(ReceiverConstant.ACTION_SET_FLOAT_BUTTON_VISIBLE) - filter.addAction(ReceiverConstant.ACTION_ADB_WIFI_CLICKED) - registerReceiver(receiver, filter) - } - - override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { - initNotification() - showFloatButton() - return super.onStartCommand(intent, flags, startId) - } - - override fun onDestroy() { - try { - FloatWindow.destroy(FLOAT_BUTTON) - unregisterReceiver(receiver) - stopForeground(true) - } catch (th: Throwable) { - - } - - super.onDestroy() - } - - override fun onBind(intent: Intent): IBinder = null!! - - - private inner class Receiver : BroadcastReceiver() { - override fun onReceive(context: Context?, intent: Intent?) { - when (intent?.action) { - ReceiverConstant.ACTION_SET_FLOAT_BUTTON_VISIBLE -> { - val visible = intent.getBooleanExtra("visible", false) - if (visible) { - showFloatButton() - } else { - hideFloatButton() - } - } - ReceiverConstant.ACTION_ADB_WIFI_CLICKED -> { - updateNotificationContent("正在开启adb wifi") - val success = ShellManager.openAdbWifi() - if (success) { - updateNotificationContent("adb wifi 已开启") - updateNotificationWifi(R.drawable.ic_wifi_primary) - } else { - updateNotificationContent("adb wifi 开启失败") - updateNotificationWifi(R.drawable.ic_wifi_gray) - } - } - } - } - - } -} diff --git a/app/src/main/java/com/wrbug/developerhelper/service/FloatingWindowService.kt b/app/src/main/java/com/wrbug/developerhelper/service/FloatingWindowService.kt new file mode 100644 index 0000000..8fa524e --- /dev/null +++ b/app/src/main/java/com/wrbug/developerhelper/service/FloatingWindowService.kt @@ -0,0 +1,381 @@ +package com.wrbug.developerhelper.service + +import android.annotation.SuppressLint +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.PendingIntent +import android.app.Service +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.content.pm.ServiceInfo +import android.graphics.PixelFormat +import android.graphics.Point +import android.os.Build +import android.os.Handler +import android.os.IBinder +import android.os.Looper +import android.view.Gravity +import android.view.LayoutInflater +import android.view.MotionEvent +import android.view.View +import android.view.ViewConfiguration +import android.view.ViewGroup +import android.view.WindowManager +import android.widget.RemoteViews +import androidx.core.app.NotificationCompat +import androidx.core.app.ServiceCompat +import androidx.core.view.isVisible +import androidx.core.view.setPadding +import com.wrbug.developerhelper.R +import com.wrbug.developerhelper.base.ExtraKey +import com.wrbug.developerhelper.base.isServiceRunning +import com.wrbug.developerhelper.base.registerReceiverComp +import com.wrbug.developerhelper.commonutil.AppManagerUtils +import com.wrbug.developerhelper.commonutil.UiUtils +import com.wrbug.developerhelper.commonutil.addTo +import com.wrbug.developerhelper.commonutil.dpInt +import com.wrbug.developerhelper.commonutil.shell.ShellManager +import com.wrbug.developerhelper.constant.ReceiverConstant +import com.wrbug.developerhelper.ui.activity.main.MainActivity +import com.wrbug.developerhelper.util.isPortrait +import com.wrbug.developerhelper.util.setOnDoubleCheckClickListener +import io.reactivex.rxjava3.disposables.CompositeDisposable +import kotlin.math.abs + +class FloatingWindowService : Service() { + companion object { + private const val CHANNEL_ID = "FLOAT_WINDOW_DEMON" + fun start(context: Context) { + if (isServiceRunning(context, FloatingWindowService::class.java)) { + return + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + context.startForegroundService(Intent(context, FloatingWindowService::class.java)) + } else { + context.startService(Intent(context, FloatingWindowService::class.java)) + } + } + + fun stop(context: Context) { + context.stopService(Intent(context, FloatingWindowService::class.java)) + } + + fun setFloatButtonVisible(context: Context, visible: Boolean) { + val intent = Intent(ReceiverConstant.ACTION_SET_FLOAT_BUTTON_VISIBLE) + intent.setPackage(context.packageName) + intent.putExtra("visible", visible) + context.sendBroadcast(intent) + } + } + + private lateinit var windowManager: WindowManager + private lateinit var floatingButton: View + private lateinit var notification: Notification + private lateinit var params: WindowManager.LayoutParams + private var initialX = 0 + private var initialY = 0 + private var initialTouchX = 0f + private var initialTouchY = 0f + private var isDragging = false + private var screenWidth = 0 + private var screenHeight = 0 + private lateinit var disposable: CompositeDisposable + private lateinit var receiver: Receiver + + private val floatCustomView: RemoteViews by lazy { + RemoteViews(packageName, R.layout.view_float_custom).apply { + setOnClickPendingIntent( + R.id.adbWifiContainer, PendingIntent.getBroadcast( + applicationContext, + 0, + Intent(ReceiverConstant.ACTION_ADB_WIFI_CLICKED), + PendingIntent.FLAG_UPDATE_CURRENT + ) + ) + } + } + + override fun onBind(intent: Intent?): IBinder? = null + + override fun onCreate() { + super.onCreate() + disposable = CompositeDisposable() + initReceiver() + initScreenDimensions() + createFloatingButton() + initNotification() + } + + private fun initReceiver() { + receiver = Receiver() + val filter = IntentFilter(ReceiverConstant.ACTION_SET_FLOAT_BUTTON_VISIBLE) + filter.addAction(ReceiverConstant.ACTION_ADB_WIFI_CLICKED) + filter.addAction(ReceiverConstant.ACTION_DELAY_START_APP) + registerReceiverComp(receiver, filter) + } + + + private fun initScreenDimensions() { + val display = (getSystemService(WINDOW_SERVICE) as WindowManager).defaultDisplay + val size = Point() + display.getSize(size) + screenWidth = size.x + screenHeight = size.y + } + + private fun createFloatingButton() { + windowManager = getSystemService(WINDOW_SERVICE) as WindowManager + val screen = if (isPortrait()) { + UiUtils.getDeviceWidth() * 0.1 + } else { + UiUtils.getDeviceHeight() * 0.1 + }.toInt() + 20.dpInt() + floatingButton = + LayoutInflater.from(this).inflate(R.layout.layout_float_window_button, null).apply { + // 设置按钮大小 + layoutParams = ViewGroup.LayoutParams(screen, screen) + setPadding(10.dpInt()) + } + params = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + WindowManager.LayoutParams( + screen, + screen, + WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, + PixelFormat.TRANSLUCENT + ) + } else { + WindowManager.LayoutParams( + screen, + screen, + WindowManager.LayoutParams.TYPE_PHONE, + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, + PixelFormat.TRANSLUCENT + ) + } + + // 初始位置 - 右上角 + params.gravity = Gravity.TOP or Gravity.START + params.x = 0 + params.y = screenHeight / 2 + + windowManager.addView(floatingButton, params) + setupTouchEvents() + setupClickEvent() + } + + private fun setupTouchEvents() { + floatingButton.setOnTouchListener { v, event -> + when (event.action) { + MotionEvent.ACTION_DOWN -> { + initialX = params.x + initialY = params.y + initialTouchX = event.rawX + initialTouchY = event.rawY + isDragging = false + true + } + + MotionEvent.ACTION_MOVE -> { + params.x = initialX + (event.rawX - initialTouchX).toInt() + params.y = initialY + (event.rawY - initialTouchY).toInt() + + // 限制按钮不超出屏幕 + params.x = params.x.coerceIn(0, screenWidth - floatingButton.width) + params.y = params.y.coerceIn(0, screenHeight - floatingButton.height) + + windowManager.updateViewLayout(floatingButton, params) + val moveThreshold = ViewConfiguration.get(this).scaledTouchSlop + if (abs(event.rawX - initialTouchX) > moveThreshold || abs(event.rawY - initialTouchY) > moveThreshold) { + isDragging = true + } + + if (isDragging) { + params.x = initialX + (event.rawX - initialTouchX).toInt() + params.y = initialY + (event.rawY - initialTouchY).toInt() + windowManager.updateViewLayout(floatingButton, params) + true + } else { + false + } + } + + MotionEvent.ACTION_UP -> { + if (!isDragging) { + v.performClick() + true // 让点击事件处理 + } else { + snapToNearestEdge() + true + } + } + + else -> false + } + } + } + + private fun snapToNearestEdge() { + val buttonCenterX = params.x + floatingButton.width / 2 + val buttonCenterY = params.y + floatingButton.height / 2 + + // 计算到各边的距离 + val distanceToLeft = buttonCenterX + val distanceToRight = screenWidth - buttonCenterX + val distanceToTop = buttonCenterY + val distanceToBottom = screenHeight - buttonCenterY + + // 找到最小距离 + val minHorizontal = minOf(distanceToLeft, distanceToRight) + val minVertical = minOf(distanceToTop, distanceToBottom) + + // 决定吸附到哪边 + if (minHorizontal < minVertical) { + // 水平方向吸附 + params.x = + if (distanceToLeft < distanceToRight) 0 else screenWidth - floatingButton.width + } else { + // 垂直方向吸附 + params.y = + if (distanceToTop < distanceToBottom) 0 else screenHeight - floatingButton.height + } + + windowManager.updateViewLayout(floatingButton, params) + } + + private fun setupClickEvent() { + floatingButton.setOnDoubleCheckClickListener { + if (!DeveloperHelperAccessibilityService.isAccessibilitySettingsOn()) { + AccessibilityManager.startService(this).subscribe({ data -> + if (data) { + it.postDelayed({ + sendBroadcast( + Intent(ReceiverConstant.ACTION_HIERARCHY_VIEW).setPackage( + packageName + ) + ) + }, 500) + } + }, { + + }).addTo(disposable) + return@setOnDoubleCheckClickListener + } + sendBroadcast(Intent(ReceiverConstant.ACTION_HIERARCHY_VIEW).setPackage(packageName)) + } + } + + @SuppressLint("UnspecifiedImmutableFlag") + private fun initNotification() { + val notificationManager = + getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val channel = NotificationChannel( + CHANNEL_ID, getString(R.string.demon_process), NotificationManager.IMPORTANCE_LOW + ) + channel.enableLights(true) + channel.setShowBadge(true) + notificationManager.createNotificationChannel(channel) + } + val intent = Intent(this, MainActivity::class.java) + intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK + val pendingIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + PendingIntent.getActivity( + this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE + ) + } else { + PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT) + } + val builder = NotificationCompat.Builder(this, CHANNEL_ID).setAutoCancel(false) + .setContentIntent(pendingIntent).setContentTitle(getString(R.string.app_name)) + .setContentText(getString(R.string.demon_process_content)) + .setSmallIcon(R.drawable.ic_launcher_notify).setVibrate(null) + notification = builder.build() + notification.flags = + Notification.FLAG_ONGOING_EVENT or Notification.FLAG_NO_CLEAR or Notification.FLAG_FOREGROUND_SERVICE + updateNotification() + } + + + private fun updateNotification() { + val type = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC + } else { + 0 + } + runCatching { + ServiceCompat.startForeground(this, 0x10000, notification, type) + }.getOrElse { + it.printStackTrace() + } + } + + override fun onDestroy() { + super.onDestroy() + disposable.dispose() + if (::receiver.isInitialized) { + unregisterReceiver(receiver) + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + stopForeground(STOP_FOREGROUND_REMOVE) + } else { + stopForeground(true) + } + if (::floatingButton.isInitialized) { + windowManager.removeView(floatingButton) + } + } + + private fun setFloatButtonVisible(isVisible: Boolean) { + floatingButton.isVisible = isVisible + } + + private fun updateNotificationContent(text: String) { + floatCustomView.setTextViewText(R.id.contentTv, text) + updateNotification() + } + + private fun updateNotificationWifi(id: Int) { + floatCustomView.setImageViewResource(R.id.adbWifiIv, id) + updateNotification() + } + + private inner class Receiver : BroadcastReceiver() { + + override fun onReceive(context: Context?, intent: Intent?) { + when (intent?.action) { + ReceiverConstant.ACTION_SET_FLOAT_BUTTON_VISIBLE -> { + val visible = intent.getBooleanExtra("visible", false) + setFloatButtonVisible(visible) + } + + ReceiverConstant.ACTION_ADB_WIFI_CLICKED -> { + updateNotificationContent("正在开启adb wifi") + val success = ShellManager.openAdbWifi() + if (success) { + updateNotificationContent("adb wifi 已开启") + updateNotificationWifi(R.drawable.ic_wifi_primary) + } else { + updateNotificationContent("adb wifi 开启失败") + updateNotificationWifi(R.drawable.ic_wifi_gray) + } + } + + ReceiverConstant.ACTION_DELAY_START_APP -> { + val pkg = intent.getStringExtra(ExtraKey.PACKAGE_NAME) ?: return + context?.let { + Handler(Looper.getMainLooper()).post { + AppManagerUtils.startApp(it, pkg) + } + } + } + } + } + + } + + +} \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/activity/appbackup/AppBackupDetailActivity.kt b/app/src/main/java/com/wrbug/developerhelper/ui/activity/appbackup/AppBackupDetailActivity.kt new file mode 100644 index 0000000..b890f87 --- /dev/null +++ b/app/src/main/java/com/wrbug/developerhelper/ui/activity/appbackup/AppBackupDetailActivity.kt @@ -0,0 +1,161 @@ +package com.wrbug.developerhelper.ui.activity.appbackup + +import android.app.ActionBar.LayoutParams +import android.content.Context +import android.content.Intent +import android.os.Bundle +import androidx.recyclerview.widget.LinearLayoutManager +import com.wrbug.developerhelper.R +import com.wrbug.developerhelper.base.BaseActivity +import com.wrbug.developerhelper.base.ExtraKey +import com.wrbug.developerhelper.commonutil.AppInfoManager +import com.wrbug.developerhelper.commonutil.GlobalEvent +import com.wrbug.developerhelper.commonutil.addTo +import com.wrbug.developerhelper.commonutil.dpInt +import com.wrbug.developerhelper.databinding.ActivityAppBackupDetailBinding +import com.wrbug.developerhelper.model.entity.BackupAppItemInfo +import com.wrbug.developerhelper.ui.adapter.ExMultiTypeAdapter +import com.wrbug.developerhelper.ui.decoration.SpaceItemDecoration +import com.wrbug.developerhelper.util.BackupUtils +import com.yanzhenjie.recyclerview.SwipeMenuItem + +class AppBackupDetailActivity : BaseActivity() { + + companion object { + private const val REQUEST_CODE = 100 + fun start(context: Context, appName: String, packageName: String, fromAppSetting: Boolean) { + context.startActivity(Intent(context, AppBackupDetailActivity::class.java).apply { + putExtra(ExtraKey.PACKAGE_NAME, packageName) + putExtra(ExtraKey.KEY_1, appName) + putExtra(ExtraKey.KEY_2, fromAppSetting) + }) + } + } + + + private val adapter by ExMultiTypeAdapter.get() + + private val pkgName by lazy { + intent?.getStringExtra(ExtraKey.PACKAGE_NAME).orEmpty() + } + private val appName by lazy { + intent?.getStringExtra(ExtraKey.KEY_1).orEmpty() + } + private val fromAppSetting by lazy { + intent?.getBooleanExtra(ExtraKey.KEY_2, false) ?: false + } + private val binding by lazy { + ActivityAppBackupDetailBinding.inflate(layoutInflater) + } + private var listCache = arrayListOf() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(binding.root) + initView() + loadData() + } + + private fun loadData() { + adapter.showLoading() + BackupUtils.getBackupAppInfo(pkgName).subscribe({ + val list = it.backupMap.values.sortedByDescending { it.time } + listCache.clear() + listCache.addAll(list) + setupList() + }, { + adapter.showEmpty() + }).addTo(disposable) + + } + + private fun setupList() { + if (listCache.isEmpty()) { + adapter.showEmpty() + } else { + adapter.loadData(listCache) + } + } + + private fun initView() { + binding.appBar.setSubTitle(appName) + binding.rvAppBackupList.layoutManager = LinearLayoutManager(this) + binding.rvAppBackupList.addItemDecoration(SpaceItemDecoration.standard) + adapter.register(BackupDetailDelegate(appName)) + binding.rvAppBackupList.setSwipeMenuCreator { _, rightMenu, _ -> + rightMenu.addMenuItem(SwipeMenuItem(this).apply { + text = getString(R.string.item_swipe_menu_restore) + width = 56.dpInt() + height = LayoutParams.MATCH_PARENT + setTextColorResource(R.color.material_text_color_white_text) + setBackgroundColorResource(R.color.colorAccent) + }) + rightMenu.addMenuItem(SwipeMenuItem(this).apply { + text = getString(R.string.item_swipe_menu_delete) + width = 56.dpInt() + height = LayoutParams.MATCH_PARENT + setTextColorResource(R.color.material_text_color_white_text) + setBackgroundColorResource(R.color.material_color_red_600) + }) + } + binding.rvAppBackupList.setOnItemMenuClickListener { menuBridge, adapterPosition -> + menuBridge.closeMenu() + when (menuBridge.position) { + 0 -> { + recover(adapter.items[adapterPosition] as? BackupAppItemInfo) + } + + 1 -> { + showDialog( + R.string.notice, + R.string.delete_backup_notice, + R.string.ok, + R.string.cancel, + { + deleteBackup(adapter.items[adapterPosition] as? BackupAppItemInfo) + dismiss() + }, + { + dismiss() + }) + } + + else -> {} + } + } + binding.rvAppBackupList.adapter = adapter + } + + private fun recover(backupAppItemInfo: BackupAppItemInfo?) { + backupAppItemInfo ?: return + if (!AppInfoManager.isInstalled(backupAppItemInfo.packageName) && !backupAppItemInfo.backupApk) { + showSnack(getString(R.string.not_installed_and_backup_apk_notice)) + return + } + if (fromAppSetting) { + AppRecoverActivity.startForResult(this, REQUEST_CODE, appName, backupAppItemInfo) + } else { + AppRecoverActivity.start(this, appName, backupAppItemInfo) + } + } + + private fun deleteBackup(backupAppItemInfo: BackupAppItemInfo?) { + backupAppItemInfo ?: return + BackupUtils.deleteBackupItem(backupAppItemInfo.packageName, backupAppItemInfo.backupFile) + .subscribe({ + loadData() + }, { + showSnack(getString(R.string.delete_backup_failed_retry)) + }) + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + if (requestCode == REQUEST_CODE) { + if (resultCode == RESULT_OK) { + GlobalEvent.postEvent(GlobalEvent.Action.CloseAll) + finish() + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/activity/appbackup/AppRecoverActivity.kt b/app/src/main/java/com/wrbug/developerhelper/ui/activity/appbackup/AppRecoverActivity.kt new file mode 100644 index 0000000..b54f715 --- /dev/null +++ b/app/src/main/java/com/wrbug/developerhelper/ui/activity/appbackup/AppRecoverActivity.kt @@ -0,0 +1,257 @@ +package com.wrbug.developerhelper.ui.activity.appbackup + +import android.app.Activity +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.os.Bundle +import android.os.Parcelable +import androidx.core.view.isVisible +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.work.Constraints +import androidx.work.Data +import androidx.work.ExistingWorkPolicy +import androidx.work.OneTimeWorkRequestBuilder +import androidx.work.OutOfQuotaPolicy +import androidx.work.WorkManager +import com.wrbug.developerhelper.R +import com.wrbug.developerhelper.base.BaseActivity +import com.wrbug.developerhelper.base.ExtraKey +import com.wrbug.developerhelper.base.registerReceiverComp +import com.wrbug.developerhelper.base.sendBroadcastComp +import com.wrbug.developerhelper.base.versionCodeLong +import com.wrbug.developerhelper.commonutil.AppInfoManager +import com.wrbug.developerhelper.commonutil.getParcelableCompat +import com.wrbug.developerhelper.commonutil.getSerializableCompat +import com.wrbug.developerhelper.commonutil.toJson +import com.wrbug.developerhelper.constant.ReceiverConstant +import com.wrbug.developerhelper.databinding.ActivityAppRecoverBinding +import com.wrbug.developerhelper.model.entity.BackupAppItemInfo +import com.wrbug.developerhelper.ui.activity.appbackup.entity.RecoverTimeLineItem +import com.wrbug.developerhelper.ui.activity.appbackup.worker.AppRecoverWorker +import com.wrbug.developerhelper.ui.activity.appbackup.worker.AppRecoverWorkerData +import com.wrbug.developerhelper.ui.adapter.ExMultiTypeAdapter +import com.wrbug.developerhelper.util.getString +import com.wrbug.developerhelper.util.setOnDoubleCheckClickListener +import org.jetbrains.anko.toast + +class AppRecoverActivity : BaseActivity() { + + companion object { + fun start(context: Context, appName: String, backupAppItemInfo: BackupAppItemInfo) { + context.startActivity(Intent(context, AppRecoverActivity::class.java).apply { + putExtra(ExtraKey.KEY_1, backupAppItemInfo as Parcelable) + putExtra(ExtraKey.KEY_2, appName) + }) + } + + fun startForResult( + activity: Activity, + requestCode: Int, + appName: String, + backupAppItemInfo: BackupAppItemInfo + ) { + activity.startActivityForResult(Intent(activity, AppRecoverActivity::class.java).apply { + putExtra(ExtraKey.KEY_1, backupAppItemInfo as Parcelable) + putExtra(ExtraKey.KEY_2, appName) + }, requestCode) + } + } + + private val backupAppItemInfo: BackupAppItemInfo? by lazy { + intent?.getParcelableCompat(ExtraKey.KEY_1) + } + private val appName: String by lazy { + intent?.getStringExtra(ExtraKey.KEY_2).orEmpty() + } + private val apkInfo by lazy { + AppInfoManager.getAppByPackageName(backupAppItemInfo?.packageName.orEmpty()) + } + private val adapter by ExMultiTypeAdapter.get() + + private val binding by lazy { + ActivityAppRecoverBinding.inflate(layoutInflater) + } + private val receiver by lazy { + Receiver() + } + private val timeLineList: ArrayList = arrayListOf() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(binding.root) + initView() + initListener() + loadData() + } + + private fun loadData() { + if (apkInfo == null && backupAppItemInfo?.backupApk == true) { + binding.cbApk.isChecked = true + binding.cbApk.isEnabled = false + } + } + + private fun initListener() { + binding.viewCbMask.setOnDoubleCheckClickListener { } + binding.cbApk.setOnCheckedChangeListener { _, _ -> + updateItemList() + } + binding.cbData.setOnCheckedChangeListener { _, _ -> + updateItemList() + } + binding.cbAndroidData.setOnCheckedChangeListener { _, _ -> + updateItemList() + } + binding.btnRecover.setOnDoubleCheckClickListener { + if (it.tag == true) { + toast(getString(R.string.recover_success_launch_app, appName)) + setResult(RESULT_OK) + finish() + sendBroadcastComp(ReceiverConstant.ACTION_DELAY_START_APP) { + it.putExtra(ExtraKey.PACKAGE_NAME, backupAppItemInfo?.packageName) + } + return@setOnDoubleCheckClickListener + } + updateItemList() + apkVersionCheck() + } + val intentFilter = IntentFilter(AppRecoverWorker.ACTION_STEP_STATUS) + intentFilter.addAction(AppRecoverWorker.ACTION_SUCCESS) + registerReceiverComp(receiver, intentFilter) + } + + private fun apkVersionCheck() { + val backupVersionCode = backupAppItemInfo?.versionCode ?: return + val backupVersionName = backupAppItemInfo?.versionName ?: return + val appVersionCode = apkInfo?.packageInfo?.versionCodeLong ?: return + val appVersionName = apkInfo?.packageInfo?.versionName ?: return + if (!binding.cbApk.isChecked && apkInfo != null && backupVersionCode != appVersionCode) { + showDialog( + R.string.notice, + R.string.apk_version_backup_version_not_same_notice.getString( + "$appVersionName($appVersionCode)", "$backupVersionName($backupVersionCode)", + ), + R.string.ok, + R.string.cancel, + { + systemAppCheck() + dismiss() + }, + { + dismiss() + }) + return + } + systemAppCheck() + } + + private fun systemAppCheck() { + startRecover() + } + + private fun startRecover() { + val constraints = Constraints.Builder().build() + val data = Data.Builder().putString( + AppRecoverWorker.DATA, AppRecoverWorkerData( + appItemInfo = backupAppItemInfo, + appName = appName, + recoverApk = binding.cbApk.isChecked, + recoverData = binding.cbData.isChecked, + recoverAndroidData = binding.cbAndroidData.isChecked + ).toJson() + ).build() + WorkManager.getInstance(this).beginUniqueWork( + AppRecoverWorker.TAG, + ExistingWorkPolicy.REPLACE, + OneTimeWorkRequestBuilder().addTag(AppRecoverWorker.TAG) + .setInputData(data).setConstraints(constraints) + .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST).build() + ).enqueue() + binding.viewCbMask.isVisible = true + binding.btnRecover.isEnabled = false + binding.btnRecover.text = "正在恢复数据..." + } + + private fun updateItemList() { + timeLineList.clear() + if (!binding.cbApk.isChecked && !binding.cbData.isChecked && !binding.cbAndroidData.isChecked) { + binding.btnRecover.isEnabled = false + adapter.loadData(timeLineList) + return + } + binding.btnRecover.isEnabled = true + timeLineList.add(RecoverTimeLineItem("初始化准备", 1)) + if (binding.cbApk.isChecked) { + timeLineList.add( + RecoverTimeLineItem( + R.string.time_line_recover_title.getString(R.string.item_backup_apk.getString()), + 0 + ) + ) + } + if (binding.cbData.isChecked) { + timeLineList.add( + RecoverTimeLineItem( + R.string.time_line_recover_title.getString(R.string.item_backup_data.getString()), + 0 + ) + ) + } + if (binding.cbAndroidData.isChecked) { + timeLineList.add( + RecoverTimeLineItem( + R.string.time_line_recover_title.getString(R.string.item_backup_android_data.getString()), + 0 + ) + ) + } + timeLineList.last().lineType = 2 + adapter.loadData(timeLineList) + } + + private fun initView() { + binding.cbApk.isEnabled = backupAppItemInfo?.backupApk == true + binding.cbData.isEnabled = backupAppItemInfo?.backupData == true + binding.cbAndroidData.isEnabled = backupAppItemInfo?.backupAndroidData == true + binding.rvTimeLine.layoutManager = LinearLayoutManager(this) + binding.rvTimeLine.adapter = adapter + adapter.register(RecoverTimeLineDelegate()) + adapter.loadData(timeLineList) + } + + override fun onDestroy() { + super.onDestroy() + unregisterReceiver(receiver) + } + + private inner class Receiver : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent?) { + when (intent?.action) { + AppRecoverWorker.ACTION_STEP_STATUS -> { + val (step, status) = intent.getSerializableCompat>(ExtraKey.KEY_1) + ?: return + timeLineList[step].status = RecoverTimeLineItem.Status.get(status) + adapter.notifyItemChanged(step) + } + + AppRecoverWorker.ACTION_SUCCESS -> { + binding.btnRecover.isEnabled = true + binding.btnRecover.tag = true + binding.btnRecover.text = "打开 $appName" + } + + AppRecoverWorker.ACTION_FAILED -> { + binding.btnRecover.isEnabled = true + binding.viewCbMask.isVisible = false + } + + else -> { + + } + } + } + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/activity/appbackup/BackupAppActivity.kt b/app/src/main/java/com/wrbug/developerhelper/ui/activity/appbackup/BackupAppActivity.kt new file mode 100644 index 0000000..8b42d40 --- /dev/null +++ b/app/src/main/java/com/wrbug/developerhelper/ui/activity/appbackup/BackupAppActivity.kt @@ -0,0 +1,55 @@ +package com.wrbug.developerhelper.ui.activity.appbackup + +import android.os.Bundle +import androidx.recyclerview.widget.LinearLayoutManager +import com.drakeet.multitype.MultiTypeAdapter +import com.wrbug.developerhelper.R +import com.wrbug.developerhelper.base.BaseActivity +import com.wrbug.developerhelper.base.setupActionBar +import com.wrbug.developerhelper.commonutil.addTo +import com.wrbug.developerhelper.commonutil.dpInt +import com.wrbug.developerhelper.commonutil.runOnIO +import com.wrbug.developerhelper.databinding.ActivityBackupAppBinding +import com.wrbug.developerhelper.ui.adapter.ExMultiTypeAdapter +import com.wrbug.developerhelper.ui.decoration.SpaceItemDecoration +import com.wrbug.developerhelper.util.BackupUtils + +class BackupAppActivity : BaseActivity() { + private val binding by lazy { + ActivityBackupAppBinding.inflate(layoutInflater) + } + private val adapter by ExMultiTypeAdapter.get() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(binding.root) + initView() + } + + private fun initView() { + binding.rvAppList.layoutManager = LinearLayoutManager(this) + binding.rvAppList.adapter = adapter + binding.rvAppList.addItemDecoration(SpaceItemDecoration.standard) + adapter.register(BackupInfoItemDelegate { + AppBackupDetailActivity.start(this, it.appName, it.packageName, false) + }) + } + + override fun onStart() { + super.onStart() + loadData() + } + + private fun loadData() { + adapter.showLoading() + BackupUtils.getAllBackupInfo().runOnIO().subscribe({ + if (it.isEmpty()) { + adapter.showEmpty() + return@subscribe + } + adapter.loadData(it) + }, { + adapter.showEmpty() + }).addTo(disposable) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/activity/appbackup/BackupDetailDelegate.kt b/app/src/main/java/com/wrbug/developerhelper/ui/activity/appbackup/BackupDetailDelegate.kt new file mode 100644 index 0000000..fec537d --- /dev/null +++ b/app/src/main/java/com/wrbug/developerhelper/ui/activity/appbackup/BackupDetailDelegate.kt @@ -0,0 +1,53 @@ +package com.wrbug.developerhelper.ui.activity.appbackup + +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.TextView +import com.wrbug.developerhelper.R +import com.wrbug.developerhelper.commonutil.byteToShowSize +import com.wrbug.developerhelper.databinding.ItemBackupDetailInfoBinding +import com.wrbug.developerhelper.model.entity.BackupAppItemInfo +import com.wrbug.developerhelper.ui.adapter.delegate.BaseItemViewBindingDelegate +import com.wrbug.developerhelper.util.BackupUtils +import com.wrbug.developerhelper.util.format +import com.wrbug.developerhelper.util.getColor +import com.wrbug.developerhelper.util.getString +import java.io.File + +class BackupDetailDelegate(private val appName: String) : + BaseItemViewBindingDelegate() { + override fun onBindViewHolder(binding: ItemBackupDetailInfoBinding, item: BackupAppItemInfo) { + binding.tvTime.text = item.time.format() + binding.tvTitle.text = item.memo.ifEmpty { + binding.root.context.getString( + R.string.backup_default_meme, + appName + ) + } + binding.tvVersion.text = "${getFileSize(item)} | ${item.versionName}(${item.versionCode})" + binding.tvBackupApk.setStatusColor(item.backupApk) + binding.tvBackupAndroidData.setStatusColor(item.backupAndroidData) + binding.tvBackupData.setStatusColor(item.backupData) + } + + private fun getFileSize(item: BackupAppItemInfo): String { + return File(BackupUtils.getAppBackupDir(item.packageName), item.backupFile).length() + .byteToShowSize() + } + + private fun TextView.setStatusColor(enable: Boolean) { + val color = if (enable) { + R.color.material_color_green_500.getColor() + } else { + R.color.material_color_red_500.getColor() + } + setTextColor(color) + } + + override fun onCreateViewBinding( + inflater: LayoutInflater, + parent: ViewGroup + ): ItemBackupDetailInfoBinding { + return ItemBackupDetailInfoBinding.inflate(inflater, parent, false) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/activity/appbackup/BackupInfoItemDelegate.kt b/app/src/main/java/com/wrbug/developerhelper/ui/activity/appbackup/BackupInfoItemDelegate.kt new file mode 100644 index 0000000..7e7492d --- /dev/null +++ b/app/src/main/java/com/wrbug/developerhelper/ui/activity/appbackup/BackupInfoItemDelegate.kt @@ -0,0 +1,63 @@ +package com.wrbug.developerhelper.ui.activity.appbackup + +import android.view.LayoutInflater +import android.view.ViewGroup +import com.wrbug.developerhelper.R +import com.wrbug.developerhelper.commonutil.AppInfoManager +import com.wrbug.developerhelper.commonutil.SpannableBuilder +import com.wrbug.developerhelper.commonutil.byteToShowSize +import com.wrbug.developerhelper.databinding.ItemBackupAppInfoBinding +import com.wrbug.developerhelper.model.entity.BackupAppData +import com.wrbug.developerhelper.ui.adapter.delegate.BaseItemViewBindingDelegate +import com.wrbug.developerhelper.util.BackupUtils +import com.wrbug.developerhelper.util.format +import com.wrbug.developerhelper.util.getColor +import com.wrbug.developerhelper.util.getString +import com.wrbug.developerhelper.util.loadImage +import com.wrbug.developerhelper.util.setOnDoubleCheckClickListener +import java.io.File + +class BackupInfoItemDelegate(private val listener: (BackupAppData) -> Unit) : + BaseItemViewBindingDelegate() { + override fun onBindViewHolder(binding: ItemBackupAppInfoBinding, item: BackupAppData) { + val appInfo = AppInfoManager.getAppByPackageName(item.packageName) + val title = if (appInfo == null) { + val tag = R.string.backup_uninstalled_app_tag.getString() + SpannableBuilder.with(binding.root.context, tag + " " + item.appName) + .addSpanWithColor(tag, R.color.material_color_red_600.getColor()).build() + } else if (appInfo.isSystemApp()) { + val tag = R.string.backup_system_app_tag.getString() + SpannableBuilder.with(binding.root.context, tag + " " + item.appName) + .addSpanWithColor(tag, R.color.colorAccent.getColor()).build() + } else { + item.appName + } + + binding.tvAppName.text = title + binding.tvAppPackageName.text = item.packageName + val size = item.backupMap.size + val fileSize = item.backupMap.values.sumOf { + File( + BackupUtils.getAppBackupDir(it.packageName), + it.backupFile + ).length() + }.byteToShowSize() + binding.tvBackupCount.text = SpannableBuilder.with( + binding.root.context, + R.string.app_info_backup_count.getString(size, fileSize) + ).addSpanWithBold(size.toString()).addSpanWithBold(fileSize).build() + val time = item.backupMap.values.maxByOrNull { it.time }?.time ?: 0 + binding.tvLastBackupTime.text = R.string.last_backup_time.getString(time.format()) + binding.ivIcon.loadImage(item.iconPath, R.drawable.ic_default_app_ico_place_holder) + binding.root.setOnDoubleCheckClickListener { + listener(item) + } + } + + override fun onCreateViewBinding( + inflater: LayoutInflater, + parent: ViewGroup + ): ItemBackupAppInfoBinding { + return ItemBackupAppInfoBinding.inflate(inflater, parent, false) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/activity/appbackup/RecoverTimeLineDelegate.kt b/app/src/main/java/com/wrbug/developerhelper/ui/activity/appbackup/RecoverTimeLineDelegate.kt new file mode 100644 index 0000000..956eb05 --- /dev/null +++ b/app/src/main/java/com/wrbug/developerhelper/ui/activity/appbackup/RecoverTimeLineDelegate.kt @@ -0,0 +1,73 @@ +package com.wrbug.developerhelper.ui.activity.appbackup + +import android.graphics.PorterDuff +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat +import com.github.vipulasri.timelineview.TimelineView +import com.wrbug.developerhelper.R +import com.wrbug.developerhelper.databinding.ItemRecoverAppTimeLineBinding +import com.wrbug.developerhelper.ui.activity.appbackup.entity.RecoverTimeLineItem +import com.wrbug.developerhelper.ui.adapter.delegate.BaseItemViewBindingDelegate +import com.wrbug.developerhelper.util.getColor + +class RecoverTimeLineDelegate : + BaseItemViewBindingDelegate() { + override fun onBindViewHolder( + binding: ItemRecoverAppTimeLineBinding, + item: RecoverTimeLineItem + ) { + binding.textTimelineTitle.text = item.title + binding.timeMarker.initLine(item.lineType) + when (item.status) { + RecoverTimeLineItem.Status.Pending -> { + binding.textTimelineContent.text = "等待中" + binding.timeMarker.setMarker( + R.drawable.ic_marker_inactive, + R.color.material_color_grey_500 + ) + } + + RecoverTimeLineItem.Status.Running -> { + binding.textTimelineContent.text = "运行中" + binding.timeMarker.setMarker( + R.drawable.ic_marker_active, + R.color.colorPrimary + ) + } + + RecoverTimeLineItem.Status.Done -> { + binding.textTimelineContent.text = "已完成" + binding.timeMarker.setMarker( + R.drawable.ic_marker_active, + R.color.colorAccent + ) + } + + RecoverTimeLineItem.Status.Failed -> { + binding.textTimelineContent.text = "恢复失败" + binding.timeMarker.setMarker( + R.drawable.ic_marker_active, + R.color.material_color_red_300 + ) + } + } + } + + override fun onCreateViewBinding( + inflater: LayoutInflater, + parent: ViewGroup + ): ItemRecoverAppTimeLineBinding { + return ItemRecoverAppTimeLineBinding.inflate(inflater, parent, false) + } + + private fun TimelineView.setMarker(drawableResId: Int, colorFilter: Int) { + marker = VectorDrawableCompat.create( + context.resources, + drawableResId, + context.theme + )?.apply { + setTint(colorFilter.getColor()) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/activity/appbackup/entity/RecoverTimeLineItem.kt b/app/src/main/java/com/wrbug/developerhelper/ui/activity/appbackup/entity/RecoverTimeLineItem.kt new file mode 100644 index 0000000..624041a --- /dev/null +++ b/app/src/main/java/com/wrbug/developerhelper/ui/activity/appbackup/entity/RecoverTimeLineItem.kt @@ -0,0 +1,17 @@ +package com.wrbug.developerhelper.ui.activity.appbackup.entity + +data class RecoverTimeLineItem( + val title: String, + var lineType: Int, + var status: Status = Status.Pending +) { + enum class Status(val status: Int) { + Pending(-1), Running(1), Done(0), Failed(2); + + companion object { + fun get(status: Int): Status { + return entries.find { it.status == status } ?: Pending + } + } + } +} diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/activity/appbackup/worker/AppRecoverWorker.kt b/app/src/main/java/com/wrbug/developerhelper/ui/activity/appbackup/worker/AppRecoverWorker.kt new file mode 100644 index 0000000..ce86f28 --- /dev/null +++ b/app/src/main/java/com/wrbug/developerhelper/ui/activity/appbackup/worker/AppRecoverWorker.kt @@ -0,0 +1,166 @@ +package com.wrbug.developerhelper.ui.activity.appbackup.worker + +import android.content.Context +import android.content.Intent +import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC +import androidx.work.CoroutineWorker +import androidx.work.ForegroundInfo +import androidx.work.WorkerParameters +import com.wrbug.developerhelper.R +import com.wrbug.developerhelper.base.ExtraKey +import com.wrbug.developerhelper.base.sendBroadcastComp +import com.wrbug.developerhelper.commonutil.AppInfoManager +import com.wrbug.developerhelper.commonutil.AppManagerUtils +import com.wrbug.developerhelper.commonutil.Constant +import com.wrbug.developerhelper.commonutil.createNotification +import com.wrbug.developerhelper.commonutil.fromJson +import com.wrbug.developerhelper.commonutil.shell.ShellManager +import com.wrbug.developerhelper.ui.activity.appbackup.entity.RecoverTimeLineItem +import com.wrbug.developerhelper.util.BackupUtils +import com.wrbug.developerhelper.util.getString +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import java.io.File + +class AppRecoverWorker(appContext: Context, params: WorkerParameters) : + CoroutineWorker(appContext, params) { + + private val data: AppRecoverWorkerData? by lazy { + params.inputData.getString(DATA)?.fromJson() + } + + private val workDir by lazy { + BackupUtils.getAppBackupDir(data?.appItemInfo?.packageName.orEmpty()).absolutePath + } + private val tmpDir by lazy { + File(workDir, "tmp").absolutePath + } + + private var step = 0 + + companion object { + const val ACTION_STEP_STATUS = "ACTION_STEP_STATUS" + const val ACTION_SUCCESS = "ACTION_SUCCESS" + const val ACTION_FAILED = "ACTION_FAILED" + const val DATA = "data" + private const val CHANNEL_ID = "APP_RECOVER_DEMON" + const val TAG = "AppRecoverWorker" + private const val NOTIFICATION_ID = 1234 + } + + private val foregroundInfo = ForegroundInfo( + NOTIFICATION_ID, createNotification(applicationContext, CHANNEL_ID, "recover") { + setContentTitle(R.string.app_name.getString()) + setSmallIcon(R.drawable.ic_launcher_notify) + setContentText(R.string.recovering_app_data.getString(data?.appName.orEmpty())) + }, FOREGROUND_SERVICE_TYPE_DATA_SYNC + ) + + override suspend fun doWork(): Result { + setForeground(foregroundInfo) + return withContext(Dispatchers.IO) { + step = 0 + if (!prepareExecute()) { + sendStatus(RecoverTimeLineItem.Status.Failed) + sendComplete(false) + return@withContext Result.success() + } + sendStatus(RecoverTimeLineItem.Status.Done) + if (!recoverApk()) { + sendStatus(RecoverTimeLineItem.Status.Failed) + sendComplete(false) + return@withContext Result.success() + } + sendStatus(RecoverTimeLineItem.Status.Done) + if (!recoverData()) { + sendStatus(RecoverTimeLineItem.Status.Failed) + sendComplete(false) + return@withContext Result.success() + } + sendStatus(RecoverTimeLineItem.Status.Done) + if (!recoverAndroidData()) { + sendStatus(RecoverTimeLineItem.Status.Failed) + sendComplete(false) + return@withContext Result.success() + } + sendStatus(RecoverTimeLineItem.Status.Done) + sendComplete(true) + Result.success() + } + } + + private fun recoverAndroidData(): Boolean { + if (data?.recoverAndroidData != true) { + return true + } + step++ + sendStatus(RecoverTimeLineItem.Status.Running) + val androidDataDir = "/sdcard/Android/data/" + data?.appItemInfo?.packageName + if (ShellManager.mkDir(androidDataDir)) { + return false + } + return ShellManager.tarXF(tmpDir + "/" + data?.appItemInfo?.androidDataFile, androidDataDir) + } + + private fun recoverData(): Boolean { + if (data?.recoverData != true) { + return true + } + step++ + sendStatus(RecoverTimeLineItem.Status.Running) + val dataDir = Constant.getDataDir(data?.appItemInfo?.packageName.orEmpty()) + val map = ShellManager.getDataDirUserAndGroup(dataDir) + if (!ShellManager.tarXF(tmpDir + "/" + data?.appItemInfo?.dataFile, "$dataDir/")) { + return false + } + return ShellManager.chownDataDir(dataDir, map) + } + + private fun recoverApk(): Boolean { + if (data?.recoverApk != true) { + return true + } + step++ + sendStatus(RecoverTimeLineItem.Status.Running) + if (AppInfoManager.isInstalled(data?.appItemInfo?.packageName.orEmpty())) { + val result = ShellManager.uninstallApk(data?.appItemInfo?.packageName.orEmpty()) + if (!result) { + return false + } + } + val tmpApk = "/data/local/tmp/" + data?.appItemInfo?.packageName + ".apk" + if (!ShellManager.cpFile(tmpDir + "/" + data?.appItemInfo?.apkFile, tmpApk)) { + return false + } + return ShellManager.installApk(tmpApk) + } + + private fun prepareExecute(): Boolean { + sendStatus(RecoverTimeLineItem.Status.Running) + AppManagerUtils.forceStopApp(data?.appItemInfo?.packageName.orEmpty()) + if (!ShellManager.rmFile(tmpDir)) { + return false + } + if (!ShellManager.mkDir(tmpDir)) { + return false + } + return ShellManager.tarXF( + File(workDir, data?.appItemInfo?.backupFile.orEmpty()).absolutePath, tmpDir + ) + } + + + override suspend fun getForegroundInfo(): ForegroundInfo { + return foregroundInfo + } + + private fun sendStatus(status: RecoverTimeLineItem.Status) { + applicationContext.sendBroadcastComp(ACTION_STEP_STATUS) { + it.putExtra(ExtraKey.KEY_1, step to status.status) + } + } + + private fun sendComplete(success: Boolean) { + applicationContext.sendBroadcastComp(if (success) ACTION_SUCCESS else ACTION_FAILED) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/activity/appbackup/worker/AppRecoverWorkerData.kt b/app/src/main/java/com/wrbug/developerhelper/ui/activity/appbackup/worker/AppRecoverWorkerData.kt new file mode 100644 index 0000000..befe60f --- /dev/null +++ b/app/src/main/java/com/wrbug/developerhelper/ui/activity/appbackup/worker/AppRecoverWorkerData.kt @@ -0,0 +1,14 @@ +package com.wrbug.developerhelper.ui.activity.appbackup.worker + +import android.os.Parcelable +import com.wrbug.developerhelper.model.entity.BackupAppItemInfo +import kotlinx.parcelize.Parcelize + +@Parcelize +class AppRecoverWorkerData( + val appItemInfo: BackupAppItemInfo?, + val appName: String, + val recoverApk: Boolean, + val recoverData: Boolean, + val recoverAndroidData: Boolean +) : Parcelable diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/activity/databaseedit/DatabaseEditActivity.kt b/app/src/main/java/com/wrbug/developerhelper/ui/activity/databaseedit/DatabaseEditActivity.kt index cdfe075..0aafbf5 100644 --- a/app/src/main/java/com/wrbug/developerhelper/ui/activity/databaseedit/DatabaseEditActivity.kt +++ b/app/src/main/java/com/wrbug/developerhelper/ui/activity/databaseedit/DatabaseEditActivity.kt @@ -10,23 +10,24 @@ import android.widget.TextView import androidx.recyclerview.widget.RecyclerView import com.evrencoskun.tableview.listener.ITableViewListener import com.wrbug.developerhelper.R -import com.wrbug.developerhelper.basecommon.BaseActivity -import com.wrbug.developerhelper.basecommon.setupActionBar -import com.wrbug.developerhelper.basecommon.showToast +import com.wrbug.developerhelper.base.* import com.wrbug.developerhelper.model.entity.DatabaseTableInfo import com.wrbug.developerhelper.commonutil.shell.ShellManager import com.wrbug.developerhelper.util.DatabaseUtils import com.wrbug.developerhelper.commonutil.dp2px -import kotlinx.android.synthetic.main.activity_database_edit.* +import com.wrbug.developerhelper.databinding.ActivityDatabaseEditBinding import org.jetbrains.anko.doAsync +import org.jetbrains.anko.toast import org.jetbrains.anko.uiThread import java.io.File import java.util.* import kotlin.collections.ArrayList -class DatabaseEditActivity : BaseActivity() { +class DatabaseEditActivity: BaseActivity() { + private var filePath: String? = "" private lateinit var dbPath: File + private lateinit var dataBinding: ActivityDatabaseEditBinding private val tableNames = ArrayList() private var dbMap: Map = TreeMap() private val adapter = DatabaseTableAdapter(this) @@ -40,6 +41,7 @@ class DatabaseEditActivity : BaseActivity() { } companion object { + fun start(context: Context, filePath: String) { val intent = Intent(context, DatabaseEditActivity::class.java) intent.putExtra("filePath", filePath) @@ -49,12 +51,13 @@ class DatabaseEditActivity : BaseActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_database_edit) + dataBinding = ActivityDatabaseEditBinding.inflate(layoutInflater) + setContentView(dataBinding.root) intent?.run { filePath = getStringExtra("filePath") } if (filePath.isNullOrEmpty()) { - showToast(getString(R.string.get_database_failed)) + toast(getString(R.string.get_database_failed)) finish() return } @@ -67,8 +70,8 @@ class DatabaseEditActivity : BaseActivity() { } private fun initTableView() { - tableView.adapter = adapter - tableView.tableViewListener = object : ITableViewListener { + dataBinding.tableView.adapter = adapter + dataBinding.tableView.tableViewListener = object: ITableViewListener { override fun onCellLongPressed(p0: RecyclerView.ViewHolder, p1: Int, p2: Int) { } @@ -96,10 +99,11 @@ class DatabaseEditActivity : BaseActivity() { if (dstDir.exists().not()) { dstDir.mkdir() } - val success = ShellManager.cpFile(dbPath.absolutePath, "${dstDir.absolutePath}/${dbPath.name}") + val success = + ShellManager.cpFile(dbPath.absolutePath, "${dstDir.absolutePath}/${dbPath.name}") if (!success) { uiThread { - showToast(getString(R.string.get_database_failed)) + toast(getString(R.string.get_database_failed)) finish() } return@doAsync @@ -115,11 +119,13 @@ class DatabaseEditActivity : BaseActivity() { } } - private fun setTableContainer() { - tableNameContainer.removeAllViews() + dataBinding.tableNameContainer.removeAllViews() val layoutParams = - LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT) + LinearLayout.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) val dp10 = dp2px(10F) val dp20 = dp2px(20F) tableNames.forEachIndexed { index, name -> @@ -135,7 +141,7 @@ class DatabaseEditActivity : BaseActivity() { tv.setOnClickListener { selectTable(it.tag as Int) } - tableNameContainer.addView(tv, layoutParams) + dataBinding.tableNameContainer.addView(tv, layoutParams) } } @@ -161,9 +167,9 @@ class DatabaseEditActivity : BaseActivity() { uiThread { setHasData(!cell.isEmpty()) adapter.setAllItems(keyList, rowHeaders, cell) - var tv = tableNameContainer.getChildAt(selectedIndex) as TextView + var tv = dataBinding.tableNameContainer.getChildAt(selectedIndex) as TextView tv.setTextColor(resources.getColor(R.color.text_color_666666)) - tv = tableNameContainer.getChildAt(position) as TextView + tv = dataBinding.tableNameContainer.getChildAt(position) as TextView tv.setTextColor(resources.getColor(R.color.colorPrimary)) selectedIndex = position } @@ -172,7 +178,7 @@ class DatabaseEditActivity : BaseActivity() { } private fun setHasData(hasData: Boolean) { - noDataTv.visibility = if (hasData) View.GONE else View.VISIBLE - tableView.visibility = if (hasData) View.VISIBLE else View.GONE + dataBinding.noDataTv.visibility = if (hasData) View.GONE else View.VISIBLE + dataBinding.tableView.visibility = if (hasData) View.VISIBLE else View.GONE } } diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/activity/databaseedit/DatabaseTableAdapter.kt b/app/src/main/java/com/wrbug/developerhelper/ui/activity/databaseedit/DatabaseTableAdapter.kt index 9ee8514..680b799 100644 --- a/app/src/main/java/com/wrbug/developerhelper/ui/activity/databaseedit/DatabaseTableAdapter.kt +++ b/app/src/main/java/com/wrbug/developerhelper/ui/activity/databaseedit/DatabaseTableAdapter.kt @@ -1,5 +1,6 @@ package com.wrbug.developerhelper.ui.activity.databaseedit +import android.annotation.SuppressLint import android.content.Context import android.view.View import android.view.ViewGroup @@ -55,6 +56,7 @@ class DatabaseTableAdapter(val context: Context) : AbstractTableAdapter? = null - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_guide) - mSectionsPagerAdapter = SectionsPagerAdapter(supportFragmentManager) - viewPager.adapter = mSectionsPagerAdapter - viewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener { - override fun onPageSelected(position: Int) { - currentPosition = position - updateIndicators(position) - viewPager.setBackgroundColor(bgColors[position]) - buttonPre.visibility = if (position == 0) View.GONE else View.VISIBLE - buttonNext.visibility = if (position == 2) View.GONE else View.VISIBLE - buttonFinish.visibility = if (position == 2) View.VISIBLE else View.GONE - } - - override fun onPageScrollStateChanged(position: Int) { - - } - - override fun onPageScrolled(p0: Int, p1: Float, p2: Int) { - val colorUpdate = ArgbEvaluator().evaluate( - p1, - bgColors[p0], - bgColors[if (p0 == 2) p0 else p0 + 1] - ) as Int - viewPager.setBackgroundColor(colorUpdate) - } - - }) - indicators = arrayOf(imageViewIndicator0 as View, imageViewIndicator1 as View, imageViewIndicator2 as View) - } - - private fun updateIndicators(position: Int) { - for (i in 0 until indicators?.size!!) { - indicators?.get(i)?.setBackgroundResource( - if (i == position) R.drawable.onboarding_indicator_selected else R.drawable.onboarding_indicator_unselected - ) - } - } -} diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/activity/guide/GuideStepAccessibilityFragment.kt b/app/src/main/java/com/wrbug/developerhelper/ui/activity/guide/GuideStepAccessibilityFragment.kt deleted file mode 100644 index 1466d40..0000000 --- a/app/src/main/java/com/wrbug/developerhelper/ui/activity/guide/GuideStepAccessibilityFragment.kt +++ /dev/null @@ -1,45 +0,0 @@ -package com.wrbug.developerhelper.ui.activity.guide - -import android.content.Intent -import android.os.Bundle -import android.provider.Settings -import android.view.View -import com.wrbug.developerhelper.R -import com.wrbug.developerhelper.service.AccessibilityManager -import com.wrbug.developerhelper.service.DeveloperHelperAccessibilityService -import kotlinx.android.synthetic.main.fragment_guide.* - -class GuideStepAccessibilityFragment : GuideStepFragment() { - override fun getLabelText(): String { - return "无障碍功能" - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - checkIsOpen() - icoIv.setImageResource(R.drawable.ic_accessibility_black) - contentTv.setOnClickListener { - if (DeveloperHelperAccessibilityService.serviceRunning) { - return@setOnClickListener - } - AccessibilityManager.startService(activity) - - } - } - - private fun checkIsOpen() { - contentTv.text = - if (DeveloperHelperAccessibilityService.serviceRunning) "无障碍辅助已开启" else "无障碍辅助已关闭,将无法分析布局和页面信息,点击开启" - } - - override fun onResume() { - super.onResume() - checkIsOpen() - } - - companion object { - fun instance(): GuideStepAccessibilityFragment { - val fragment = GuideStepAccessibilityFragment() - return fragment - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/activity/guide/GuideStepFloatWindowFragment.kt b/app/src/main/java/com/wrbug/developerhelper/ui/activity/guide/GuideStepFloatWindowFragment.kt deleted file mode 100644 index 3e5fd57..0000000 --- a/app/src/main/java/com/wrbug/developerhelper/ui/activity/guide/GuideStepFloatWindowFragment.kt +++ /dev/null @@ -1,57 +0,0 @@ -package com.wrbug.developerhelper.ui.activity.guide - -import android.content.Intent -import android.net.Uri -import android.os.Bundle -import android.provider.Settings -import android.view.View -import com.wrbug.developerhelper.R -import com.wrbug.developerhelper.service.FloatWindowService -import com.wrbug.developerhelper.util.DeviceUtils -import kotlinx.android.synthetic.main.fragment_guide.* - -class GuideStepFloatWindowFragment : GuideStepFragment() { - override fun getLabelText(): String { - return getString(R.string.float_window_permission) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - icoIv.setImageResource(R.drawable.ic_float_air_bubble) - contentTv.setOnClickListener { - if (DeviceUtils.isFloatWindowOpened(activity!!)) { - return@setOnClickListener - } - startActivityForResult( - Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:${activity?.packageName}")), - 0 - ) - } - } - - override fun onResume() { - super.onResume() - checkFloatWindow() - } - - private fun checkFloatWindow() { - if (activity != null && DeviceUtils.isFloatWindowOpened(activity!!)) { - contentTv.text = getString(R.string.float_window_opened) - FloatWindowService.start(activity!!) - } else { - contentTv.text = getString(R.string.float_window_closed) - } - } - - companion object { - fun instance(): GuideStepFloatWindowFragment { - val fragment = GuideStepFloatWindowFragment() - return fragment - } - } - - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - if (requestCode == 0) { - checkFloatWindow() - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/activity/guide/GuideStepFragment.kt b/app/src/main/java/com/wrbug/developerhelper/ui/activity/guide/GuideStepFragment.kt deleted file mode 100644 index 772c1d3..0000000 --- a/app/src/main/java/com/wrbug/developerhelper/ui/activity/guide/GuideStepFragment.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.wrbug.developerhelper.ui.activity.guide - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import com.wrbug.developerhelper.R -import com.wrbug.developerhelper.basecommon.BaseFragment -import kotlinx.android.synthetic.main.fragment_guide.view.* - - - abstract class GuideStepFragment : BaseFragment() { - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - val rootView = inflater.inflate(R.layout.fragment_guide, container, false) - rootView.labelTv.text = getLabelText() - return rootView - } - abstract fun getLabelText(): String -} \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/activity/guide/SectionsPagerAdapter.kt b/app/src/main/java/com/wrbug/developerhelper/ui/activity/guide/SectionsPagerAdapter.kt deleted file mode 100644 index 9515409..0000000 --- a/app/src/main/java/com/wrbug/developerhelper/ui/activity/guide/SectionsPagerAdapter.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.wrbug.developerhelper.ui.activity.guide - -import androidx.fragment.app.Fragment -import androidx.fragment.app.FragmentManager -import androidx.fragment.app.FragmentPagerAdapter - -class SectionsPagerAdapter(fm: FragmentManager) : FragmentPagerAdapter(fm) { - private val fragments = arrayOf( - GuideStepFloatWindowFragment.instance(), - GuideStepAccessibilityFragment.instance() - ) - - override fun getItem(position: Int): Fragment { - return fragments[position] - } - - override fun getCount(): Int { - return fragments.size - } -} \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/activity/hierachy/AppInfoDialog.kt b/app/src/main/java/com/wrbug/developerhelper/ui/activity/hierachy/AppInfoDialog.kt index 4d4e06e..0e85d4c 100644 --- a/app/src/main/java/com/wrbug/developerhelper/ui/activity/hierachy/AppInfoDialog.kt +++ b/app/src/main/java/com/wrbug/developerhelper/ui/activity/hierachy/AppInfoDialog.kt @@ -9,64 +9,74 @@ import android.view.ViewGroup import androidx.fragment.app.DialogFragment import com.wrbug.developerhelper.R import com.wrbug.developerhelper.commonutil.entity.ApkInfo -import com.wrbug.developerhelper.commonutil.entity.TopActivityInfo import com.wrbug.developerhelper.commonutil.UiUtils import com.wrbug.developerhelper.commonutil.dp2px -import kotlinx.android.synthetic.main.dialog_apk_info.* +import com.wrbug.developerhelper.databinding.DialogApkInfoBinding +import com.wrbug.developerhelper.util.isPortrait +import io.reactivex.rxjava3.disposables.CompositeDisposable class AppInfoDialog : DialogFragment() { - private var apkInfo: ApkInfo? = null - private var topActivity: TopActivityInfo? = null + + private val apkInfo: ApkInfo? by lazy { + arguments?.getParcelable("apkInfo") + } private var listener: AppInfoDialogEventListener? = null + private lateinit var binding: DialogApkInfoBinding + private lateinit var disposable: CompositeDisposable + private val pagerAdapter by lazy { + AppInfoPagerAdapter(this, disposable) + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setStyle(DialogFragment.STYLE_NORMAL, R.style.FullScreenDialog) - arguments?.let { - apkInfo = it.getParcelable("apkInfo") - topActivity = it.getParcelable("topActivity") - } + setStyle(STYLE_NORMAL, R.style.FullScreenDialog) } - override fun onAttach(activity: Activity?) { + override fun onAttach(activity: Activity) { super.onAttach(activity) if (activity is AppInfoDialogEventListener) { listener = activity } } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - val view = inflater.inflate(R.layout.dialog_apk_info, container, false) - dialog.window.takeUnless { - it == null - }?.run { + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View { + disposable = CompositeDisposable() + binding = DialogApkInfoBinding.inflate(inflater, container, false) + dialog?.window?.run { val layoutParams = attributes - layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT - layoutParams.height = UiUtils.getDeviceHeight() / 2 + dp2px(40F) + if (isPortrait()) { + layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT + layoutParams.height = UiUtils.getDeviceHeight() / 2 + dp2px(40F) + setGravity(Gravity.TOP) + } else { + layoutParams.width = UiUtils.getDeviceWidth() / 2 + layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT + setGravity(Gravity.END) + } attributes = layoutParams - setGravity(Gravity.TOP) } - return view + return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - titleContainer.setPadding(0, UiUtils.getStatusHeight(), 0, 0) - activity?.let { - val pagerAdapter = AppInfoPagerAdapter(this, apkInfo, topActivity) - pagerAdapter.listener = listener - viewPager.adapter = pagerAdapter - tabLayout.setupWithViewPager(viewPager) - } - apkInfo?.let { it -> - logoIv.setImageDrawable(it.getIco()) - titleTv.text = it.getAppName() - subTitleTv.text = it.applicationInfo.packageName + binding.titleContainer.setPadding(0, UiUtils.getStatusHeight(), 0, 0) + pagerAdapter.listener = listener + binding.viewPager.adapter = pagerAdapter + binding.tabLayout.setupWithViewPager(binding.viewPager) + apkInfo?.let { + binding.logoIv.setImageDrawable(it.getIco()) + binding.titleTv.text = it.getAppName() + binding.subTitleTv.text = it.applicationInfo.packageName } + pagerAdapter.loadData(apkInfo) } - override fun onDestroyView() { - super.onDestroyView() listener?.close() + disposable.dispose() + super.onDestroyView() } } \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/activity/hierachy/AppInfoPagerAdapter.kt b/app/src/main/java/com/wrbug/developerhelper/ui/activity/hierachy/AppInfoPagerAdapter.kt index a118cb8..44079d1 100644 --- a/app/src/main/java/com/wrbug/developerhelper/ui/activity/hierachy/AppInfoPagerAdapter.kt +++ b/app/src/main/java/com/wrbug/developerhelper/ui/activity/hierachy/AppInfoPagerAdapter.kt @@ -3,61 +3,71 @@ package com.wrbug.developerhelper.ui.activity.hierachy import android.content.Context import android.view.View import android.view.ViewGroup +import androidx.annotation.Keep import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.viewpager.widget.PagerAdapter import com.wrbug.developerhelper.R +import com.wrbug.developerhelper.base.versionCodeLong import com.wrbug.developerhelper.commonutil.entity.ApkInfo -import com.wrbug.developerhelper.commonutil.entity.TopActivityInfo import com.wrbug.developerhelper.commonutil.shell.ShellManager import com.wrbug.developerhelper.ui.decoration.SpaceItemDecoration import com.wrbug.developerhelper.ui.widget.appdatainfoview.AppDataInfoView import com.wrbug.developerhelper.ui.widget.appsettingview.AppSettingView import com.wrbug.developerhelper.ui.widget.layoutinfoview.infopage.InfoAdapter import com.wrbug.developerhelper.ui.widget.layoutinfoview.infopage.ItemInfo -import com.wrbug.developerhelper.util.EnforceUtils import com.wrbug.developerhelper.commonutil.UiUtils import com.wrbug.developerhelper.util.format import com.wrbug.developerhelper.util.getString -import org.jetbrains.anko.doAsync -import org.jetbrains.anko.uiThread +import io.reactivex.rxjava3.disposables.CompositeDisposable import java.util.ArrayList +@Keep class AppInfoPagerAdapter( - private val dialog: AppInfoDialog, - private val apkInfo: ApkInfo?, - private val topActivity: TopActivityInfo? -) : - PagerAdapter() { - private val context: Context = dialog.activity!! + private val dialog: AppInfoDialog, private val disposable: CompositeDisposable +) : PagerAdapter() { + + private val context: Context = dialog.requireContext() private val tabList = arrayListOf() private val viewList = arrayListOf() - private val adapter = InfoAdapter(context) - private val enforceItem = ItemInfo(context.getString(R.string.enforce_type), context.getString(R.string.analyzing)) + private val adapter by lazy { + InfoAdapter(context, analyzeItem) + } var listener: AppInfoDialogEventListener? = null - private val itemInfos = ArrayList() + private val itemInfos = ArrayList() + private val analyzeItem by lazy { + ItemInfo(getString(R.string.page_analyze), getString(R.string.click_to_analyze)).apply { + showCopy = false + textColor = context.resources.getColor(R.color.colorPrimaryDark) + setOnClickListener(View.OnClickListener { + listener?.showHierachyView() + dialog.dismissAllowingStateLoss() + }) + } + } - init { - initAppInfoTab() - initAppDataInfoTab() - initAppSettingTab() + fun loadData(apkInfo: ApkInfo?) { + initAppInfoTab(apkInfo) + initAppDataInfoTab(apkInfo) + initAppSettingTab(apkInfo) + notifyDataSetChanged() } - private fun initAppSettingTab() { + private fun initAppSettingTab(apkInfo: ApkInfo?) { tabList.add(context.getString(R.string.app_setting)) val view = AppSettingView(context) - view.apkInfo = apkInfo + view.setApkInfo(apkInfo) viewList.add(view) } - private fun initAppDataInfoTab() { + private fun initAppDataInfoTab(apkInfo: ApkInfo?) { tabList.add(context.getString(R.string.data_info)) val appDataInfoView = AppDataInfoView(context) viewList.add(appDataInfoView) appDataInfoView.apkInfo = apkInfo } - private fun initAppInfoTab() { + private fun initAppInfoTab(apkInfo: ApkInfo?) { tabList.add(context.getString(R.string.base_info)) val rv = RecyclerView(context) viewList.add(rv) @@ -68,64 +78,31 @@ class AppInfoPagerAdapter( itemDecoration.setFirstTopPadding(UiUtils.dp2px(context, 10F)) rv.addItemDecoration(itemDecoration) apkInfo?.let { it -> - val item = ItemInfo(getString(R.string.page_analyze), getString(R.string.click_to_analyze)) - item.setOnClickListener(View.OnClickListener { - listener?.showHierachyView() - dialog.dismissAllowingStateLoss() - }) - itemInfos.add(item) - itemInfos.add(ItemInfo("VersionCode", it.packageInfo.versionCode)) - itemInfos.add(ItemInfo("VersionName", it.packageInfo.versionName)) - topActivity?.let { - itemInfos.add(ItemInfo("Activity", it.activity)) - it.fragments.takeUnless { fragments -> - fragments.isNullOrEmpty() - }?.forEach { - if (it.hidden.not()) { - itemInfos.add(ItemInfo("Fragment", it.name)) - } - } - } + itemInfos.add(ItemInfo("PackageName", it.packageInfo.packageName)) it.applicationInfo.className?.let { name -> itemInfos.add(ItemInfo("Application", name)) } - itemInfos.add(enforceItem) + it.topActivity.takeIf { it.isNotEmpty() }?.let { + itemInfos.add(ItemInfo("Activity", it)) + } + itemInfos.add(ItemInfo("VersionName", it.packageInfo.versionName)) + itemInfos.add(ItemInfo("VersionCode", it.packageInfo.versionCodeLong)) itemInfos.add(ItemInfo("uid", it.applicationInfo.uid)) - ShellManager.getPid(it.packageInfo.packageName).takeUnless { - it.isEmpty() - }?.let { + ShellManager.getPid(it.packageInfo.packageName).takeUnless { it.isEmpty() }?.let { itemInfos.add(ItemInfo("Pid", it)) } itemInfos.add( ItemInfo( - getString(R.string.first_install_time), - it.packageInfo.firstInstallTime.format() + getString(R.string.first_install_time), it.packageInfo.firstInstallTime.format() ) ) itemInfos.add( ItemInfo( - getString(R.string.last_update_time), - it.packageInfo.lastUpdateTime.format() + getString(R.string.last_update_time), it.packageInfo.lastUpdateTime.format() ) ) itemInfos.add(ItemInfo("DataDir", it.applicationInfo.dataDir)) adapter.setItems(itemInfos) - getEnforce(it.packageInfo.packageName) - } - } - - private fun setEnforceType(type: EnforceUtils.EnforceType) { - enforceItem.content = type.type - } - - private fun getEnforce(packageName: String) { - doAsync { - val type = EnforceUtils.getEnforceType(packageName) - uiThread { - setEnforceType(type) - adapter.notifyItemChanged(enforceItem) - } - } } @@ -137,7 +114,6 @@ class AppInfoPagerAdapter( return tabList.size } - override fun instantiateItem(container: ViewGroup, position: Int): Any { container.addView(viewList[position]) return viewList[position] diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/activity/hierachy/HierarchyActivity.kt b/app/src/main/java/com/wrbug/developerhelper/ui/activity/hierachy/HierarchyActivity.kt index eed2cf4..5aaea85 100644 --- a/app/src/main/java/com/wrbug/developerhelper/ui/activity/hierachy/HierarchyActivity.kt +++ b/app/src/main/java/com/wrbug/developerhelper/ui/activity/hierachy/HierarchyActivity.kt @@ -7,44 +7,39 @@ import android.content.Intent import android.content.IntentFilter import android.os.Bundle import android.view.View -import com.google.gson.reflect.TypeToken -import com.wrbug.developerhelper.R -import com.wrbug.developerhelper.basecommon.BaseActivity -import com.wrbug.developerhelper.constant.ReceiverConstant +import com.wrbug.developerhelper.base.BaseActivity +import com.wrbug.developerhelper.base.entry.HierarchyNode +import com.wrbug.developerhelper.base.registerReceiverComp +import com.wrbug.developerhelper.commonutil.GlobalEvent import com.wrbug.developerhelper.commonutil.entity.ApkInfo -import com.wrbug.developerhelper.model.entity.HierarchyNode -import com.wrbug.developerhelper.commonutil.entity.TopActivityInfo -import com.wrbug.developerhelper.ui.widget.hierarchyView.HierarchyView -import com.wrbug.developerhelper.commonutil.JsonHelper import com.wrbug.developerhelper.constant.ReceiverConstant.ACTION_FINISH_HIERACHY_Activity -import com.wrbug.developerhelper.service.FloatWindowService -import com.wrbug.developerhelper.ui.widget.layoutinfoview.LayoutInfoView -import com.wrbug.developerhelper.ui.widget.layoutinfoview.OnNodeChangedListener -import kotlinx.android.synthetic.main.activity_hierarchy.* +import com.wrbug.developerhelper.databinding.ActivityHierarchyBinding +import com.wrbug.developerhelper.service.FloatingWindowService +import com.wrbug.developerhelper.ui.widget.hierarchyView.HierarchyView +import com.wrbug.developerhelper.ui.widget.layoutinfoview.LayoutInfoDialog import java.lang.ref.WeakReference -class HierarchyActivity : BaseActivity(), AppInfoDialogEventListener, OnNodeChangedListener { - +class HierarchyActivity : BaseActivity(), AppInfoDialogEventListener { - private var apkInfo: ApkInfo? = null - private var nodeList: List? = null - private var nodeMap: HashMap? = null + private val apkInfo: ApkInfo? by lazy { + intent?.getParcelableExtra("apkInfo") + } + private val nodeList: ArrayList? by lazy { + intent?.getParcelableArrayListExtra("node") + } private var showHierachyView = false - private var topActivity: TopActivityInfo? = null + private lateinit var binding: ActivityHierarchyBinding companion object { + fun start( - context: Context?, - apkInfo: ApkInfo?, - node: ArrayList?, - topActivity: TopActivityInfo? + context: Context?, apkInfo: ApkInfo?, node: ArrayList ) { val intent = Intent(context, HierarchyActivity::class.java) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) val bundle = Bundle() bundle.putParcelable("apkInfo", apkInfo) bundle.putParcelableArrayList("node", node) - bundle.putParcelable("topActivity", topActivity) intent.putExtras(bundle) context?.startActivity(intent) } @@ -57,7 +52,7 @@ class HierarchyActivity : BaseActivity(), AppInfoDialogEventListener, OnNodeChan override fun onReceive(context: Context?, intent: Intent?) { when (intent?.action) { - ReceiverConstant.ACTION_FINISH_HIERACHY_Activity -> { + ACTION_FINISH_HIERACHY_Activity -> { reference?.get()?.finish() } @@ -69,56 +64,70 @@ class HierarchyActivity : BaseActivity(), AppInfoDialogEventListener, OnNodeChan override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_hierarchy) - intent?.run { - apkInfo = getParcelableExtra("apkInfo") - nodeList = getParcelableArrayListExtra("node") - topActivity = getParcelableExtra("topActivity") - val json = getStringExtra("nodeMap") - nodeMap = JsonHelper.fromJson(json, object : TypeToken>() {}.type) - } + binding = ActivityHierarchyBinding.inflate(layoutInflater) + setContentView(binding.root) + checkNodeList() val filter = IntentFilter(ACTION_FINISH_HIERACHY_Activity) receiver.setActivity(this) - registerReceiver(receiver, filter) + GlobalEvent.register(GlobalEvent.Action.CloseAll, createTime) { + finish() + } + registerReceiverComp(receiver, filter) showAppInfoDialog() - FloatWindowService.setFloatButtonVisible(this, false) + FloatingWindowService.setFloatButtonVisible(this, false) + } + + private fun checkNodeList() { + nodeList ?: return + if ((nodeList?.size ?: 0) <= 1) { + return + } + var hierarchyNode: HierarchyNode? = null + nodeList?.forEach { + if (hierarchyNode == null) { + hierarchyNode = it + } else if (hierarchyNode?.screenBounds?.let { it1 -> it.screenBounds?.contains(it1) } == true) { + hierarchyNode = it + } + } + nodeList?.clear() + hierarchyNode?.let { + nodeList?.add(it) + } } private fun showAppInfoDialog() { val dialog = AppInfoDialog() val bundle = Bundle() bundle.putParcelable("apkInfo", apkInfo) - bundle.putParcelable("topActivity", topActivity) dialog.arguments = bundle dialog.show(supportFragmentManager, "") } override fun showHierachyView() { showHierachyView = true - hierarchyView.setHierarchyNodes(nodeList) - hierarchyView.setOnHierarchyNodeClickListener(object : HierarchyView.OnHierarchyNodeClickListener { + binding.hierarchyView.setHierarchyNodes(nodeList) + binding.hierarchyView.setOnHierarchyNodeClickListener(object : + HierarchyView.OnHierarchyNodeClickListener { override fun onClick(node: HierarchyNode, parentNode: HierarchyNode?) { - hierarchyDetailView.visibility = View.VISIBLE - hierarchyDetailView.setNode(node, parentNode) - val layoutInfoView = LayoutInfoView(context, nodeList, node) - layoutInfoView.setOnNodeChangedListener(this@HierarchyActivity) - layoutInfoView.show() + binding.hierarchyDetailView.visibility = View.VISIBLE + binding.hierarchyDetailView.setNode(node, parentNode) + LayoutInfoDialog.show(supportFragmentManager, nodeList, node) { node, parentNode -> + binding.hierarchyDetailView.setNode(node, parentNode) + } } override fun onSelectedNodeChanged(node: HierarchyNode, parentNode: HierarchyNode?) { - hierarchyDetailView.visibility = View.VISIBLE - hierarchyDetailView.setNode(node, parentNode) + binding.hierarchyDetailView.visibility = View.VISIBLE + binding.hierarchyDetailView.setNode(node, parentNode) } }) } - override fun onChanged(node: HierarchyNode, parentNode: HierarchyNode?) { - hierarchyDetailView.setNode(node, parentNode) - } - override fun onDestroy() { - FloatWindowService.setFloatButtonVisible(this, true) + FloatingWindowService.setFloatButtonVisible(this, true) unregisterReceiver(receiver) + GlobalEvent.unRegister(GlobalEvent.Action.CloseAll, createTime) super.onDestroy() } @@ -129,12 +138,17 @@ class HierarchyActivity : BaseActivity(), AppInfoDialogEventListener, OnNodeChan } override fun onBackPressed() { - if (hierarchyDetailView.visibility == View.VISIBLE) { - hierarchyDetailView.visibility = View.GONE + if (binding.hierarchyDetailView.visibility == View.VISIBLE) { + binding.hierarchyDetailView.visibility = View.GONE + return + } + if (showHierachyView) { + showHierachyView = false + binding.hierarchyView.visibility = View.GONE + showAppInfoDialog() return } super.onBackPressed() } - } diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/activity/main/MainActivity.kt b/app/src/main/java/com/wrbug/developerhelper/ui/activity/main/MainActivity.kt index 41f1901..598ceb2 100644 --- a/app/src/main/java/com/wrbug/developerhelper/ui/activity/main/MainActivity.kt +++ b/app/src/main/java/com/wrbug/developerhelper/ui/activity/main/MainActivity.kt @@ -1,200 +1,220 @@ package com.wrbug.developerhelper.ui.activity.main -import android.content.* +import android.Manifest +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter import android.net.Uri +import android.os.Build import android.os.Bundle import android.provider.Settings import android.view.Menu import android.view.MenuItem -import android.widget.CompoundButton import androidx.appcompat.app.AlertDialog -import androidx.databinding.DataBindingUtil -import com.wrbug.developerhelper.BuildConfig +import androidx.core.view.isVisible import com.wrbug.developerhelper.R -import com.wrbug.developerhelper.basecommon.BaseVMActivity -import com.wrbug.developerhelper.basecommon.obtainViewModel -import com.wrbug.developerhelper.basecommon.setupActionBar -import com.wrbug.developerhelper.basecommon.showToast +import com.wrbug.developerhelper.base.BaseActivity +import com.wrbug.developerhelper.base.registerReceiverComp +import com.wrbug.developerhelper.base.requestStoragePermission +import com.wrbug.developerhelper.base.setupActionBar import com.wrbug.developerhelper.commonutil.ClipboardUtils -import com.wrbug.developerhelper.commonutil.shell.Callback +import com.wrbug.developerhelper.commonutil.shell.ShellManager +import com.wrbug.developerhelper.util.setOnDoubleCheckClickListener import com.wrbug.developerhelper.constant.ReceiverConstant import com.wrbug.developerhelper.databinding.ActivityMainBinding +import com.wrbug.developerhelper.mmkv.ConfigKv +import com.wrbug.developerhelper.mmkv.manager.MMKVManager +import com.wrbug.developerhelper.model.entity.VersionInfo import com.wrbug.developerhelper.service.AccessibilityManager -import com.wrbug.developerhelper.service.FloatWindowService -import com.wrbug.developerhelper.commonutil.shell.ShellManager -import com.wrbug.developerhelper.ui.activity.main.viewmodel.MainViewModel -import com.wrbug.developerhelper.ui.widget.settingitemview.SettingItemView +import com.wrbug.developerhelper.service.DeveloperHelperAccessibilityService +import com.wrbug.developerhelper.service.FloatingWindowService +import com.wrbug.developerhelper.ui.activity.appbackup.BackupAppActivity import com.wrbug.developerhelper.util.DeviceUtils -import com.wrbug.developerhelper.commonutil.toInt -import com.wrbug.developerhelper.model.entity.VersionInfo -import com.wrbug.developerhelper.ui.activity.xposed.xposedsetting.XposedSettingActivity -import com.wrbug.developerhelper.util.UpdateUtils -import kotlinx.android.synthetic.main.activity_main.* - - -class MainActivity : BaseVMActivity() { +class MainActivity : BaseActivity() { + private val configKv: ConfigKv = MMKVManager.get(ConfigKv::class.java) + private val binding: ActivityMainBinding by lazy { + ActivityMainBinding.inflate(layoutInflater) + } - lateinit var binding: ActivityMainBinding - lateinit var xposedSettingView: SettingItemView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) if (DeviceUtils.isFloatWindowOpened()) { - FloatWindowService.start(this) - "".toInt() + FloatingWindowService.start(this) } - binding = DataBindingUtil.setContentView(this, R.layout.activity_main) - binding.presenter = Presenter() - xposedSettingView = binding.xposedSettingView - binding.mainVm = vm - setupActionBar(R.id.toolbar) { - - } - + setContentView(binding.root) + setupActionBar(R.id.toolbar) ShellManager.openAccessibilityService() + initView() initListener() val filter = IntentFilter(ReceiverConstant.ACTION_ACCESSIBILITY_SERVICE_STATUS_CHANGED) - registerReceiver(receiver, filter) + registerReceiverComp(receiver, filter) + } + + private fun initView() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + binding.notificationSettingView.checkable = false + binding.notificationSettingView.isVisible = true + binding.notificationSettingView.checked = + hasPermission(Manifest.permission.POST_NOTIFICATIONS) + } else { + binding.notificationSettingView.isVisible = false + } } private fun initListener() { - floatWindowSettingView.setOnCheckedChangeListener(CompoundButton.OnCheckedChangeListener { _, isChecked -> + binding.floatWindowSettingView.setOnCheckedChangeListener { _, isChecked -> if (isChecked && DeviceUtils.isFloatWindowOpened()) { - FloatWindowService.start(this) + FloatingWindowService.start(this) } else { - FloatWindowService.stop(this) + FloatingWindowService.stop(this) } - }) - rootSettingView.setOnCheckedChangeListener(CompoundButton.OnCheckedChangeListener { _, _ -> - - }) - xposedSettingView.setOnClickListener { - if (xposedSettingView.isChecked().not()) { - showSnack(getString(R.string.open_xposed_first)) - return@setOnClickListener + } + binding.backupAppSettingView.setOnDoubleCheckClickListener { + requestStoragePermission { + startActivity(Intent(this, BackupAppActivity::class.java)) } - startActivity(Intent(this, XposedSettingActivity::class.java)) } - } - - override fun getViewModel(): MainViewModel { - return obtainViewModel(MainViewModel::class.java) - } - - override fun onDestroy() { - super.onDestroy() - unregisterReceiver(receiver) - } - - inner class Presenter { - fun onAccessibilityClick() { - if (!accessibilitySettingView.checked) { + binding.accessibilitySettingView.setOnDoubleCheckClickListener { + if (!binding.accessibilitySettingView.checked) { AccessibilityManager.startAccessibilitySetting(context) } else { showSnack(getString(R.string.accessibility_service_opened)) } } - - fun onFloatWindowClick() { - if (!floatWindowSettingView.checked) { + binding.floatWindowSettingView.setOnDoubleCheckClickListener { + if (!binding.floatWindowSettingView.checked) { startActivityForResult( - Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:$packageName")), - 0 + Intent( + Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:$packageName") + ), 0 ) } else { showSnack(getString(R.string.float_window_opened)) } } + binding.rootSettingView.setOnDoubleCheckClickListener { + if (!DeviceUtils.isRoot()) { + showSnack(R.string.devices_is_not_root) + return@setOnDoubleCheckClickListener + } + val isRootEnable = binding.rootSettingView.isChecked() + binding.rootSettingView.checked = !isRootEnable + configKv.setOpenRoot(!isRootEnable) + } + binding.notificationSettingView.setOnSwitcherClickListener { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + requestPermission(arrayOf(Manifest.permission.POST_NOTIFICATIONS), + object : PermissionCallback() { + override fun granted() { + binding.notificationSettingView.checked = true + } + }) + } + } + } + - fun onRootClick() { - vm.toggleRootPermission() + override fun onResume() { + super.onResume() + checkStatus() + } + + + private fun checkStatus() { + binding.accessibilitySettingView.checked = + DeveloperHelperAccessibilityService.serviceRunning + binding.floatWindowSettingView.checked = DeviceUtils.isFloatWindowOpened() + if (configKv.isOpenRoot()) { + if (DeviceUtils.isRoot()) { + binding.rootSettingView.checked = true + } else { + binding.rootSettingView.checked = false + configKv.setOpenRoot(false) + } + } + if (DeviceUtils.isFloatWindowOpened()) { + FloatingWindowService.start(application) } } + override fun onDestroy() { + super.onDestroy() + unregisterReceiver(receiver) + } + override fun onCreateOptionsMenu(menu: Menu?): Boolean { menuInflater.inflate(R.menu.menu_main, menu) return super.onCreateOptionsMenu(menu) } - override fun onOptionsItemSelected(item: MenuItem?): Boolean { - item?.let { - when (it.itemId) { - R.id.about_menu -> { - showAboutDialog() - } - R.id.exit_menu -> { - showExitMenuDialog() - } + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + R.id.about_menu -> { + showAboutDialog() } - } + R.id.exit_menu -> { + showExitMenuDialog() + } + } return super.onOptionsItemSelected(item) } - private fun showExitMenuDialog() = AlertDialog.Builder(this) - .setTitle(R.string.notice) + private fun showExitMenuDialog() = AlertDialog.Builder(this).setTitle(R.string.notice) .setMessage(getString(R.string.exit_content)) .setPositiveButton(getString(R.string.ok)) { _, _ -> - FloatWindowService.stop(this) + FloatingWindowService.stop(this) finish() - } - .setNegativeButton(getString(R.string.cancel), null) - .create() - .show() + }.setNegativeButton(getString(R.string.cancel), null).create().show() - private fun showAboutDialog() = AlertDialog.Builder(this) - .setTitle(R.string.about) + private fun showAboutDialog() = AlertDialog.Builder(this).setTitle(R.string.about) .setMessage(getString(R.string.about_content)) .setPositiveButton(getString(R.string.copy_group_number)) { _, _ -> ClipboardUtils.saveClipboardText(this, "627962572") showSnack(R.string.copy_success) - } - .setNeutralButton("检查更新") { _, _ -> + }.setNeutralButton(getString(R.string.check_update)) { _, _ -> checkUpdate(true) - } - .create().show() - - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - - } + }.create().show() private fun checkUpdate(showSnack: Boolean = false) { - if (showSnack) { - showSnack("正在检查新版本...") - } - UpdateUtils.checkUpdate(object : Callback { - override fun onSuccess(data: VersionInfo) { - if (BuildConfig.VERSION_NAME == data.versionName) { - showSnack("暂无新版本") - return - } - showUpdateDialog(data) - } - - override fun onFailed(msg: String) { - if (showSnack) { - showSnack("检查失败...") - } - } - }) +// if (showSnack) { +// showSnack(getString(R.string.checking_update)) +// } +// UpdateUtils.checkUpdate(object : Callback { +// override fun onSuccess(data: VersionInfo) { +// if (BuildConfig.VERSION_NAME == data.versionName) { +// showSnack(getString(R.string.no_new_version)) +// return +// } +// showUpdateDialog(data) +// } +// +// override fun onFailed(msg: String) { +// if (showSnack) { +// showSnack(getString(R.string.check_update_failed)) +// } +// } +// }) } - private fun showUpdateDialog(data: VersionInfo) = AlertDialog.Builder(this) - .setTitle("发现新版本") - .setMessage("版本号:${data.versionName}\n更新时间:${data.updateDate}\n大小:${data.size}\n版本说明:\n${data.feature}") - .setPositiveButton("下载") { _, _ -> - val intent = Intent(Intent.ACTION_VIEW) - val uri = Uri.parse(data.downloadUrl) - intent.data = uri - startActivity(intent) - } - .create().show() + private fun showUpdateDialog(data: VersionInfo) = + AlertDialog.Builder(this).setTitle(getString(R.string.find_new_version)) + .setMessage("版本号:${data.versionName}\n更新时间:${data.updateDate}\n大小:${data.size}\n版本说明:\n${data.feature}") + .setPositiveButton(getString(R.string.download)) { _, _ -> + val intent = Intent(Intent.ACTION_VIEW) + val uri = Uri.parse(data.downloadUrl) + intent.data = uri + startActivity(intent) + }.create().show() private val receiver = object : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { intent?.run { if (action == ReceiverConstant.ACTION_ACCESSIBILITY_SERVICE_STATUS_CHANGED) { - accessibilitySettingView.checked = intent.getBooleanExtra("status", false) + binding.accessibilitySettingView.checked = + intent.getBooleanExtra("status", false) } } } diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/activity/main/daggerinject/MainActivityComponent.kt b/app/src/main/java/com/wrbug/developerhelper/ui/activity/main/daggerinject/MainActivityComponent.kt deleted file mode 100644 index a7a23fc..0000000 --- a/app/src/main/java/com/wrbug/developerhelper/ui/activity/main/daggerinject/MainActivityComponent.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.wrbug.developerhelper.ui.activity.main.daggerinject - -import com.wrbug.developerhelper.ui.activity.main.MainActivity -import dagger.Component - -@Component -interface MainActivityComponent { - fun inject(activity: MainActivity) -} \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/activity/main/viewmodel/MainViewModel.kt b/app/src/main/java/com/wrbug/developerhelper/ui/activity/main/viewmodel/MainViewModel.kt deleted file mode 100644 index c6959ae..0000000 --- a/app/src/main/java/com/wrbug/developerhelper/ui/activity/main/viewmodel/MainViewModel.kt +++ /dev/null @@ -1,50 +0,0 @@ -package com.wrbug.developerhelper.ui.activity.main.viewmodel - -import androidx.databinding.ObservableBoolean -import androidx.databinding.ObservableField -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.OnLifecycleEvent -import com.wrbug.developerhelper.R -import com.wrbug.developerhelper.basecommon.BaseViewModel -import com.wrbug.developerhelper.mmkv.ConfigKv -import com.wrbug.developerhelper.mmkv.manager.MMKVManager -import com.wrbug.developerhelper.service.DeveloperHelperAccessibilityService -import com.wrbug.developerhelper.service.FloatWindowService -import com.wrbug.developerhelper.util.DeviceUtils -import javax.inject.Inject - -class MainViewModel @Inject constructor() : BaseViewModel() { - val openAccessibility = ObservableBoolean() - val openFloatWindow = ObservableBoolean() - val openRoot = ObservableBoolean() - private val configKv: ConfigKv = MMKVManager.get(ConfigKv::class.java) - private fun checkStatus() { - openAccessibility.set(DeveloperHelperAccessibilityService.serviceRunning) - openFloatWindow.set(DeviceUtils.isFloatWindowOpened()) - if (configKv.isOpenRoot()) { - if (DeviceUtils.isRoot()) { - openRoot.set(true) - } else { - openRoot.set(false) - configKv.setOpenRoot(false) - } - } - if (DeviceUtils.isFloatWindowOpened()) { - FloatWindowService.start(application) - } - } - - @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) - fun onResume() { - checkStatus() - } - - fun toggleRootPermission() { - if (!DeviceUtils.isRoot()) { - showSnack(R.string.devices_is_not_root) - return - } - openRoot.set(!openRoot.get()) - configKv.setOpenRoot(openRoot.get()) - } -} diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/activity/sharedpreferencesedit/SharedPreferenceEditActivity.kt b/app/src/main/java/com/wrbug/developerhelper/ui/activity/sharedpreferencesedit/SharedPreferenceEditActivity.kt index 1516235..04089b9 100644 --- a/app/src/main/java/com/wrbug/developerhelper/ui/activity/sharedpreferencesedit/SharedPreferenceEditActivity.kt +++ b/app/src/main/java/com/wrbug/developerhelper/ui/activity/sharedpreferencesedit/SharedPreferenceEditActivity.kt @@ -6,31 +6,48 @@ import android.os.Bundle import android.view.Menu import android.view.MenuItem import androidx.appcompat.app.AlertDialog +import androidx.core.view.isVisible import androidx.recyclerview.widget.LinearLayoutManager import com.wrbug.developerhelper.R -import com.wrbug.developerhelper.basecommon.BaseActivity -import com.wrbug.developerhelper.basecommon.setupActionBar -import com.wrbug.developerhelper.commonutil.shell.ShellManager +import com.wrbug.developerhelper.base.BaseActivity +import com.wrbug.developerhelper.base.setupActionBar +import com.wrbug.developerhelper.commonutil.AppManagerUtils +import com.wrbug.developerhelper.commonutil.addTo +import com.wrbug.developerhelper.commonutil.dpInt +import com.wrbug.developerhelper.commonutil.runOnIO +import com.wrbug.developerhelper.util.startPageLoading +import com.wrbug.developerhelper.util.stopPageLoading +import com.wrbug.developerhelper.databinding.ActivitySharedPreferenceEditBinding import com.wrbug.developerhelper.ui.decoration.SpaceItemDecoration -import com.wrbug.developerhelper.util.OutSharedPreferenceManager -import com.wrbug.developerhelper.util.XmlUtil -import com.wrbug.developerhelper.commonutil.dp2px -import kotlinx.android.synthetic.main.activity_shared_preference_edit.* -import org.jetbrains.anko.doAsync -import org.jetbrains.anko.uiThread +import com.wrbug.developerhelper.util.OutSharedPreference import java.io.File -class SharedPreferenceEditActivity : BaseActivity(), SharedPreferenceListAdapter.OnValueChangedListener { +class SharedPreferenceEditActivity : BaseActivity() { - - private var filePath: String = "" + private val filePath by lazy { + intent?.getStringExtra(KEY_FILE_PATH).orEmpty() + } + private val filePackageName by lazy { + intent?.getStringExtra(KEY_PACKAGE_NAME).orEmpty() + } + private val appName by lazy { + intent?.getStringExtra(KEY_APP_NAME).orEmpty() + } private lateinit var adapter: SharedPreferenceListAdapter private var saveMenuItem: MenuItem? = null + private lateinit var binding: ActivitySharedPreferenceEditBinding + private lateinit var outSharedPreference: OutSharedPreference companion object { - fun start(context: Context, filePath: String) { + + private const val KEY_FILE_PATH = "filePath" + private const val KEY_PACKAGE_NAME = "packageName" + private const val KEY_APP_NAME = "appName" + fun start(context: Context, filePath: String, packageName: String, appName: String) { val intent = Intent(context, SharedPreferenceEditActivity::class.java) - intent.putExtra("filePath", filePath) + intent.putExtra(KEY_FILE_PATH, filePath) + intent.putExtra(KEY_PACKAGE_NAME, packageName) + intent.putExtra(KEY_APP_NAME, appName) context.startActivity(intent) } @@ -38,39 +55,42 @@ class SharedPreferenceEditActivity : BaseActivity(), SharedPreferenceListAdapter override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_shared_preference_edit) - intent?.let { - filePath = it.getStringExtra("filePath") - } + binding = ActivitySharedPreferenceEditBinding.inflate(layoutInflater).inject() setupActionBar(R.id.toolbar) { if (filePath.isNotEmpty()) { title = File(filePath).name } } - - sprefRv.layoutManager = LinearLayoutManager(this) + outSharedPreference = OutSharedPreference(this, filePath) + binding.sprefRv.layoutManager = LinearLayoutManager(this) adapter = SharedPreferenceListAdapter(this) - sprefRv.adapter = adapter - adapter.setOnValueChangedListener(this) - val spaceItemDecoration = SpaceItemDecoration(dp2px(10F)) - sprefRv.addItemDecoration(spaceItemDecoration) + binding.sprefRv.adapter = adapter + adapter.setOnValueChangedListener { + saveMenuItem?.isVisible = it + } + val spaceItemDecoration = SpaceItemDecoration( + 24.dpInt(context), + 16.dpInt(context), + 24.dpInt(context), + 0, + 16.dpInt(context), + 40.dpInt(context) + ) + binding.sprefRv.addItemDecoration(spaceItemDecoration) parseXml() - - } - - override fun onChanged(changed: Boolean) { - saveMenuItem?.isVisible = changed } private fun parseXml() { - doAsync { - val xml = ShellManager.catFile(filePath) - val list = XmlUtil.parseSharedPreference(xml) - uiThread { - saveMenuItem?.isVisible = false - adapter.setData(list) - } - } + binding.flLoading.startPageLoading() + outSharedPreference.parse().runOnIO().subscribe({ + binding.emptyView.isVisible = it.isEmpty() + binding.sprefRv.isVisible = it.isNotEmpty() + saveMenuItem?.isVisible = false + adapter.setData(it) + binding.flLoading.stopPageLoading() + }, { + binding.flLoading.stopPageLoading() + }).addTo(disposable) } override fun onBackPressed() { @@ -85,44 +105,50 @@ class SharedPreferenceEditActivity : BaseActivity(), SharedPreferenceListAdapter private fun showSaveDialog() { AlertDialog.Builder(this).setMessage(getString(R.string.confirm_shared_preference_save)) - .setTitle(R.string.notice) - .setPositiveButton(R.string.save_and_exit) { _, _ -> - if (doSave()) { - showSnack(getString(R.string.save_shared_preference_success)) - finish() - } else { - showSnack(getString(R.string.save_shared_preference_failed)) - } - } - .setNegativeButton(getString(R.string.do_not_save)) { _, _ -> + .setTitle(R.string.notice).setPositiveButton(R.string.save_and_exit) { _, _ -> + doSave() + }.setNegativeButton(getString(R.string.do_not_save)) { _, _ -> finish() }.show() } - private fun doSave(): Boolean { + private fun doSave() { val data = adapter.getData() - val file = OutSharedPreferenceManager.saveToFile(this, data) - val success = ShellManager.catFile(file.absolutePath, filePath, "666") - file.delete() - return success + outSharedPreference.saveToFile(this, data).runOnIO().subscribe({ + if (it) { + parseXml() + showDialog(R.string.notice, + getString(R.string.save_shared_preference_success, appName), + R.string.ok, + R.string.cancel, + { + AppManagerUtils.restartApp( + this@SharedPreferenceEditActivity, filePackageName + ) + finish() + }) + } else { + showSnack(getString(R.string.save_shared_preference_failed)) + } + }, { + + }).addTo(disposable) } - override fun onOptionsItemSelected(item: MenuItem?): Boolean { - item?.run { - when (itemId) { - R.id.save_menu -> { - if (!doSave()) { - showSnack(getString(R.string.save_shared_preference_failed)) - return@run - } - showSnack(getString(R.string.save_shared_preference_success)) - parseXml() - } + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + R.id.save_menu -> { + doSave() } } return super.onOptionsItemSelected(item) } + override fun onDestroy() { + super.onDestroy() + outSharedPreference.deleteTmpFile() + } + override fun onCreateOptionsMenu(menu: Menu?): Boolean { menuInflater.inflate(R.menu.menu_shared_preference_edit, menu) saveMenuItem = menu?.findItem(R.id.save_menu) diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/activity/sharedpreferencesedit/SharedPreferenceListAdapter.kt b/app/src/main/java/com/wrbug/developerhelper/ui/activity/sharedpreferencesedit/SharedPreferenceListAdapter.kt index a952e6e..0e68495 100644 --- a/app/src/main/java/com/wrbug/developerhelper/ui/activity/sharedpreferencesedit/SharedPreferenceListAdapter.kt +++ b/app/src/main/java/com/wrbug/developerhelper/ui/activity/sharedpreferencesedit/SharedPreferenceListAdapter.kt @@ -4,33 +4,34 @@ import android.content.Context import android.text.Editable import android.text.TextWatcher import android.view.LayoutInflater -import android.view.View import android.view.ViewGroup -import android.widget.EditText -import android.widget.TextView import androidx.recyclerview.widget.RecyclerView import com.wrbug.developerhelper.R +import com.wrbug.developerhelper.util.inVisible +import com.wrbug.developerhelper.databinding.ItemSharedPreferenceInfoBinding import com.wrbug.developerhelper.model.entity.SharedPreferenceItemInfo class SharedPreferenceListAdapter(val context: Context) : RecyclerView.Adapter() { private val data: ArrayList = arrayListOf() - private var onValueChangedListener: OnValueChangedListener? = null + private var onValueChangedListener: ((Boolean) -> Unit)? = null private var changedFlag: Long = 0 - fun setOnValueChangedListener(listener: OnValueChangedListener) { + fun setOnValueChangedListener(listener: (Boolean) -> Unit) { onValueChangedListener = listener } fun setData(array: Array) { data.clear() changedFlag = 0 - data.addAll(array.sortedBy { it.key }) + data.addAll(array.sortedBy { it.key.lowercase() }) notifyDataSetChanged() } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { - return ViewHolder(LayoutInflater.from(context).inflate(R.layout.item_shared_preference_info, parent, false)) + return ViewHolder( + ItemSharedPreferenceInfoBinding.inflate(LayoutInflater.from(context), parent, false) + ) } override fun getItemCount(): Int { @@ -39,18 +40,15 @@ class SharedPreferenceListAdapter(val context: Context) : override fun onBindViewHolder(holder: ViewHolder, position: Int) { val sharedPreferenceItemInfo = data[position] - holder.titleTv?.text = sharedPreferenceItemInfo.key - holder.contentTv?.setText(sharedPreferenceItemInfo.value) - holder.restoreTv?.visibility = - if (sharedPreferenceItemInfo.value == sharedPreferenceItemInfo.newValue) { - View.INVISIBLE - } else { - View.VISIBLE - } + holder.removeTextChangedListener() + holder.binding.titleTv.text = sharedPreferenceItemInfo.key + holder.binding.contentEt.setText(sharedPreferenceItemInfo.newValue) + holder.binding.restoreTv.inVisible = + sharedPreferenceItemInfo.value == sharedPreferenceItemInfo.newValue if (sharedPreferenceItemInfo.isValueValid()) { - holder.contentTv?.error = null + holder.binding.contentEt.error = null } else { - holder.contentTv?.error = context.getString(R.string.input_error) + holder.binding.contentEt.error = context.getString(R.string.input_error) } holder.tag = sharedPreferenceItemInfo holder.addTextChangedListener() @@ -61,21 +59,26 @@ class SharedPreferenceListAdapter(val context: Context) : } - inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { - val titleTv: TextView? = itemView.findViewById(R.id.titleTv) - val contentTv: EditText? = itemView.findViewById(R.id.contentEt) - var restoreTv: TextView? = itemView.findViewById(R.id.restoreTv) + inner class ViewHolder(val binding: ItemSharedPreferenceInfoBinding) : + RecyclerView.ViewHolder(binding.root) { var tag: SharedPreferenceItemInfo? = null var textWatcher: TextWatcher? = null init { - restoreTv?.setOnClickListener { + binding.restoreTv.setOnClickListener { tag?.run { - contentTv?.setText(value) + binding.contentEt.setText(value) } } } + fun removeTextChangedListener() { + if (textWatcher != null) { + binding.contentEt.removeTextChangedListener(textWatcher) + textWatcher = null + } + } + fun addTextChangedListener() { if (textWatcher != null) { return @@ -90,9 +93,9 @@ class SharedPreferenceListAdapter(val context: Context) : tag?.let { it.newValue = toString() if (it.isValueValid()) { - contentTv?.error = null + binding.contentEt.error = null } else { - contentTv?.error = context.getString(R.string.input_error) + binding.contentEt.error = context.getString(R.string.input_error) } } changedFlag = if (toString() == tag?.value) { @@ -100,30 +103,22 @@ class SharedPreferenceListAdapter(val context: Context) : } else { changedFlag or 1L shl index } - onValueChangedListener?.onChanged(changedFlag != 0L) + onValueChangedListener?.invoke(changedFlag != 0L) } - restoreTv?.visibility = - if (tag?.value == tag?.newValue) { - View.INVISIBLE - } else { - View.VISIBLE - } + binding.restoreTv.inVisible = tag?.value == tag?.newValue } - override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { + override fun beforeTextChanged( + s: CharSequence?, start: Int, count: Int, after: Int + ) { } override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { } } - contentTv?.addTextChangedListener(textWatcher) + binding.contentEt.addTextChangedListener(textWatcher) } } - - - interface OnValueChangedListener { - fun onChanged(changed: Boolean) - } } \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/activity/xposed/shellmanager/ShellAppListAdapter.kt b/app/src/main/java/com/wrbug/developerhelper/ui/activity/xposed/shellmanager/ShellAppListAdapter.kt deleted file mode 100644 index 8c7580e..0000000 --- a/app/src/main/java/com/wrbug/developerhelper/ui/activity/xposed/shellmanager/ShellAppListAdapter.kt +++ /dev/null @@ -1,82 +0,0 @@ -package com.wrbug.developerhelper.ui.activity.xposed.shellmanager - -import android.content.Context -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.ImageView -import android.widget.TextView -import androidx.recyclerview.widget.RecyclerView -import com.wrbug.developerhelper.R -import com.wrbug.developerhelper.commonutil.entity.ApkInfo -import com.wrbug.developerhelper.ui.widget.bottommenu.BottomMenu -import com.wrbug.developerhelper.ui.widget.bottommenu.OnItemClickListener -import com.wrbug.developerhelper.xposed.processshare.DumpDexListProcessData -import com.wrbug.developerhelper.xposed.processshare.ProcessDataManager - -class ShellAppListAdapter(val context: Context) : RecyclerView.Adapter() { - private val list = ArrayList() - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = - ViewHolder(LayoutInflater.from(context).inflate(R.layout.item_shell_app_info, parent, false)) - - fun setData(data: List) { - list.clear() - list.addAll(data) - notifyDataSetChanged() - } - - override fun getItemCount(): Int = list.size - - override fun onBindViewHolder(holder: ViewHolder, position: Int) { - val apkInfo = list[position] - holder.icoIv.setImageDrawable(apkInfo.getIco()) - holder.appNameTv.text = apkInfo.getAppName() - holder.packageNameTv.text = apkInfo.packageInfo.packageName - holder.apkInfo = apkInfo - } - - - inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { - var icoIv: ImageView = itemView.findViewById(R.id.icoIv) - var appNameTv: TextView = itemView.findViewById(R.id.appNameTv) - var packageNameTv: TextView = itemView.findViewById(R.id.packageNameTv) - var apkInfo: ApkInfo? = null - - init { - itemView.setOnClickListener { - apkInfo?.apply { - showDialog(this) - } - } - } - - private fun showDialog(apkInfo: ApkInfo) { - val bottomMenu = BottomMenu.Builder(context) - .menuItems(arrayOf(context.getString(R.string.remove_item))) - .onItemClickListener(object : OnItemClickListener { - override fun onClick(position: Int) { - when (position) { - 0 -> { - removeItem(apkInfo) - } - } - } - }) - .build() - bottomMenu.show() - } - } - - private fun removeItem(apkInfo: ApkInfo) { - val dexListProcessData = ProcessDataManager.get(DumpDexListProcessData::class.java) - val packageNames = dexListProcessData.getData() - packageNames?.apply { - remove(apkInfo.packageInfo.packageName) - dexListProcessData.setData(this) - } - val index = list.indexOf(apkInfo) - list.remove(apkInfo) - notifyItemRemoved(index) - } - -} diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/activity/xposed/shellmanager/ShellAppManagerActivity.kt b/app/src/main/java/com/wrbug/developerhelper/ui/activity/xposed/shellmanager/ShellAppManagerActivity.kt deleted file mode 100644 index 06c5b8b..0000000 --- a/app/src/main/java/com/wrbug/developerhelper/ui/activity/xposed/shellmanager/ShellAppManagerActivity.kt +++ /dev/null @@ -1,90 +0,0 @@ -package com.wrbug.developerhelper.ui.activity.xposed.shellmanager - -import android.content.Context -import android.content.Intent -import android.os.Bundle -import android.view.View -import androidx.recyclerview.widget.LinearLayoutManager -import com.wrbug.developerhelper.R -import com.wrbug.developerhelper.basecommon.BaseActivity -import com.wrbug.developerhelper.basecommon.setupActionBar -import com.wrbug.developerhelper.commonutil.AppInfoManager -import com.wrbug.developerhelper.commonutil.entity.ApkInfo -import com.wrbug.developerhelper.xposed.processshare.DumpDexListProcessData -import com.wrbug.developerhelper.xposed.processshare.ProcessDataManager -import kotlinx.android.synthetic.main.activity_shell_app_manager.* -import org.jetbrains.anko.doAsync -import org.jetbrains.anko.uiThread - -class ShellAppManagerActivity : BaseActivity() { - private var tmpList = ArrayList() - private val adapter: ShellAppListAdapter by lazy { - ShellAppListAdapter(this) - } - - companion object { - fun start(context: Context) { - context.startActivity(Intent(context, ShellAppManagerActivity::class.java)) - } - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_shell_app_manager) - setupActionBar(R.id.toolbar) { - title = getString(R.string.shell_app_manager) - } - swipeLayout.setOnRefreshListener { - getShellApp() - } - initRv() - getShellApp() - } - - private fun initRv() { - shellAppRv.layoutManager = LinearLayoutManager(this) - shellAppRv.adapter = adapter - } - - private fun getShellApp() { - swipeLayout.isRefreshing = true - doAsync { - val dexListProcessData = ProcessDataManager.get(DumpDexListProcessData::class.java) - val packageNames = dexListProcessData.getData() - if (packageNames == null || packageNames.isEmpty()) { - uiThread { - swipeLayout.isRefreshing = false - emptyView.visibility = View.VISIBLE - shellAppRv.visibility = View.INVISIBLE - } - tmpList.clear() - return@doAsync - } - if (tmpList.size != packageNames.size || !tmpList.containsAll(packageNames)) { - val data = ArrayList() - packageNames.forEach { it -> - AppInfoManager.getAppByPackageName(it)?.let { - data.add(it) - } - } - tmpList = packageNames - uiThread { - setData(data) - } - } else { - uiThread { - swipeLayout.isRefreshing = false - } - } - } - } - - private fun setData(list: List) { - swipeLayout.isRefreshing = false - emptyView.visibility = if (list.isEmpty()) View.VISIBLE else View.GONE - shellAppRv.visibility = if (list.isEmpty()) View.INVISIBLE else View.VISIBLE - if (list.isEmpty().not()) { - adapter.setData(list) - } - } -} diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/activity/xposed/xposedsetting/XposedSettingActivity.kt b/app/src/main/java/com/wrbug/developerhelper/ui/activity/xposed/xposedsetting/XposedSettingActivity.kt deleted file mode 100644 index 4a491ad..0000000 --- a/app/src/main/java/com/wrbug/developerhelper/ui/activity/xposed/xposedsetting/XposedSettingActivity.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.wrbug.developerhelper.ui.activity.xposed.xposedsetting - -import android.os.Bundle -import com.wrbug.developerhelper.R -import com.wrbug.developerhelper.basecommon.BaseActivity -import com.wrbug.developerhelper.basecommon.setupActionBar -import com.wrbug.developerhelper.ui.activity.xposed.shellmanager.ShellAppManagerActivity -import kotlinx.android.synthetic.main.activity_xposed_setting.* - -class XposedSettingActivity : BaseActivity() { - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_xposed_setting) - setupActionBar(R.id.toolbar) { - title = getString(R.string.xposed_setting) - } - shellSettingItemView.setOnClickListener { - ShellAppManagerActivity.start(this) - } - } -} diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/adapter/ExMultiTypeAdapter.kt b/app/src/main/java/com/wrbug/developerhelper/ui/adapter/ExMultiTypeAdapter.kt new file mode 100644 index 0000000..fc192c7 --- /dev/null +++ b/app/src/main/java/com/wrbug/developerhelper/ui/adapter/ExMultiTypeAdapter.kt @@ -0,0 +1,36 @@ +package com.wrbug.developerhelper.ui.adapter + +import com.drakeet.multitype.MultiTypeAdapter +import com.wrbug.developerhelper.ui.adapter.bean.EmptyViewItemData +import com.wrbug.developerhelper.ui.adapter.bean.LoadingViewItemData +import com.wrbug.developerhelper.ui.adapter.delegate.EmptyViewItemDelegate +import com.wrbug.developerhelper.ui.adapter.delegate.LoadingItemDelegate + +class ExMultiTypeAdapter : MultiTypeAdapter() { + + companion object { + fun get() = lazy { + ExMultiTypeAdapter() + } + } + + init { + register(EmptyViewItemDelegate()) + register(LoadingItemDelegate()) + } + + fun showEmpty(title: String = "") { + items = listOf(EmptyViewItemData(title)) + notifyDataSetChanged() + } + + fun showLoading() { + items = listOf(LoadingViewItemData) + notifyDataSetChanged() + } + + fun loadData(list: List) { + items = list.toList() + notifyDataSetChanged() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/adapter/bean/EmptyViewItemData.kt b/app/src/main/java/com/wrbug/developerhelper/ui/adapter/bean/EmptyViewItemData.kt new file mode 100644 index 0000000..190efdb --- /dev/null +++ b/app/src/main/java/com/wrbug/developerhelper/ui/adapter/bean/EmptyViewItemData.kt @@ -0,0 +1,5 @@ +package com.wrbug.developerhelper.ui.adapter.bean + +class EmptyViewItemData(val title: String = "") + +object LoadingViewItemData \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/adapter/delegate/BaseItemViewBindingDelegate.kt b/app/src/main/java/com/wrbug/developerhelper/ui/adapter/delegate/BaseItemViewBindingDelegate.kt new file mode 100644 index 0000000..ad8b57c --- /dev/null +++ b/app/src/main/java/com/wrbug/developerhelper/ui/adapter/delegate/BaseItemViewBindingDelegate.kt @@ -0,0 +1,27 @@ +package com.wrbug.developerhelper.ui.adapter.delegate + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.viewbinding.ViewBinding +import com.drakeet.multitype.ItemViewDelegate + +abstract class BaseItemViewBindingDelegate : + ItemViewDelegate>() { + + final override fun onBindViewHolder(holder: ViewBindingHolder, item: T) { + onBindViewHolder(holder.binding, item) + } + + final override fun onCreateViewHolder( + context: Context, + parent: ViewGroup + ): ViewBindingHolder { + return ViewBindingHolder(onCreateViewBinding(LayoutInflater.from(context), parent)) + } + + abstract fun onCreateViewBinding(inflater: LayoutInflater, parent: ViewGroup): B + + abstract fun onBindViewHolder(binding: B, item: T) + +} \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/adapter/delegate/EmptyViewItemDelegate.kt b/app/src/main/java/com/wrbug/developerhelper/ui/adapter/delegate/EmptyViewItemDelegate.kt new file mode 100644 index 0000000..e191a21 --- /dev/null +++ b/app/src/main/java/com/wrbug/developerhelper/ui/adapter/delegate/EmptyViewItemDelegate.kt @@ -0,0 +1,27 @@ +package com.wrbug.developerhelper.ui.adapter.delegate + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import com.wrbug.developerhelper.R +import com.wrbug.developerhelper.databinding.ItemEmptyDataBinding +import com.wrbug.developerhelper.ui.adapter.bean.EmptyViewItemData +import com.wrbug.developerhelper.util.getString + +class EmptyViewItemDelegate : + BaseItemViewBindingDelegate() { + override fun onBindViewHolder(binding: ItemEmptyDataBinding, item: EmptyViewItemData) { + if (item.title.isNotEmpty()) { + binding.emptyView.setTitle(item.title) + } else { + binding.emptyView.setTitle(R.string.no_data.getString()) + } + } + + override fun onCreateViewBinding( + inflater: LayoutInflater, + parent: ViewGroup + ): ItemEmptyDataBinding { + return ItemEmptyDataBinding.inflate(inflater, parent, false) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/adapter/delegate/LoadingItemDelegate.kt b/app/src/main/java/com/wrbug/developerhelper/ui/adapter/delegate/LoadingItemDelegate.kt new file mode 100644 index 0000000..d778443 --- /dev/null +++ b/app/src/main/java/com/wrbug/developerhelper/ui/adapter/delegate/LoadingItemDelegate.kt @@ -0,0 +1,20 @@ +package com.wrbug.developerhelper.ui.adapter.delegate + +import android.view.LayoutInflater +import android.view.ViewGroup +import com.wrbug.developerhelper.databinding.ItemLoadingDataBinding +import com.wrbug.developerhelper.ui.adapter.bean.LoadingViewItemData + +class LoadingItemDelegate : + BaseItemViewBindingDelegate() { + override fun onBindViewHolder(binding: ItemLoadingDataBinding, item: LoadingViewItemData) { + + } + + override fun onCreateViewBinding( + inflater: LayoutInflater, + parent: ViewGroup + ): ItemLoadingDataBinding { + return ItemLoadingDataBinding.inflate(inflater, parent, false) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/adapter/delegate/ViewBindingHolder.kt b/app/src/main/java/com/wrbug/developerhelper/ui/adapter/delegate/ViewBindingHolder.kt new file mode 100644 index 0000000..4eb89fe --- /dev/null +++ b/app/src/main/java/com/wrbug/developerhelper/ui/adapter/delegate/ViewBindingHolder.kt @@ -0,0 +1,7 @@ +package com.wrbug.developerhelper.ui.adapter.delegate + +import androidx.recyclerview.widget.RecyclerView +import androidx.viewbinding.ViewBinding + +class ViewBindingHolder(val binding: T) : + RecyclerView.ViewHolder(binding.root) \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/decoration/SpaceItemDecoration.kt b/app/src/main/java/com/wrbug/developerhelper/ui/decoration/SpaceItemDecoration.kt index d31ab71..5fb18cf 100644 --- a/app/src/main/java/com/wrbug/developerhelper/ui/decoration/SpaceItemDecoration.kt +++ b/app/src/main/java/com/wrbug/developerhelper/ui/decoration/SpaceItemDecoration.kt @@ -3,6 +3,7 @@ package com.wrbug.developerhelper.ui.decoration import android.graphics.Rect import android.view.View import androidx.recyclerview.widget.RecyclerView +import com.wrbug.developerhelper.commonutil.dpInt /** * SpaceItemDecoration @@ -10,15 +11,37 @@ import androidx.recyclerview.widget.RecyclerView * @author wrbug * @since 2017/9/29 */ -class SpaceItemDecoration : RecyclerView.ItemDecoration { - private var leftSpace: Int = 0 - private var topSpace: Int = 0 - private var rightSpace: Int = 0 - private var bottomSpace: Int = 0 - private var firstTopSpace = 0 - private var lastBottomSpace: Int = 0 - - override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) { +class SpaceItemDecoration( + private val leftSpace: Int = 0, + private val topSpace: Int = 0, + private val rightSpace: Int = 0, + private val bottomSpace: Int = 0, + private var firstTopSpace: Int = 0, + private var lastBottomSpace: Int = 0, +) : RecyclerView.ItemDecoration() { + companion object { + val standard = SpaceItemDecoration( + 24.dpInt(), + 12.dpInt(), + 24.dpInt(), + 12.dpInt(), + 24.dpInt(), + 40.dpInt() + ) + } + + constructor(space: Int) : this( + bottomSpace = space, + topSpace = space, + rightSpace = space, + leftSpace = space, + lastBottomSpace = space, + firstTopSpace = space + ) + + override fun getItemOffsets( + outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State + ) { super.getItemOffsets(outRect, view, parent, state) outRect.left = leftSpace outRect.right = rightSpace @@ -35,14 +58,6 @@ class SpaceItemDecoration : RecyclerView.ItemDecoration { } - constructor(space: Int) { - bottomSpace = space - topSpace = space - rightSpace = space - leftSpace = space - lastBottomSpace = space - firstTopSpace = space - } fun setLastBottomPadding(space: Int) { lastBottomSpace = space @@ -51,13 +66,4 @@ class SpaceItemDecoration : RecyclerView.ItemDecoration { fun setFirstTopPadding(space: Int) { firstTopSpace = space } - - constructor(leftSpace: Int, topSpace: Int, rightSpace: Int, bottomSpace: Int) { - this.leftSpace = leftSpace - this.topSpace = topSpace - this.rightSpace = rightSpace - this.bottomSpace = bottomSpace - lastBottomSpace = bottomSpace - firstTopSpace = topSpace - } } diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/widget/appbar/AppBar.kt b/app/src/main/java/com/wrbug/developerhelper/ui/widget/appbar/AppBar.kt new file mode 100644 index 0000000..c208920 --- /dev/null +++ b/app/src/main/java/com/wrbug/developerhelper/ui/widget/appbar/AppBar.kt @@ -0,0 +1,88 @@ +package com.wrbug.developerhelper.ui.widget.appbar + +import android.content.Context +import android.util.AttributeSet +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.FrameLayout +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.core.view.isGone +import androidx.core.view.isInvisible +import androidx.core.view.isVisible +import androidx.fragment.app.FragmentActivity +import com.wrbug.developerhelper.R +import com.wrbug.developerhelper.databinding.ViewAppBarBinding +import com.wrbug.developerhelper.util.getColor +import com.wrbug.developerhelper.util.setOnDoubleCheckClickListener + +class AppBar @JvmOverloads constructor( + context: Context, attrs: AttributeSet? = null +) : FrameLayout(context, attrs) { + + private val binding = ViewAppBarBinding.inflate(LayoutInflater.from(context), this, false) + + init { + setBackgroundColor(R.color.colorPrimaryDark.getColor(context)) + addView(binding.root) + binding.ivBack.setOnDoubleCheckClickListener { + if (context is FragmentActivity) { + context.onBackPressedDispatcher.onBackPressed() + } + } + attrs?.let { + initAttr(it) + } + } + + private fun initAttr(attrs: AttributeSet) { + with(context.obtainStyledAttributes(attrs, R.styleable.AppBar)) { + val title = getString(R.styleable.AppBar_title) + setTitle(title) + val subTitle = getString(R.styleable.AppBar_subTitle) + setSubTitle(subTitle) + val showBack = getBoolean(R.styleable.AppBar_showBack, false) + showBack(showBack) + recycle() + } + } + + fun setSubTitle(subTitle: CharSequence?) { + binding.tvSubTitle.text = subTitle + binding.tvSubTitle.isGone = subTitle.isNullOrEmpty() + } + + fun showBack(showBack: Boolean) { + binding.ivBack.isInvisible = !showBack + } + + fun setTitle(title: CharSequence?) { + binding.tvTitle.text = title + } + + override fun addView(child: View?, index: Int, params: ViewGroup.LayoutParams?) { + if (child == binding.root) { + super.addView(child, index, params) + return + } + binding.flRight.addView(child, index, params) + } + + override fun addViewInLayout( + child: View?, + index: Int, + params: ViewGroup.LayoutParams?, + preventRequestLayout: Boolean + ): Boolean { + return super.addViewInLayout(child, index, params, preventRequestLayout) + } + + +// fun setMenu(title: String?, listener: (View) -> Unit) { +// binding.tvRightBtn.text = title +// binding.tvRightBtn.isGone = title.isNullOrEmpty() +// binding.tvRightBtn.setOnDoubleCheckClickListener { +// listener(it) +// } +// } +} \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/widget/appdatainfoview/AppDataInfoView.kt b/app/src/main/java/com/wrbug/developerhelper/ui/widget/appdatainfoview/AppDataInfoView.kt index 1d844e6..292ab52 100644 --- a/app/src/main/java/com/wrbug/developerhelper/ui/widget/appdatainfoview/AppDataInfoView.kt +++ b/app/src/main/java/com/wrbug/developerhelper/ui/widget/appdatainfoview/AppDataInfoView.kt @@ -14,37 +14,39 @@ import com.wrbug.developerhelper.commonutil.shell.ShellManager import com.wrbug.developerhelper.ui.activity.databaseedit.DatabaseEditActivity import com.wrbug.developerhelper.ui.activity.sharedpreferencesedit.SharedPreferenceEditActivity import com.wrbug.developerhelper.commonutil.UiUtils -import kotlinx.android.synthetic.main.view_app_data_info.view.* +import com.wrbug.developerhelper.databinding.ViewAppDataInfoBinding import org.jetbrains.anko.doAsync import org.jetbrains.anko.uiThread import java.io.File -class AppDataInfoView : FrameLayout { +class AppDataInfoView: FrameLayout { + + private lateinit var binding: ViewAppDataInfoBinding var apkInfo: ApkInfo? = null set(value) { field = value loadData() } - constructor(context: Context) : super(context) { + constructor(context: Context): super(context) { initView() } - constructor(context: Context, attributeSet: AttributeSet) : super(context, attributeSet) { + constructor(context: Context, attributeSet: AttributeSet): super(context, attributeSet) { initView() } private fun initView() { - LayoutInflater.from(context).inflate(R.layout.view_app_data_info, this) + binding = ViewAppDataInfoBinding.inflate(LayoutInflater.from(context), this, true) } private fun loadData() { apkInfo?.run { - apkPathTv.text = applicationInfo.publicSourceDir + binding.apkPathTv.text = applicationInfo.publicSourceDir val apkSignInfo = ApkUtils.getApkSignInfo(context, applicationInfo.packageName) - apkSha1Tv.text = apkSignInfo.sha1 - apkMd5Tv.text = apkSignInfo.md5 + binding.apkSha1Tv.text = apkSignInfo.sha1 + binding.apkMd5Tv.text = apkSignInfo.md5 getSharedPreferencesFiles(applicationInfo.packageName) getDatabaseFiles(applicationInfo.packageName) } @@ -55,11 +57,12 @@ class AppDataInfoView : FrameLayout { val sqliteFiles = ShellManager.getSqliteFiles(packageName) uiThread { if (sqliteFiles.isEmpty()) { - defaultDbTv.setText(R.string.none) + binding.defaultDbTv.setText(R.string.none) return@uiThread } - databaseContainer.removeAllViews() - val params = LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT) + binding.databaseContainer.removeAllViews() + val params = + LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT) for (sqliteFile in sqliteFiles) { val textView = AppCompatTextView(context) textView.text = sqliteFile.name @@ -67,7 +70,7 @@ class AppDataInfoView : FrameLayout { textView.setTextColor(resources.getColor(R.color.item_content_text)) textView.setPadding(0, UiUtils.dp2px(context, 8F), 0, 0) textView.tag = sqliteFile - databaseContainer.addView(textView, params) + binding.databaseContainer.addView(textView, params) textView.setOnClickListener { DatabaseEditActivity.start(context, (it.tag as File).absolutePath) } @@ -82,11 +85,12 @@ class AppDataInfoView : FrameLayout { val files = AppInfoManager.getSharedPreferencesFiles(packageName) uiThread { if (files.isEmpty()) { - defaultSpTv.setText(R.string.none) + binding.defaultSpTv.setText(R.string.none) return@uiThread } - sharedPreferenceContainer.removeAllViews() - val params = LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT) + binding.sharedPreferenceContainer.removeAllViews() + val params = + LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT) files.forEach { val textView = AppCompatTextView(context) textView.text = it.name @@ -94,9 +98,14 @@ class AppDataInfoView : FrameLayout { textView.setTextColor(resources.getColor(R.color.item_content_text)) textView.setPadding(0, UiUtils.dp2px(context, 8F), 0, 0) textView.tag = it - sharedPreferenceContainer.addView(textView, params) + binding.sharedPreferenceContainer.addView(textView, params) textView.setOnClickListener { - SharedPreferenceEditActivity.start(context, (it.tag as File).absolutePath) + SharedPreferenceEditActivity.start( + context, + (it.tag as File).absolutePath, + packageName, + apkInfo?.getAppName() ?: "" + ) } } } diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/widget/appsettingview/AppSettingView.kt b/app/src/main/java/com/wrbug/developerhelper/ui/widget/appsettingview/AppSettingView.kt index 26db862..777f8e6 100644 --- a/app/src/main/java/com/wrbug/developerhelper/ui/widget/appsettingview/AppSettingView.kt +++ b/app/src/main/java/com/wrbug/developerhelper/ui/widget/appsettingview/AppSettingView.kt @@ -1,39 +1,34 @@ package com.wrbug.developerhelper.ui.widget.appsettingview -import android.Manifest import android.app.Activity -import android.app.AlertDialog import android.content.Context -import android.content.DialogInterface import android.util.AttributeSet import android.view.LayoutInflater import android.widget.ScrollView +import androidx.appcompat.app.AlertDialog +import androidx.fragment.app.FragmentActivity +import com.wrbug.developerhelper.BuildConfig import com.wrbug.developerhelper.R -import com.wrbug.developerhelper.basecommon.showToast +import com.wrbug.developerhelper.base.requestStoragePermission import com.wrbug.developerhelper.commonutil.AppManagerUtils import com.wrbug.developerhelper.commonutil.entity.ApkInfo +import com.wrbug.developerhelper.util.setOnRootCheckClickListener +import com.wrbug.developerhelper.util.visible +import com.wrbug.developerhelper.databinding.DialogBackupAppSelectBinding +import com.wrbug.developerhelper.databinding.ViewAppSettingBinding import com.wrbug.developerhelper.mmkv.ConfigKv import com.wrbug.developerhelper.mmkv.manager.MMKVManager -import kotlinx.android.synthetic.main.view_app_setting.view.* -import android.content.Intent -import android.net.Uri -import androidx.appcompat.widget.AppCompatButton -import com.wrbug.developerhelper.basecommon.BaseActivity -import com.wrbug.developerhelper.commonutil.shell.ShellManager -import com.wrbug.developerhelper.commonutil.zip -import com.wrbug.developerhelper.util.BackupUtils -import com.wrbug.developerhelper.util.toUri -import gdut.bsx.share2.Share2 -import gdut.bsx.share2.ShareContentType -import org.jetbrains.anko.doAsync -import org.jetbrains.anko.uiThread -import java.io.File - +import com.wrbug.developerhelper.ui.activity.appbackup.AppBackupDetailActivity +import com.wrbug.developerhelper.util.getString +import io.reactivex.rxjava3.disposables.CompositeDisposable +import org.jetbrains.anko.toast class AppSettingView : ScrollView { - var apkInfo: ApkInfo? = null + + private var apkInfo: ApkInfo? = null private val configKv = MMKVManager.get(ConfigKv::class.java) - private var exportDexBtn: AppCompatButton? = null + private lateinit var binding: ViewAppSettingBinding + private lateinit var disposable: CompositeDisposable constructor(context: Context) : super(context) { initView() @@ -43,89 +38,88 @@ class AppSettingView : ScrollView { initView() } - private fun initView() { - LayoutInflater.from(context).inflate(R.layout.view_app_setting, this) - exportDexBtn = findViewById(R.id.exportDexBtn) - if (configKv.isOpenRoot().not()) { - backupApkBtn.isEnabled = false - backupApkDataDirBtn.isEnabled = false - restartAppBtn.isEnabled = false - stopAppBtn.isEnabled = false - deleteAppDataBtn.isEnabled = false - exportDexBtn?.isEnabled = false + fun setApkInfo(apkInfo: ApkInfo?) { + this.apkInfo = apkInfo + if (apkInfo?.applicationInfo?.packageName == BuildConfig.APPLICATION_ID) { + binding.uninstallAppBtn.visible = false } + } + + private fun initView() { + binding = ViewAppSettingBinding.inflate(LayoutInflater.from(context), this, true) initListener() } private fun initListener() { - backupApkBtn.setOnClickListener { - doBackupApk() - } - backupApkDataDirBtn.setOnClickListener { - doBackupDataDir() + binding.backupAppBtn.setOnRootCheckClickListener { + showBackupSelect() + } + binding.restoreAppBtn.setOnRootCheckClickListener { + AppBackupDetailActivity.start( + context, + apkInfo?.getAppName().orEmpty(), + apkInfo?.packageInfo?.packageName.orEmpty(), true + ) } - restartAppBtn.setOnClickListener { + binding.restartAppBtn.setOnRootCheckClickListener { doRestartApp() } - stopAppBtn.setOnClickListener { + binding.stopAppBtn.setOnRootCheckClickListener { doStopApp() } - deleteAppDataBtn.setOnClickListener { + binding.deleteAppDataBtn.setOnRootCheckClickListener { doDeleteAppData() } - uninstallAppBtn.setOnClickListener { + binding.uninstallAppBtn.setOnRootCheckClickListener { doUninstallApp() } - - exportDexBtn?.setOnClickListener { - doBackupDexData() - } } - private fun doBackupDexData() { - apkInfo?.apply { - showToast(context.getString(R.string.packing_files)) - doAsync { - val dir = File(context.externalCacheDir, "dex/${applicationInfo.packageName}") - if (dir.exists()) { - ShellManager.rmFile(dir.absolutePath) - } - dir.mkdirs() - val dexDir = "/data/data/${applicationInfo.packageName}/dump" - val lsDir = ShellManager.lsDir(dexDir) - if (lsDir.isEmpty()) { - uiThread { - showToast(context.getString(R.string.no_dex_files)) - } - return@doAsync - } - if (ShellManager.cpFile(dexDir, dir.absolutePath)) { - val zipFile = File(context.externalCacheDir, "${apkInfo?.getAppName() ?: ""}-dex.zip") - dir.zip(zipFile) - val uri = zipFile.toUri() - if (uri == null) { - showToast(R.string.export_failed) - return@doAsync - } - uiThread { - showShareDexNotice(uri) - } - } else { - uiThread { - showToast(context.getString(R.string.export_failed)) - } - } + private fun showBackupSelect() { + context.requestStoragePermission { + val selected = booleanArrayOf(false, false, false) + val binding = DialogBackupAppSelectBinding.inflate(LayoutInflater.from(context)) + binding.cbApk.setOnCheckedChangeListener { _, isChecked -> + selected[0] = isChecked } + binding.cbData.setOnCheckedChangeListener { _, isChecked -> + selected[1] = isChecked + } + binding.cbAndroidData.setOnCheckedChangeListener { _, isChecked -> + selected[2] = isChecked + } + binding.tvMemo.setText( + R.string.backup_default_meme.getString( + apkInfo?.getAppName().orEmpty() + ) + ) + AlertDialog.Builder(context).setTitle(R.string.backup_app_file).setView(binding.root) + .setNegativeButton(R.string.cancel, null).setPositiveButton( + R.string.ok + ) { _, _ -> + doBackup(selected, binding.tvMemo.text.toString()) + }.create().show() } } - private fun showShareDexNotice(uri: Uri) { - Share2.Builder(context as Activity) - .setContentType(ShareContentType.FILE) - .setShareFileUri(uri) - .setOnActivityResult(10) - .build() - .shareBySystem() + override fun onAttachedToWindow() { + super.onAttachedToWindow() + disposable = CompositeDisposable() + } + + override fun onDetachedFromWindow() { + super.onDetachedFromWindow() + disposable.dispose() + } + + private fun doBackup(selected: BooleanArray, memo: String) { + val activity = context as? FragmentActivity ?: return + if (selected.find { it } == null) { + return + } + BackupAppDialog.show( + activity.supportFragmentManager, apkInfo, memo, selected[0], selected[1], selected[2] + ) } private fun doUninstallApp() { @@ -140,13 +134,13 @@ class AppSettingView : ScrollView { } apkInfo?.apply { showNotice( - context.getString(R.string.confirm_delete_app_data), - DialogInterface.OnClickListener { _, _ -> - if (AppManagerUtils.clearAppData(applicationInfo.packageName)) { - activityFinish() - showToast(context.getString(R.string.clear_complete)) - } - }) + context.getString(R.string.confirm_delete_app_data) + ) { + if (AppManagerUtils.clearAppData(applicationInfo.packageName)) { + activityFinish() + context.toast(context.getString(R.string.clear_complete)) + } + } } } @@ -156,11 +150,11 @@ class AppSettingView : ScrollView { return } apkInfo?.apply { - showNotice(context.getString(R.string.confirm_stop_app), DialogInterface.OnClickListener { _, _ -> + showNotice(context.getString(R.string.confirm_stop_app)) { if (AppManagerUtils.forceStopApp(applicationInfo.packageName)) { activityFinish() } - }) + } } } @@ -169,132 +163,34 @@ class AppSettingView : ScrollView { return } apkInfo?.apply { - showNotice(context.getString(R.string.confirm_restart_app), DialogInterface.OnClickListener { _, _ -> - if (!AppManagerUtils.forceStopApp(applicationInfo.packageName)) { - showToast(context.getString(R.string.restart_failed)) - return@OnClickListener - } + showNotice( + context.getString(R.string.confirm_restart_app) + ) { + AppManagerUtils.restartApp(context, applicationInfo.packageName) activityFinish() - val intent = context.packageManager.getLaunchIntentForPackage(applicationInfo.packageName) - intent?.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - context.startActivity(intent) - }) - } - } - - private fun doBackupDataDir() { - if (checkRoot().not()) { - return - } - apkInfo?.apply { - val backupAppData = BackupUtils.backupAppData(applicationInfo.packageName, applicationInfo.dataDir) - if (backupAppData == null) { - showToast(context.getString(R.string.backup_failed)) - return } - if (context !is BaseActivity) { - showToast(context.getString(R.string.backup_success_msg)) - return - } - showShareDataNotice(backupAppData) } } - private fun showShareDataNotice(backupAppData: File) { - showNotice( - context.getString(R.string.backup_success_and_share_msg), - DialogInterface.OnClickListener { _, _ -> - (context as BaseActivity).requestPermission(arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), - object : BaseActivity.PermissionCallback() { - override fun granted() { - val zipFile = File(context.externalCacheDir, "${apkInfo?.getAppName() ?: ""}-data.zip") - backupAppData.zip(zipFile) - val uri = zipFile.toUri() - if (uri == null) { - showToast(context.getString(R.string.share_failed)) - return - } - activityFinish() - Share2.Builder(context as Activity) - .setContentType(ShareContentType.FILE) - .setShareFileUri(uri) - .setOnActivityResult(10) - .build() - .shareBySystem() - } - - }) - - }) - } - - private fun doBackupApk() { - if (checkRoot().not()) { - return - } - apkInfo?.apply { - val uri = BackupUtils.backupApk( - applicationInfo.packageName, - applicationInfo.publicSourceDir, - "${getAppName()}_${packageInfo.versionName}.apk" - ) - if (uri == null) { - showToast(context.getString(R.string.backup_failed)) - return - } - if (context !is BaseActivity) { - showToast(context.getString(R.string.backup_success_msg)) - return - } - showShareApkDialog(uri) - - } - } - - private fun showShareApkDialog(uri: Uri) { - showNotice( - context.getString(R.string.backup_success_and_share_msg), - DialogInterface.OnClickListener { _, _ -> - (context as BaseActivity).requestPermission(arrayOf( - Manifest.permission.READ_EXTERNAL_STORAGE, - Manifest.permission.WRITE_EXTERNAL_STORAGE - ), - object : BaseActivity.PermissionCallback() { - override fun granted() { - activityFinish() - Share2.Builder(context as Activity) - .setContentType(ShareContentType.FILE) - .setShareFileUri(uri) - .setOnActivityResult(10) - .build() - .shareBySystem() - } - - }) - - }) - } - private fun checkRoot(): Boolean { if (configKv.isOpenRoot().not()) { - showToast(context.getString(R.string.please_open_root)) + context.toast(context.getString(R.string.please_open_root)) return false } return true } - fun activityFinish() { + private fun activityFinish() { if (context is Activity) { (context as Activity).finish() } } - private fun showNotice(msg: String, listener: DialogInterface.OnClickListener) { - AlertDialog.Builder(context) - .setTitle(R.string.notice) - .setMessage(msg) - .setNegativeButton(R.string.cancel, null) - .setPositiveButton(R.string.ok, listener) - .create().show() + private fun showNotice(msg: String, listener: () -> Unit) { + AlertDialog.Builder(context).setTitle(R.string.notice).setMessage(msg) + .setNegativeButton(R.string.cancel, null).setPositiveButton( + R.string.ok + ) { _, _ -> listener() }.create().show() } + } \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/widget/appsettingview/BackupAppDialog.kt b/app/src/main/java/com/wrbug/developerhelper/ui/widget/appsettingview/BackupAppDialog.kt new file mode 100644 index 0000000..294b259 --- /dev/null +++ b/app/src/main/java/com/wrbug/developerhelper/ui/widget/appsettingview/BackupAppDialog.kt @@ -0,0 +1,276 @@ +package com.wrbug.developerhelper.ui.widget.appsettingview + +import android.app.Dialog +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.content.ContextCompat +import androidx.core.view.isVisible +import androidx.fragment.app.FragmentManager +import com.google.android.material.bottomsheet.BottomSheetDialogFragment +import com.wrbug.developerhelper.R +import com.wrbug.developerhelper.base.ExtraKey +import com.wrbug.developerhelper.base.versionCodeLong +import com.wrbug.developerhelper.commonutil.addTo +import com.wrbug.developerhelper.commonutil.entity.ApkInfo +import com.wrbug.developerhelper.commonutil.observeOnMain +import com.wrbug.developerhelper.commonutil.shell.ShellManager +import com.wrbug.developerhelper.util.setOnDoubleCheckClickListener +import com.wrbug.developerhelper.databinding.DialogBackupAppBinding +import com.wrbug.developerhelper.model.entity.BackupAppItemInfo +import com.wrbug.developerhelper.ui.widget.backupprogress.BackupProgressView +import com.wrbug.developerhelper.util.BackupUtils +import com.wrbug.developerhelper.util.format +import io.reactivex.rxjava3.core.Single +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.schedulers.Schedulers + + +class BackupAppDialog : BottomSheetDialogFragment() { + companion object { + fun show( + fragmentManager: FragmentManager, + apkInfo: ApkInfo?, + memo: String, + backupApk: Boolean, + backupData: Boolean, + backupAndroidData: Boolean + ) { + BackupAppDialog().apply { + arguments = Bundle().apply { + putParcelable(ExtraKey.DATA, apkInfo) + putBoolean(ExtraKey.KEY_1, backupApk) + putBoolean(ExtraKey.KEY_2, backupData) + putBoolean(ExtraKey.KEY_3, backupAndroidData) + putString(ExtraKey.KEY_4, memo) + } + }.show(fragmentManager, "BackupAppDialog") + } + } + + private lateinit var binding: DialogBackupAppBinding + private lateinit var disposable: CompositeDisposable + private lateinit var dateDir: String + private var backupTimeStamp: Long = 0 + + private val apkInfo: ApkInfo? by lazy { + arguments?.getParcelable(ExtraKey.DATA) + } + + private val backupApk by lazy { + arguments?.getBoolean(ExtraKey.KEY_1) ?: false + } + + private val memo by lazy { + arguments?.getString(ExtraKey.KEY_4).orEmpty() + } + + private val backupData by lazy { + arguments?.getBoolean(ExtraKey.KEY_2) ?: false + } + + + private val backupAndroidData by lazy { + arguments?.getBoolean(ExtraKey.KEY_3) ?: false + } + + private var successCount = 0 + set(value) { + field = value + checkIsAllSuccess() + } + private var hasError = false + set(value) { + field = value + checkIsAllSuccess() + } + + private fun checkIsAllSuccess() { + if (hasError) { + binding.tvNotice.isVisible = false + binding.btnExit.isVisible = true + binding.zipFileProgress.setStatus(BackupProgressView.Status.Canceled) + return + } + if (successCount < 3) { + return + } + createInfoFile() + } + + private fun createInfoFile() { + binding.zipFileProgress.setStatus(BackupProgressView.Status.Processing) + runOnIo().map { apkInfo -> + BackupUtils.zipBackupFile(apkInfo.packageInfo.packageName.orEmpty(), dateDir) + ?.let { apkInfo to it } + ?: throw Exception() + }.map { + val info = BackupAppItemInfo( + it.second.name, + backupApk, + backupData, + backupAndroidData, + it.first.generateBackupApkFileName(), + it.first.packageInfo.versionName.orEmpty(), + it.first.packageInfo.versionCodeLong, + it.first.packageInfo.packageName.orEmpty(), + backupTimeStamp, + memo = memo + ) + val success = + BackupUtils.saveBackupInfo(it.first, info, it.second.name) + if (success) { + return@map it.second.absolutePath + } + throw Exception() + }.observeOnMain().subscribe({ + binding.zipFileProgress.setStatus(BackupProgressView.Status.Success, it) + showSuccess() + }, { + hasError = true + binding.zipFileProgress.setStatus(BackupProgressView.Status.Failed) + }).addTo(disposable) + } + + private fun showSuccess() { + binding.btnExit.isVisible = true + binding.tvNotice.text = getString( + R.string.backup_success_notice, + BackupUtils.getAppBackupDir( + apkInfo?.packageInfo?.packageName.orEmpty() + ).absolutePath + ) + binding.tvNotice.setTextColor( + ContextCompat.getColor( + requireContext(), + R.color.material_color_green_600 + ) + ) + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + successCount = 0 + backupTimeStamp = System.currentTimeMillis() + dateDir = backupTimeStamp.format("yyyy-MM-dd-HH_mm_ss") + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + disposable = CompositeDisposable() + binding = DialogBackupAppBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + return super.onCreateDialog(savedInstanceState).apply { + setCancelable(false) + setCanceledOnTouchOutside(false) + } + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + binding.apkProgress.setTitle(R.string.item_backup_apk) + binding.androidDataProgress.setTitle(R.string.item_backup_android_data) + binding.dataProgress.setTitle(R.string.item_backup_data) + binding.zipFileProgress.setTitle(R.string.item_zip_backup_file) + binding.zipFileProgress.setStatus(BackupProgressView.Status.Waiting) + binding.btnExit.setOnDoubleCheckClickListener { + dismissAllowingStateLoss() + } + backupApk() + backupData() + backupAndroidData() + } + + private fun backupApk() { + if (!backupApk) { + successCount++ + binding.apkProgress.setStatus(BackupProgressView.Status.Ignore) + return + } + binding.apkProgress.setStatus(BackupProgressView.Status.Processing) + runOnIo().map { + BackupUtils.backupApk( + it.applicationInfo.packageName, + dateDir, + it.applicationInfo.publicSourceDir, + it.generateBackupApkFileName() + ) ?: throw Exception() + }.observeOnMain().subscribe({ + successCount++ + binding.apkProgress.setStatus(BackupProgressView.Status.Success, it) + }, { + hasError = true + binding.apkProgress.setStatus(BackupProgressView.Status.Failed) + }).addTo(disposable) + } + + override fun onDestroyView() { + super.onDestroyView() + disposable.dispose() + } + + override fun onDestroy() { + super.onDestroy() + if (hasError) { + ShellManager.rmFile( + BackupUtils.getCurrentAppBackupDir( + apkInfo?.packageInfo?.packageName.orEmpty(), + dateDir + ).absolutePath + ) + } + } + + private fun backupAndroidData() { + if (!backupAndroidData) { + successCount++ + binding.androidDataProgress.setStatus(BackupProgressView.Status.Ignore) + return + } + binding.androidDataProgress.setStatus(BackupProgressView.Status.Processing) + runOnIo().map { + BackupUtils.backupAppAndroidData(dateDir, it.applicationInfo.packageName) + ?: throw Exception() + }.observeOnMain().subscribe({ + successCount++ + binding.androidDataProgress.setStatus( + BackupProgressView.Status.Success, + it.ifEmpty { getString(R.string.no_need_to_backup) } + ) + }, { + hasError = true + binding.androidDataProgress.setStatus(BackupProgressView.Status.Failed) + }).addTo(disposable) + } + + private fun backupData() { + if (!backupData) { + successCount++ + binding.dataProgress.setStatus(BackupProgressView.Status.Ignore) + return + } + binding.dataProgress.setStatus(BackupProgressView.Status.Processing) + runOnIo().map { + BackupUtils.backupAppData(dateDir, it.applicationInfo.packageName) ?: throw Exception() + }.observeOnMain().subscribe({ + successCount++ + binding.dataProgress.setStatus(BackupProgressView.Status.Success, it.absolutePath) + }, { + hasError = true + binding.dataProgress.setStatus(BackupProgressView.Status.Failed) + }).addTo(disposable) + } + + + private fun runOnIo() = if (apkInfo == null) { + Single.error(Exception()) + } else { + Single.just(apkInfo!!).subscribeOn(Schedulers.newThread()) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/widget/backupprogress/BackupProgressView.kt b/app/src/main/java/com/wrbug/developerhelper/ui/widget/backupprogress/BackupProgressView.kt new file mode 100644 index 0000000..698bf87 --- /dev/null +++ b/app/src/main/java/com/wrbug/developerhelper/ui/widget/backupprogress/BackupProgressView.kt @@ -0,0 +1,98 @@ +package com.wrbug.developerhelper.ui.widget.backupprogress + +import android.content.Context +import android.content.res.ColorStateList +import android.util.AttributeSet +import android.view.LayoutInflater +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.core.content.ContextCompat +import androidx.core.view.isVisible +import com.wrbug.developerhelper.R +import com.wrbug.developerhelper.databinding.ViewBackupProgressBinding +import com.wrbug.developerhelper.util.getString + +class BackupProgressView @JvmOverloads constructor( + context: Context, attrs: AttributeSet? = null +) : ConstraintLayout(context, attrs) { + private val binding = ViewBackupProgressBinding.inflate(LayoutInflater.from(context), this) + + + fun setTitle(title: Int) { + binding.tvTitle.text = getString(title) + } + + fun setStatus(status: Status, successPath: String = "") { + when (status) { + Status.Processing -> { + binding.progress.isVisible = true + binding.ivStatus.isVisible = false + binding.tvStatus.text = context.getString(R.string.backuping_data) + } + + Status.Success -> { + binding.progress.isVisible = false + binding.ivStatus.isVisible = true + binding.ivStatus.setImageResource(R.drawable.ic_success) + binding.ivStatus.imageTintList = ColorStateList.valueOf( + ContextCompat.getColor( + context, + R.color.material_color_green_400 + ) + ) + binding.tvStatus.text = successPath + } + + Status.Failed -> { + binding.progress.isVisible = false + binding.ivStatus.isVisible = true + binding.tvStatus.text = context.getString(R.string.backup_fail) + binding.ivStatus.setImageResource(R.drawable.ic_fail) + binding.ivStatus.imageTintList = ColorStateList.valueOf( + ContextCompat.getColor( + context, + R.color.material_color_red_400 + ) + ) + + } + + Status.Ignore -> { + binding.progress.isVisible = false + binding.ivStatus.isVisible = true + binding.tvStatus.text = context.getString(R.string.ignore_backup) + binding.ivStatus.setImageResource(R.drawable.ic_ignore) + binding.ivStatus.imageTintList = ColorStateList.valueOf( + ContextCompat.getColor( + context, + R.color.material_color_grey_400 + ) + ) + } + + Status.Waiting -> { + binding.progress.isVisible = true + binding.ivStatus.isVisible = true + binding.tvStatus.text = context.getString(R.string.backup_waiting) + binding.ivStatus.setImageResource(R.drawable.ic_waiting) + binding.ivStatus.imageTintList = ColorStateList.valueOf( + ContextCompat.getColor(context, R.color.material_color_grey_400) + ) + } + + Status.Canceled -> { + binding.progress.isVisible = false + binding.ivStatus.isVisible = true + binding.ivStatus.setImageResource(R.drawable.ic_canceled) + binding.tvStatus.text = context.getString(R.string.canceled) + binding.ivStatus.imageTintList = ColorStateList.valueOf( + ContextCompat.getColor(context, R.color.material_color_grey_400) + ) + } + } + } + + + enum class Status { + Processing, Success, Failed, Ignore, Waiting, Canceled + } +} \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/widget/bottommenu/BottomMenu.kt b/app/src/main/java/com/wrbug/developerhelper/ui/widget/bottommenu/BottomMenu.kt deleted file mode 100644 index fcbc77c..0000000 --- a/app/src/main/java/com/wrbug/developerhelper/ui/widget/bottommenu/BottomMenu.kt +++ /dev/null @@ -1,93 +0,0 @@ -package com.wrbug.developerhelper.ui.widget.bottommenu - -import android.content.Context -import android.graphics.drawable.ColorDrawable -import android.os.Bundle -import android.view.View -import androidx.recyclerview.widget.DividerItemDecoration -import androidx.recyclerview.widget.LinearLayoutManager -import com.google.android.material.bottomsheet.BottomSheetDialog -import com.wrbug.developerhelper.R -import com.wrbug.developerhelper.commonutil.UiUtils -import com.wrbug.developerhelper.ui.decoration.SpaceItemDecoration -import kotlinx.android.synthetic.main.dialog_bottom_menu.* - -class BottomMenu(context: Context) : BottomSheetDialog(context), OnItemClickListener { - - private var title = "" - val adapter: BottomMenuItemAdapter by lazy { - BottomMenuItemAdapter(context) - } - - private var items: Array = arrayOf() - private var listener: OnItemClickListener? = null - - init { - setContentView(R.layout.dialog_bottom_menu) - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - initRv() - initView() - } - - private fun initView() { - titleTv.text = title - titleTv.visibility = if (title.isEmpty()) View.GONE else View.VISIBLE - } - - private fun initRv() { - menuListRv.layoutManager = LinearLayoutManager(context) - val decoration = SpaceItemDecoration(UiUtils.dp2px(context, 0F)) - decoration.setLastBottomPadding(UiUtils.dp2px(context, 10F)) - decoration.setFirstTopPadding(UiUtils.dp2px(context, 10F)) - menuListRv.addItemDecoration(decoration) - val dividerItemDecoration = DividerItemDecoration(context, DividerItemDecoration.VERTICAL) - dividerItemDecoration.setDrawable(ColorDrawable(context.resources.getColor(R.color.divider_color))) - menuListRv.addItemDecoration(dividerItemDecoration) - adapter.setOnItemClickListener(this) - menuListRv.adapter = adapter - adapter.list = items - } - - override fun onClick(position: Int) { - listener?.onClick(position) - dismiss() - } - - class Builder(val context: Context) { - private var title = "" - private var items: Array = arrayOf() - private var listener: OnItemClickListener? = null - - fun title(id: Int): Builder { - title = context.getString(id) - return this - } - - fun title(title: String): Builder { - this.title = title - return this - } - - fun menuItems(items: Array): Builder { - this.items = items - return this - } - - fun onItemClickListener(listener: OnItemClickListener?): Builder { - this.listener = listener - return this - } - - fun build(): BottomMenu { - val menu = BottomMenu(context) - menu.items = items - menu.title = title - menu.listener = listener - return menu - } - } - -} diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/widget/bottommenu/BottomMenuItemAdapter.kt b/app/src/main/java/com/wrbug/developerhelper/ui/widget/bottommenu/BottomMenuItemAdapter.kt deleted file mode 100644 index a8e5b9c..0000000 --- a/app/src/main/java/com/wrbug/developerhelper/ui/widget/bottommenu/BottomMenuItemAdapter.kt +++ /dev/null @@ -1,51 +0,0 @@ -package com.wrbug.developerhelper.ui.widget.bottommenu - -import android.content.Context -import android.view.Gravity -import android.view.View -import android.view.ViewGroup -import android.widget.TextView -import androidx.recyclerview.widget.RecyclerView -import com.wrbug.developerhelper.R -import com.wrbug.developerhelper.commonutil.UiUtils - -class BottomMenuItemAdapter(val context: Context) : RecyclerView.Adapter() { - var list: Array = arrayOf() - set(value) { - field = value - notifyDataSetChanged() - } - var listener: OnItemClickListener? = null - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { - val tv = TextView(context) - tv.setTextColor(context.resources.getColor(R.color.text_color_666666)) - tv.gravity = Gravity.CENTER - val dp10 = UiUtils.dp2px(context, 10F) - tv.setPadding(dp10, dp10, dp10, dp10) - val params = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) - tv.layoutParams = params - return ViewHolder(tv) - } - - override fun getItemCount(): Int = list.size - - override fun onBindViewHolder(holder: ViewHolder, position: Int) { - holder.tv.text = list[position] - holder.index = position - } - - fun setOnItemClickListener(listener: OnItemClickListener) { - this.listener = listener - } - - inner class ViewHolder(val tv: TextView) : RecyclerView.ViewHolder(tv) { - var index = 0 - - init { - tv.setOnClickListener { - listener?.onClick(index) - } - } - - } -} diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/widget/bottommenu/OnItemClickListener.kt b/app/src/main/java/com/wrbug/developerhelper/ui/widget/bottommenu/OnItemClickListener.kt deleted file mode 100644 index 2a4f90b..0000000 --- a/app/src/main/java/com/wrbug/developerhelper/ui/widget/bottommenu/OnItemClickListener.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.wrbug.developerhelper.ui.widget.bottommenu - -interface OnItemClickListener { - fun onClick(position: Int) -} \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/widget/boundsinfoview/BoundsInfoView.kt b/app/src/main/java/com/wrbug/developerhelper/ui/widget/boundsinfoview/BoundsInfoView.kt index 20d5d2e..828526c 100644 --- a/app/src/main/java/com/wrbug/developerhelper/ui/widget/boundsinfoview/BoundsInfoView.kt +++ b/app/src/main/java/com/wrbug/developerhelper/ui/widget/boundsinfoview/BoundsInfoView.kt @@ -17,9 +17,9 @@ class BoundsInfoView : View { field = value invalidate() } - private val edgeLineSize = UiUtils.dp2px(context,4F).toFloat() + private val edgeLineSize = UiUtils.dp2px(context, 4F).toFloat() private val paint = Paint() - var unit: Unit = Unit.DP + private var unit: Unit = Unit.DP set(value) { if (field == value) { return @@ -27,13 +27,21 @@ class BoundsInfoView : View { field = value invalidate() } - private val textMargin = UiUtils.dp2px(context,3F) + private val textMargin = UiUtils.dp2px(context, 3F) - constructor(context: Context) : super(context) + constructor(context: Context) : this(context, null) - constructor(context: Context, attrs: AttributeSet) : super(context, attrs) + constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) { + setOnClickListener { + toggleUnit() + } + } - override fun onDraw(canvas: Canvas?) { + private fun toggleUnit() { + unit = if (unit == Unit.DP) Unit.PX else Unit.DP + } + + override fun onDraw(canvas: Canvas) { val rect = drawRect(canvas) val lineRect = drawEdge(rect, canvas) drawAl(rect, lineRect, canvas) @@ -45,16 +53,16 @@ class BoundsInfoView : View { bounds?.let { paint.reset() paint.isAntiAlias = true - paint.textSize = UiUtils.dp2px(context,14F).toFloat() + paint.textSize = UiUtils.dp2px(context, 14F).toFloat() var leftMargin = it.left var topMargin = it.top var rightMargin = UiUtils.getDeviceWidth() - it.right var bottomMargin = UiUtils.getDeviceHeight() - it.bottom if (unit == Unit.DP) { - leftMargin = UiUtils.px2dp(context,leftMargin.toFloat()).toInt() - topMargin = UiUtils.px2dp(context,topMargin.toFloat()).toInt() - rightMargin = UiUtils.px2dp(context,rightMargin.toFloat()).toInt() - bottomMargin = UiUtils.px2dp(context,bottomMargin.toFloat()).toInt() + leftMargin = UiUtils.px2dp(context, leftMargin.toFloat()).toInt() + topMargin = UiUtils.px2dp(context, topMargin.toFloat()).toInt() + rightMargin = UiUtils.px2dp(context, rightMargin.toFloat()).toInt() + bottomMargin = UiUtils.px2dp(context, bottomMargin.toFloat()).toInt() } val leftMarginText = "$leftMargin ${unit.s}" val topMarginText = "$topMargin ${unit.s}" @@ -100,13 +108,13 @@ class BoundsInfoView : View { var width = it.right - it.left var height = it.bottom - it.top if (unit == Unit.DP) { - width = UiUtils.px2dp(context,width.toFloat()).toInt() - height = UiUtils.px2dp(context,height.toFloat()).toInt() + width = UiUtils.px2dp(context, width.toFloat()).toInt() + height = UiUtils.px2dp(context, height.toFloat()).toInt() } val text = "$width ${unit.s} × $height ${unit.s}" paint.reset() paint.isAntiAlias = true - paint.textSize = UiUtils.dp2px(context,14F).toFloat() + paint.textSize = UiUtils.dp2px(context, 14F).toFloat() val bounds = Rect() paint.getTextBounds(text, 0, text.length, bounds) val textWidth = bounds.width() @@ -127,7 +135,7 @@ class BoundsInfoView : View { paint.style = Paint.Style.STROKE paint.isAntiAlias = true paint.color = resources.getColor(R.color.colorAccent) - paint.strokeWidth = UiUtils.dp2px(context,1F).toFloat() + paint.strokeWidth = UiUtils.dp2px(context, 1F).toFloat() CanvasHelper.drawAL( rect.left, lineRect.left + edgeLineSize / 2, @@ -174,7 +182,12 @@ class BoundsInfoView : View { val lineWidth = measuredWidth / 6 val lineHeight = measuredHeight / 6 val lineRect = - RectF(rect.left / 2F, rect.top / 2F, measuredWidth - rect.left / 2F, measuredHeight - rect.top / 2F) + RectF( + rect.left / 2F, + rect.top / 2F, + measuredWidth - rect.left / 2F, + measuredHeight - rect.top / 2F + ) canvas?.run { drawLine( lineRect.left, @@ -213,7 +226,7 @@ class BoundsInfoView : View { paint.style = Paint.Style.STROKE paint.isAntiAlias = true paint.color = resources.getColor(R.color.colorAccent) - paint.strokeWidth = UiUtils.dp2px(context,2F).toFloat() + paint.strokeWidth = UiUtils.dp2px(context, 2F).toFloat() val width = measuredWidth / 3F val height = measuredHeight / 4F val top = measuredHeight * 3 / 8F @@ -224,6 +237,6 @@ class BoundsInfoView : View { enum class Unit(var s: String) { - DP("dp"), PX("dp") + DP("dp"), PX("px") } } diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/widget/emptyview/EmptyView.kt b/app/src/main/java/com/wrbug/developerhelper/ui/widget/emptyview/EmptyView.kt index e5c3504..92af967 100644 --- a/app/src/main/java/com/wrbug/developerhelper/ui/widget/emptyview/EmptyView.kt +++ b/app/src/main/java/com/wrbug/developerhelper/ui/widget/emptyview/EmptyView.kt @@ -3,48 +3,21 @@ package com.wrbug.developerhelper.ui.widget.emptyview import android.content.Context import android.util.AttributeSet import android.view.Gravity -import android.view.View +import android.view.LayoutInflater import android.widget.ImageView import android.widget.LinearLayout import android.widget.TextView +import androidx.constraintlayout.widget.ConstraintLayout import com.wrbug.developerhelper.R import com.wrbug.developerhelper.commonutil.UiUtils +import com.wrbug.developerhelper.databinding.ViewEmptyViewBinding -class EmptyView : LinearLayout { - val icoIv: ImageView by lazy { - val icoIv = ImageView(context) - icoIv.setImageResource(R.drawable.ic_ic_empty_666666) - icoIv - } - val textTv: TextView by lazy { - val tv = TextView(context) - tv.setTextColor(resources.getColor(R.color.text_color_666666)) - tv.text = "无数据" - tv.textSize = 20F - tv.gravity = Gravity.CENTER - val params = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT) - params.topMargin = UiUtils.dp2px(context, 20F) - tv.layoutParams = params - tv - } - - constructor(context: Context) : super(context) { - } - - constructor(context: Context, attributeSet: AttributeSet?) : super(context, attributeSet) { - init() - initView() - } - - private fun init() { - orientation = VERTICAL - gravity = Gravity.CENTER - } +class EmptyView @JvmOverloads constructor( + context: Context, attrs: AttributeSet? = null +) : ConstraintLayout(context, attrs) { + private val binding = ViewEmptyViewBinding.inflate(LayoutInflater.from(context), this) - private fun initView() { - addView(icoIv) - addView(textTv) + fun setTitle(title: String) { + binding.tvTitle.text = title } - - } \ No newline at end of file diff --git a/commonwidget/src/main/java/com/wrbug/developerhelper/commonwidget/flexibletoast/FlexibleToast.kt b/app/src/main/java/com/wrbug/developerhelper/ui/widget/flexibletoast/FlexibleToast.kt similarity index 63% rename from commonwidget/src/main/java/com/wrbug/developerhelper/commonwidget/flexibletoast/FlexibleToast.kt rename to app/src/main/java/com/wrbug/developerhelper/ui/widget/flexibletoast/FlexibleToast.kt index f492466..ddd5aa0 100644 --- a/commonwidget/src/main/java/com/wrbug/developerhelper/commonwidget/flexibletoast/FlexibleToast.kt +++ b/app/src/main/java/com/wrbug/developerhelper/ui/widget/flexibletoast/FlexibleToast.kt @@ -1,6 +1,8 @@ -package com.wrbug.developerhelper.commonwidget.flexibletoast +package com.wrbug.developerhelper.ui.widget.flexibletoast import android.content.Context +import android.os.Handler +import android.os.Looper import android.view.Gravity import android.view.LayoutInflater import android.view.View @@ -8,9 +10,36 @@ import android.widget.ImageView import android.widget.TextView import android.widget.Toast import com.wrbug.developerhelper.commonutil.UiUtils -import com.wrbug.developerhelper.commonwidget.R +import com.wrbug.developerhelper.databinding.LayoutToastFlexibleBinding -class FlexibleToast(private val mContext: Context) { +class FlexibleToast private constructor(private val mContext: Context) { + + companion object { + const val GRAVITY_BOTTOM = 0 + const val GRAVITY_CENTER = 1 + const val GRAVITY_TOP = 2 + const val TOAST_SHORT = 0 + const val TOAST_LONG = 1 + private var instance: FlexibleToast? = null + private fun getInstance(context: Context): FlexibleToast { + if (instance == null) { + instance = FlexibleToast(context.applicationContext) + } + return instance as FlexibleToast + + } + + fun toastShow(context: Context, msg: String) { + val toast = getInstance(context) + val builder = Builder(context).setGravity(GRAVITY_BOTTOM) + builder.setSecondText(msg) + if (Looper.myLooper() != Looper.getMainLooper()) { + Handler(Looper.getMainLooper()).post { toast.toastShow(builder) } + } else { + toast.toastShow(builder) + } + } + } private val flexibleToast: Toast by lazy { Toast(mContext) @@ -23,11 +52,13 @@ class FlexibleToast(private val mContext: Context) { 0, 0 ) + builder.mGravity == GRAVITY_TOP -> flexibleToast.setGravity( Gravity.TOP or Gravity.CENTER_VERTICAL, 0, UiUtils.dp2px(mContext, 20F) ) + else -> flexibleToast.setGravity( Gravity.BOTTOM or Gravity.CENTER_VERTICAL, 0, @@ -42,7 +73,7 @@ class FlexibleToast(private val mContext: Context) { if (builder.hasCustomerView && builder.mCustomerView != null) { flexibleToast.view = builder.mCustomerView } else { - flexibleToast.view = builder.mDefaultView + flexibleToast.view = builder.binding.root } flexibleToast.show() } @@ -51,46 +82,31 @@ class FlexibleToast(private val mContext: Context) { * 控制Toast的显示样式 */ class Builder(context: Context) { - val mDefaultView: View = LayoutInflater.from(context) - .inflate(R.layout.layout_toast_flexible, null) + val binding = LayoutToastFlexibleBinding.inflate(LayoutInflater.from(context)) var mCustomerView: View? = null - private val mIvImage: ImageView - private val mTvFirst: TextView - private val mTvSecond: TextView - - private val dividerFirst: View - private val dividerSecond: View var mDuration = Toast.LENGTH_SHORT// 0 short, 1 long var mGravity = 0 var hasCustomerView = false // 是否使用自定义layout - init { - mIvImage = mDefaultView.findViewById(R.id.imgIv) - mTvFirst = mDefaultView.findViewById(R.id.firstTv) - mTvSecond = mDefaultView.findViewById(R.id.secondTv) - dividerFirst = mDefaultView.findViewById(R.id.firstDividerView) - dividerSecond = mDefaultView.findViewById(R.id.secondDividerView) - } - fun setImageResource(resId: Int): Builder { - this.mIvImage.setImageResource(resId) - this.mIvImage.visibility = View.VISIBLE - this.dividerFirst.visibility = View.VISIBLE + binding.imgIv.setImageResource(resId) + binding.imgIv.visibility = View.VISIBLE + binding.firstDividerView.visibility = View.VISIBLE return this } fun setFirstText(firstText: String): Builder { - this.mTvFirst.text = firstText - this.mTvFirst.visibility = View.VISIBLE - this.dividerSecond.visibility = View.VISIBLE + binding.firstTv.text = firstText + binding.firstTv.visibility = View.VISIBLE + binding.secondDividerView.visibility = View.VISIBLE return this } fun setSecondText(secondText: String): Builder { - this.mTvSecond.text = secondText - this.mTvSecond.visibility = View.VISIBLE + binding.secondTv.text = secondText + binding.secondTv.visibility = View.VISIBLE return this } @@ -116,14 +132,4 @@ class FlexibleToast(private val mContext: Context) { } } - companion object { - - const val GRAVITY_BOTTOM = 0 - const val GRAVITY_CENTER = 1 - const val GRAVITY_TOP = 2 - const val TOAST_SHORT = 0 - const val TOAST_LONG = 1 - } - - } diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/widget/hierarchyView/HierarchyDetailView.kt b/app/src/main/java/com/wrbug/developerhelper/ui/widget/hierarchyView/HierarchyDetailView.kt index 1309219..fb54b6e 100644 --- a/app/src/main/java/com/wrbug/developerhelper/ui/widget/hierarchyView/HierarchyDetailView.kt +++ b/app/src/main/java/com/wrbug/developerhelper/ui/widget/hierarchyView/HierarchyDetailView.kt @@ -6,15 +6,16 @@ import android.graphics.Color import android.graphics.Paint import android.util.AttributeSet import android.widget.FrameLayout -import com.wrbug.developerhelper.model.entity.HierarchyNode +import com.wrbug.developerhelper.base.entry.HierarchyNode import com.wrbug.developerhelper.R import com.wrbug.developerhelper.ui.widget.helper.CanvasHelper import com.wrbug.developerhelper.commonutil.UiUtils class HierarchyDetailView : FrameLayout { + private val paint: Paint by lazy { val paint = Paint() - paint.color = context.resources.getColor(R.color.colorAccent) + paint.color = context.resources.getColor(R.color.material_color_blue_800) paint.style = Paint.Style.STROKE paint.strokeWidth = 3F paint @@ -34,7 +35,6 @@ class HierarchyDetailView : FrameLayout { initView() } - constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { initView() } @@ -50,14 +50,13 @@ class HierarchyDetailView : FrameLayout { setWillNotDraw(false) } - - override fun onDraw(canvas: Canvas?) { + override fun onDraw(canvas: Canvas) { super.onDraw(canvas) if (hierarchyNode != null) { - drawNode(canvas) if (hierarchyNode?.parentId!! > -1) { drawParentNode(canvas) } + drawNode(canvas) } } @@ -65,7 +64,7 @@ class HierarchyDetailView : FrameLayout { if (parentHierarchyNode == null) { return } - val bounds = parentHierarchyNode?.screenBounds + val bounds = parentHierarchyNode?.screenBounds ?: return canvas?.drawRect(bounds, parentpPaint) } diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/widget/hierarchyView/HierarchyView.kt b/app/src/main/java/com/wrbug/developerhelper/ui/widget/hierarchyView/HierarchyView.kt index fd4c119..93d95f5 100644 --- a/app/src/main/java/com/wrbug/developerhelper/ui/widget/hierarchyView/HierarchyView.kt +++ b/app/src/main/java/com/wrbug/developerhelper/ui/widget/hierarchyView/HierarchyView.kt @@ -7,8 +7,9 @@ import android.graphics.Paint import android.util.AttributeSet import android.view.MotionEvent import android.view.View -import com.wrbug.developerhelper.model.entity.HierarchyNode +import com.wrbug.developerhelper.base.entry.HierarchyNode import com.wrbug.developerhelper.service.DeveloperHelperAccessibilityService +import kotlin.math.pow class HierarchyView(context: Context, attrs: AttributeSet?) : View(context, attrs) { private val strokePaint = Paint() @@ -18,6 +19,7 @@ class HierarchyView(context: Context, attrs: AttributeSet?) : View(context, attr private var onHierarchyNodeClickListener: OnHierarchyNodeClickListener? = null private var selectedNode: HierarchyNode? = null private var selectedParentNode: HierarchyNode? = null + private var isFromLeftDown = false constructor(context: Context) : this(context, null) @@ -36,6 +38,7 @@ class HierarchyView(context: Context, attrs: AttributeSet?) : View(context, attr mHierarchyNodes.addAll(it) } nodeMap.putAll(DeveloperHelperAccessibilityService.nodeMap) + visibility = View.VISIBLE invalidate() } @@ -46,15 +49,25 @@ class HierarchyView(context: Context, attrs: AttributeSet?) : View(context, attr override fun onTouchEvent(event: MotionEvent?): Boolean { when (event?.action) { MotionEvent.ACTION_DOWN -> { + isFromLeftDown = event.x <= 15 + if (isFromLeftDown) { + return super.onTouchEvent(event) + } getSelectedNode(event.x, event.y) return true } + MotionEvent.ACTION_MOVE -> { - getSelectedNode(event.x, event.y) + if (!isFromLeftDown) { + getSelectedNode(event.x, event.y) + } } + MotionEvent.ACTION_UP -> { - selectedNode?.let { - onHierarchyNodeClickListener?.onClick(it, selectedParentNode) + if (!isFromLeftDown) { + selectedNode?.let { + onHierarchyNodeClickListener?.onClick(it, selectedParentNode) + } } } } @@ -69,15 +82,17 @@ class HierarchyView(context: Context, attrs: AttributeSet?) : View(context, attr } selectedNode = hierarchyNode.selectedNode selectedParentNode = hierarchyNode.parentNode - onHierarchyNodeClickListener?.onSelectedNodeChanged(hierarchyNode.selectedNode, hierarchyNode.parentNode) + onHierarchyNodeClickListener?.onSelectedNodeChanged( + hierarchyNode.selectedNode, hierarchyNode.parentNode + ) } } - override fun onDraw(canvas: Canvas?) { + override fun onDraw(canvas: Canvas) { drawRect(canvas, mHierarchyNodes) } - private fun drawRect(canvas: Canvas?, hierarchyNodes: List) { + private fun drawRect(canvas: Canvas, hierarchyNodes: List) { for (hierarchyNode in hierarchyNodes) { drawWidget(canvas, hierarchyNode) drawRect(canvas, hierarchyNode.childId) @@ -107,10 +122,10 @@ class HierarchyView(context: Context, attrs: AttributeSet?) : View(context, attr } else { var left = info.selectedNode.screenBounds?.left ?: 0 var top = info.selectedNode.screenBounds?.top ?: 0 - val len = Math.pow(x.toDouble() - left, 2.0) + Math.pow(y.toDouble() - top, 2.0) + val len = (x.toDouble() - left).pow(2.0) + (y.toDouble() - top).pow(2.0) left = list[index].selectedNode.screenBounds?.left ?: 0 top = list[index].selectedNode.screenBounds?.top ?: 0 - val len1 = Math.pow(x.toDouble() - left, 2.0) + Math.pow(y.toDouble() - top, 2.0) + val len1 = (x.toDouble() - left).pow(2.0) + (y.toDouble() - top).pow(2.0) if (len1 < len) { info = list[index] } @@ -119,10 +134,12 @@ class HierarchyView(context: Context, attrs: AttributeSet?) : View(context, attr return info } - private fun getNode(x: Float, y: Float, hierarchyNode: HierarchyNode, list: ArrayList) { + private fun getNode( + x: Float, y: Float, hierarchyNode: HierarchyNode, list: ArrayList + ) { val rect = hierarchyNode.screenBounds ?: return if (rect.contains(x.toInt(), y.toInt())) { - if (!hierarchyNode.childId.isEmpty()) { + if (hierarchyNode.childId.isNotEmpty()) { for (child in hierarchyNode.childId.reversed()) { getNode(x, y, child, list) } diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/widget/hierarchyView/SelectedNodeInfo.kt b/app/src/main/java/com/wrbug/developerhelper/ui/widget/hierarchyView/SelectedNodeInfo.kt index c9bfae6..c960bfd 100644 --- a/app/src/main/java/com/wrbug/developerhelper/ui/widget/hierarchyView/SelectedNodeInfo.kt +++ b/app/src/main/java/com/wrbug/developerhelper/ui/widget/hierarchyView/SelectedNodeInfo.kt @@ -1,7 +1,6 @@ package com.wrbug.developerhelper.ui.widget.hierarchyView -import android.graphics.Rect -import com.wrbug.developerhelper.model.entity.HierarchyNode +import com.wrbug.developerhelper.base.entry.HierarchyNode class SelectedNodeInfo(var selectedNode: HierarchyNode, var parentNode: HierarchyNode?) { diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/widget/layoutinfoview/LayoutInfoDialog.kt b/app/src/main/java/com/wrbug/developerhelper/ui/widget/layoutinfoview/LayoutInfoDialog.kt new file mode 100644 index 0000000..96bdc1f --- /dev/null +++ b/app/src/main/java/com/wrbug/developerhelper/ui/widget/layoutinfoview/LayoutInfoDialog.kt @@ -0,0 +1,100 @@ +package com.wrbug.developerhelper.ui.widget.layoutinfoview + +import android.app.Dialog +import android.os.Bundle +import android.view.Gravity +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.DialogFragment +import androidx.fragment.app.FragmentManager +import com.google.android.material.bottomsheet.BottomSheetDialogFragment +import com.wrbug.developerhelper.R +import com.wrbug.developerhelper.base.ExtraKey +import com.wrbug.developerhelper.base.entry.HierarchyNode +import com.wrbug.developerhelper.commonutil.UiUtils +import com.wrbug.developerhelper.commonutil.dp2px +import com.wrbug.developerhelper.databinding.ViewLayoutInfoBinding +import com.wrbug.developerhelper.util.isPortrait + +class LayoutInfoDialog : DialogFragment() { + + companion object { + fun show( + fragmentManager: FragmentManager, + list: List?, + selectedNode: HierarchyNode, + listener: ((HierarchyNode, HierarchyNode?) -> Unit) + ) { + LayoutInfoDialog().apply { + arguments = Bundle().apply { + putParcelableArrayList(ExtraKey.DATA, list?.let { ArrayList(it) }) + putParcelable(ExtraKey.SELECTED, selectedNode) + } + onNodeChangedListener = listener + }.show(fragmentManager, "LayoutInfoDialog") + } + } + + private val nodeList: List? by lazy { + arguments?.getParcelableArrayList(ExtraKey.DATA) + } + private val hierarchyNode: HierarchyNode? by lazy { + arguments?.getParcelable(ExtraKey.SELECTED) + } + private var onNodeChangedListener: ((HierarchyNode, HierarchyNode?) -> Unit)? = null + + val adapter by lazy { + LayoutInfoViewPagerAdapter(requireContext(), nodeList, hierarchyNode!!) + } + + private lateinit var binding: ViewLayoutInfoBinding + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setStyle(STYLE_NORMAL, R.style.FullScreenDialog) + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + binding = ViewLayoutInfoBinding.inflate(layoutInflater) + return binding.root + } + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + return super.onCreateDialog(savedInstanceState).apply { + setCanceledOnTouchOutside(true) + window?.run { + val layoutParams = attributes + if (isPortrait()) { + layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT + layoutParams.height = UiUtils.getDeviceHeight() / 2 + setGravity(Gravity.BOTTOM) + } else { + layoutParams.width = UiUtils.getDeviceWidth() / 2 + layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT + setGravity(Gravity.END) + } + attributes = layoutParams + } + } + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + if (dialog?.window?.attributes?.gravity != Gravity.BOTTOM) { + binding.layoutInfoContainer.setPadding(0, UiUtils.getStatusHeight(), 0, 0) + } + onNodeChangedListener?.let { + adapter.setOnNodeChangedListener(it) + } + initViewpager() + } + + private fun initViewpager() { + binding.viewPager.adapter = adapter + binding.tabLayout.setupWithViewPager(binding.viewPager) + } +} diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/widget/layoutinfoview/LayoutInfoView.kt b/app/src/main/java/com/wrbug/developerhelper/ui/widget/layoutinfoview/LayoutInfoView.kt deleted file mode 100644 index f077e16..0000000 --- a/app/src/main/java/com/wrbug/developerhelper/ui/widget/layoutinfoview/LayoutInfoView.kt +++ /dev/null @@ -1,38 +0,0 @@ -package com.wrbug.developerhelper.ui.widget.layoutinfoview - -import android.content.Context -import com.google.android.material.bottomsheet.BottomSheetDialog -import com.wrbug.developerhelper.R -import com.wrbug.developerhelper.model.entity.HierarchyNode -import com.wrbug.developerhelper.commonutil.UiUtils -import kotlinx.android.synthetic.main.view_layout_info.* - - -class LayoutInfoView( - context: Context, - private val nodeList: List?, - private val hierarchyNode: HierarchyNode -) : BottomSheetDialog(context) { - val adapter = LayoutInfoViewPagerAdapter(context, nodeList, hierarchyNode) - - init { - init() - } - - fun setOnNodeChangedListener(listener: OnNodeChangedListener) { - adapter.setOnNodeChangedListener(listener) - } - - private fun init() { - setContentView(R.layout.view_layout_info) - val layoutParams = layoutInfoContainer.layoutParams - layoutParams.height = UiUtils.getDeviceHeight(context) / 2 - layoutInfoContainer.layoutParams = layoutParams - initViewpager() - } - - private fun initViewpager() { - viewPager.adapter = adapter - tabLayout.setupWithViewPager(viewPager) - } -} diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/widget/layoutinfoview/LayoutInfoViewPagerAdapter.kt b/app/src/main/java/com/wrbug/developerhelper/ui/widget/layoutinfoview/LayoutInfoViewPagerAdapter.kt index 3c6c383..bb9148e 100644 --- a/app/src/main/java/com/wrbug/developerhelper/ui/widget/layoutinfoview/LayoutInfoViewPagerAdapter.kt +++ b/app/src/main/java/com/wrbug/developerhelper/ui/widget/layoutinfoview/LayoutInfoViewPagerAdapter.kt @@ -9,7 +9,7 @@ import androidx.recyclerview.widget.RecyclerView import androidx.viewpager.widget.PagerAdapter import androidx.viewpager.widget.ViewPager import com.wrbug.developerhelper.R -import com.wrbug.developerhelper.model.entity.HierarchyNode +import com.wrbug.developerhelper.base.entry.HierarchyNode import com.wrbug.developerhelper.ui.widget.boundsinfoview.BoundsInfoView import com.wrbug.developerhelper.ui.widget.layoutinfoview.infopage.InfoAdapter import com.wrbug.developerhelper.ui.widget.layoutinfoview.infopage.ItemInfo @@ -30,7 +30,7 @@ class LayoutInfoViewPagerAdapter( private val infoAdapter: InfoAdapter = InfoAdapter(context) private lateinit var graphView: GraphView private val boundsInfoView = BoundsInfoView(context) - private var onNodeChangedListener: OnNodeChangedListener? = null + private var onNodeChangedListener: ((HierarchyNode, HierarchyNode?) -> Unit)? = null init { initInfoTab() @@ -38,14 +38,14 @@ class LayoutInfoViewPagerAdapter( initViewTreeTab() } - fun setOnNodeChangedListener(listener: OnNodeChangedListener) { + fun setOnNodeChangedListener(listener: (HierarchyNode, HierarchyNode?) -> Unit) { onNodeChangedListener = listener } private fun initViewTreeTab() { tabList.add("ViewTree") graphView = - LayoutInflater.from(context).inflate(R.layout.layout_hierarchy_tree, null) as GraphView + LayoutInflater.from(context).inflate(R.layout.layout_hierarchy_tree, null) as GraphView val adapter = ViewTreeGraphAdapter(context, R.layout.item_tree_node_view) graphView.adapter = adapter adapter.setOnItemClickListener(object : ViewTreeGraphAdapter.OnItemClickListener { @@ -54,9 +54,8 @@ class LayoutInfoViewPagerAdapter( resetInfoTab() resetLayoutTable() resetViewTreeTab(node) - onNodeChangedListener?.onChanged(node.node, node.parent?.node) + onNodeChangedListener?.invoke(node.node, node.parent?.node) } - }) val configuration = BuchheimWalkerConfiguration.Builder() .setSiblingSeparation(100) @@ -151,7 +150,7 @@ class LayoutInfoViewPagerAdapter( "${resourceId.replace(packageName, "app")}[${idHex?.replace("#", "0x") ?: "NO_ID"}]" ) ) - if (!text.isEmpty()) { + if (text.isNotEmpty()) { list.add(ItemInfo("Text", text)) } list.add(ItemInfo("Enable", enabled)) diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/widget/layoutinfoview/OnNodeChangedListener.kt b/app/src/main/java/com/wrbug/developerhelper/ui/widget/layoutinfoview/OnNodeChangedListener.kt deleted file mode 100644 index 2aa5773..0000000 --- a/app/src/main/java/com/wrbug/developerhelper/ui/widget/layoutinfoview/OnNodeChangedListener.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.wrbug.developerhelper.ui.widget.layoutinfoview - -import com.wrbug.developerhelper.model.entity.HierarchyNode - -interface OnNodeChangedListener { - fun onChanged(node: HierarchyNode, parentNode: HierarchyNode?) -} \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/widget/layoutinfoview/ViewTreeGraphAdapter.kt b/app/src/main/java/com/wrbug/developerhelper/ui/widget/layoutinfoview/ViewTreeGraphAdapter.kt index bafec3c..1871acd 100644 --- a/app/src/main/java/com/wrbug/developerhelper/ui/widget/layoutinfoview/ViewTreeGraphAdapter.kt +++ b/app/src/main/java/com/wrbug/developerhelper/ui/widget/layoutinfoview/ViewTreeGraphAdapter.kt @@ -9,7 +9,7 @@ import androidx.cardview.widget.CardView import com.wrbug.developerhelper.R import de.blox.graphview.BaseGraphAdapter -class ViewTreeGraphAdapter(@NonNull val context: Context, @LayoutRes val layoutRes: Int) : +class ViewTreeGraphAdapter(val context: Context, @LayoutRes val layoutRes: Int) : BaseGraphAdapter(context, layoutRes) { private var listener: OnItemClickListener? = null override fun onCreateViewHolder(view: View?) = ViewHolder(view!!) @@ -18,6 +18,7 @@ class ViewTreeGraphAdapter(@NonNull val context: Context, @LayoutRes val layoutR val node = data as ViewTreeGraphNode widgetTv.text = node.node.widget cardView.setOnClickListener { + widgetTv.text = node.node.widget listener?.onClick(node, position) } when { @@ -27,13 +28,6 @@ class ViewTreeGraphAdapter(@NonNull val context: Context, @LayoutRes val layoutR cardView.setCardBackgroundColor(context.resources.getColor(R.color.colorPrimary)) if (node.shortName) { widgetTv.text = toShortName(node.node.widget) - cardView.setOnClickListener { - widgetTv.text = node.node.widget - node.shortName = false - cardView.setOnClickListener{ - listener?.onClick(node, position) - } - } } else { widgetTv.text = node.node.widget } diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/widget/layoutinfoview/ViewTreeGraphNode.kt b/app/src/main/java/com/wrbug/developerhelper/ui/widget/layoutinfoview/ViewTreeGraphNode.kt index 5827dc7..21a3846 100644 --- a/app/src/main/java/com/wrbug/developerhelper/ui/widget/layoutinfoview/ViewTreeGraphNode.kt +++ b/app/src/main/java/com/wrbug/developerhelper/ui/widget/layoutinfoview/ViewTreeGraphNode.kt @@ -1,6 +1,6 @@ package com.wrbug.developerhelper.ui.widget.layoutinfoview -import com.wrbug.developerhelper.model.entity.HierarchyNode +import com.wrbug.developerhelper.base.entry.HierarchyNode class ViewTreeGraphNode(val node: HierarchyNode) { var selected: Boolean = false diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/widget/layoutinfoview/infopage/InfoAdapter.kt b/app/src/main/java/com/wrbug/developerhelper/ui/widget/layoutinfoview/infopage/InfoAdapter.kt index 4a66f6f..f37bfe6 100644 --- a/app/src/main/java/com/wrbug/developerhelper/ui/widget/layoutinfoview/infopage/InfoAdapter.kt +++ b/app/src/main/java/com/wrbug/developerhelper/ui/widget/layoutinfoview/infopage/InfoAdapter.kt @@ -2,25 +2,56 @@ package com.wrbug.developerhelper.ui.widget.layoutinfoview.infopage import android.content.Context import android.view.LayoutInflater -import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView -import com.wrbug.developerhelper.R +import com.airbnb.lottie.LottieDrawable +import com.wrbug.developerhelper.commonutil.ClipboardUtils import com.wrbug.developerhelper.commonutil.print -import kotlinx.android.synthetic.main.item_view_info.view.* +import com.wrbug.developerhelper.util.setOnDoubleCheckClickListener +import com.wrbug.developerhelper.util.visible +import com.wrbug.developerhelper.databinding.ItemInfoLoadingBinding +import com.wrbug.developerhelper.databinding.ItemViewInfoBinding -class InfoAdapter(val context: Context) : RecyclerView.Adapter() { - private val list = arrayListOf() - override fun onCreateViewHolder(p0: ViewGroup, p1: Int): ViewHolder { - return ViewHolder(LayoutInflater.from(context).inflate(R.layout.item_view_info, p0, false)) +class InfoAdapter(val context: Context, private val topItem: ItemInfo? = null) : + RecyclerView.Adapter() { + companion object { + private const val VIEW_TYPE_ITEM = 0 + private const val VIEW_TYPE_LOADING = 1 + } + + private val list = arrayListOf() + override fun onCreateViewHolder(p0: ViewGroup, p1: Int): RecyclerView.ViewHolder { + if (p1 == VIEW_TYPE_LOADING) { + return LoadingViewHolder( + ItemInfoLoadingBinding.inflate( + LayoutInflater.from(p0.context), p0, false + ) + ) + } + return InfoViewHolder( + ItemViewInfoBinding.inflate( + LayoutInflater.from(p0.context), p0, false + ) + ) + } + + override fun getItemViewType(position: Int): Int { + return if (list[position] is LoadingItem) { + VIEW_TYPE_LOADING + } else { + VIEW_TYPE_ITEM + } } override fun getItemCount(): Int { return list.size } - fun setItems(list: ArrayList) { + fun setItems(list: List) { this.list.clear() + topItem?.let { + this.list.add(it) + } if (list.isEmpty().not()) { this.list.addAll(list) } @@ -35,15 +66,27 @@ class InfoAdapter(val context: Context) : RecyclerView.Adapter Unit) { + setOnClickListener(View.OnClickListener { v -> v.onclick() }) + } } diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/widget/layoutinfoview/infopage/LoadingItem.kt b/app/src/main/java/com/wrbug/developerhelper/ui/widget/layoutinfoview/infopage/LoadingItem.kt new file mode 100644 index 0000000..a0dc06e --- /dev/null +++ b/app/src/main/java/com/wrbug/developerhelper/ui/widget/layoutinfoview/infopage/LoadingItem.kt @@ -0,0 +1,4 @@ +package com.wrbug.developerhelper.ui.widget.layoutinfoview.infopage + +object LoadingItem { +} \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/ui/widget/settingitemview/SettingItemView.kt b/app/src/main/java/com/wrbug/developerhelper/ui/widget/settingitemview/SettingItemView.kt index 4128255..8fed9fa 100644 --- a/app/src/main/java/com/wrbug/developerhelper/ui/widget/settingitemview/SettingItemView.kt +++ b/app/src/main/java/com/wrbug/developerhelper/ui/widget/settingitemview/SettingItemView.kt @@ -1,17 +1,25 @@ package com.wrbug.developerhelper.ui.widget.settingitemview import android.content.Context +import android.content.res.ColorStateList import android.graphics.drawable.Drawable -import android.text.TextUtils import android.util.AttributeSet import android.view.LayoutInflater import android.view.View import android.widget.CompoundButton -import android.widget.FrameLayout +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.core.view.isGone +import androidx.core.view.isVisible +import androidx.core.view.updatePadding import com.wrbug.developerhelper.R -import kotlinx.android.synthetic.main.view_setting_item.view.* +import com.wrbug.developerhelper.commonutil.dpInt +import com.wrbug.developerhelper.util.setOnDoubleCheckClickListener +import com.wrbug.developerhelper.databinding.ViewSettingItemBinding + +class SettingItemView @JvmOverloads constructor( + context: Context, attrs: AttributeSet? = null +) : ConstraintLayout(context, attrs) { -class SettingItemView : FrameLayout { var checkable: Boolean = true set(value) { field = value @@ -21,35 +29,34 @@ class SettingItemView : FrameLayout { var checked: Boolean = false set(value) { field = value - switcher.isChecked = checked + binding.switcher.isChecked = checked } + private var switcherMaskViewClicked = false + private val binding = ViewSettingItemBinding.inflate(LayoutInflater.from(context), this) - - constructor(context: Context) : super(context) { - initView() - } - - constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { + init { initView() initAttrs(attrs) } private fun initView() { - LayoutInflater.from(context).inflate(R.layout.view_setting_item, this) + updatePadding(top = 8.dpInt(context), bottom = 8.dpInt(context)) setBackgroundResource(R.drawable.ripple_with_color_mask) } - private fun initAttrs(attrs: AttributeSet) { + private fun initAttrs(attrs: AttributeSet?) { + attrs ?: return with(context.obtainStyledAttributes(attrs, R.styleable.SettingItemView)) { val src = getDrawable(R.styleable.SettingItemView_src) - src?.let { - setImage(it) + setImage(src) + getColorStateList(R.styleable.SettingItemView_icoTint)?.let { + setIconTint(it) } val title = getString(R.styleable.SettingItemView_title) - titleTv.text = title + binding.titleTv.text = title setSummary(getString(R.styleable.SettingItemView_summary)) val switchVisible = getBoolean(R.styleable.SettingItemView_switchVisible, true) - switcher.visibility = if (switchVisible) View.VISIBLE else View.GONE + binding.switcher.visibility = if (switchVisible) View.VISIBLE else View.GONE checked = getBoolean(R.styleable.SettingItemView_checked, false) checkable = getBoolean(R.styleable.SettingItemView_checkable, true) recycle() @@ -57,40 +64,40 @@ class SettingItemView : FrameLayout { } + private fun setIconTint(colorStateList: ColorStateList?) { + binding.icoIv.imageTintList = colorStateList + } + override fun setOnClickListener(l: OnClickListener?) { super.setOnClickListener(l) - switcherMaskView.setOnClickListener(l) + if (switcherMaskViewClicked.not()) { + binding.switcherMaskView.setOnClickListener(l) + } + } + + fun setOnSwitcherClickListener(listener: View.() -> Unit) { + switcherMaskViewClicked = true + binding.switcherMaskView.setOnDoubleCheckClickListener(clickListener = listener) } - fun isChecked() = switcher.isChecked + fun isChecked() = binding.switcher.isChecked private fun setSwitchCheckable() { - if (checkable) { -// switcher.setOnTouchListener(null) - switcherMaskView.visibility = View.GONE - } else { -// switcher.setOnTouchListener { _, _ -> true } - switcherMaskView.visibility = View.VISIBLE - } + binding.switcherMaskView.isGone = checkable } fun setOnCheckedChangeListener(listener: CompoundButton.OnCheckedChangeListener) { - switcher.setOnCheckedChangeListener(listener) + binding.switcher.setOnCheckedChangeListener(listener) } - fun setImage(drawable: Drawable) { - icoIv.setImageDrawable(drawable) - icoIv.visibility = View.VISIBLE + fun setImage(drawable: Drawable?) { + binding.icoIv.setImageDrawable(drawable) + binding.icoIv.isVisible = drawable != null } fun setSummary(summary: String?) { - summary.takeIf { - !TextUtils.isEmpty(it) - }?.let { - summaryTv.text = it - summaryTv.visibility = View.VISIBLE - } + binding.summaryTv.text = summary + binding.summaryTv.isVisible = !summary.isNullOrEmpty() } - } \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/util/AppStatusRegister.kt b/app/src/main/java/com/wrbug/developerhelper/util/AppStatusRegister.kt new file mode 100644 index 0000000..1d49973 --- /dev/null +++ b/app/src/main/java/com/wrbug/developerhelper/util/AppStatusRegister.kt @@ -0,0 +1,56 @@ +package com.wrbug.developerhelper.util + +import android.app.Activity +import android.app.Application +import android.os.Bundle + +object AppStatusRegister { + private var count = 0 + private val backgroundMap = hashMapOf Unit>() + fun init(application: Application) { + application.registerActivityLifecycleCallbacks(object : + Application.ActivityLifecycleCallbacks { + override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) { + + } + + override fun onActivityStarted(activity: Activity) { + count++ + } + + override fun onActivityResumed(activity: Activity) { + } + + override fun onActivityPaused(activity: Activity) { + } + + override fun onActivityStopped(activity: Activity) { + count-- + backgroundCheck() + } + + override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) { + } + + override fun onActivityDestroyed(activity: Activity) { + } + + }) + } + + + fun registerBackgroundListener(key: String, listener: () -> Unit) { + backgroundMap[key] = listener + } + + fun removeBackgroundListener(key: String) { + backgroundMap.remove(key) + } + + private fun backgroundCheck() { + if (count == 0) { + backgroundMap.values.forEach { it() } + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/util/BackupUtils.kt b/app/src/main/java/com/wrbug/developerhelper/util/BackupUtils.kt index 90f8bf3..8ba7977 100644 --- a/app/src/main/java/com/wrbug/developerhelper/util/BackupUtils.kt +++ b/app/src/main/java/com/wrbug/developerhelper/util/BackupUtils.kt @@ -1,34 +1,176 @@ package com.wrbug.developerhelper.util -import android.net.Uri import android.os.Environment +import com.wrbug.developerhelper.commonutil.Constant +import com.wrbug.developerhelper.commonutil.entity.ApkInfo +import com.wrbug.developerhelper.commonutil.fromJson +import com.wrbug.developerhelper.commonutil.safeCreateSingle +import com.wrbug.developerhelper.commonutil.safeRead import com.wrbug.developerhelper.commonutil.shell.ShellManager -import com.wrbug.developerhelper.commonutil.zip +import com.wrbug.developerhelper.commonutil.toJson +import com.wrbug.developerhelper.model.entity.BackupAppData +import com.wrbug.developerhelper.model.entity.BackupAppInfo +import com.wrbug.developerhelper.model.entity.BackupAppItemInfo +import io.reactivex.rxjava3.core.Single +import net.dongliu.apk.parser.ApkFile import java.io.File object BackupUtils { - private val backupDir: File by lazy { - val file = File(Environment.getExternalStorageDirectory(), "com.wrbug.developerHelper/backup") + private const val ANDROID_DATA_TAR = "android_data.tar" + private const val DATA_TAR = "data.tar" + private const val CONFIG_JSON = "config.json" + private const val ICON_PNG = "icon.png" + private val backupRootDir: File by lazy { + val file = + File(Environment.getExternalStorageDirectory(), "DeveloperHelper/backup") if (file.exists().not()) { file.mkdirs() } file } - fun backupApk(packageName: String, apkPath: String, fileName: String): Uri? { - val apkDir = File(backupDir, "apks/$packageName/$fileName") + fun getCurrentAppBackupDir(packageName: String, dateDir: String): File { + return File(getAppBackupDir(packageName), dateDir) + } + + fun getAppBackupDir(packageName: String): File { + return File(backupRootDir, packageName).apply { + if (!exists()) { + mkdirs() + } + } + } + + fun backupApk( + packageName: String, + dateDir: String, + apkPath: String, + fileName: String + ): String? { + val apkDir = File(getCurrentAppBackupDir(packageName, dateDir), fileName) if (ShellManager.cpFile(apkPath, apkDir.absolutePath)) { - return apkDir.toUri() + return apkDir.absolutePath } return null } - fun backupAppData(packageName: String, dataDir: String): File? { - val backupDataDir = - File(backupDir, "datas/$packageName/${System.currentTimeMillis().format("yyyy-MM-dd-HH_mm_ss")}") - if (ShellManager.cpFile(dataDir, backupDataDir.absolutePath)) { + fun backupAppData(dateDir: String, packageName: String): File? { + val backupDataDir = File(getCurrentAppBackupDir(packageName, dateDir), DATA_TAR) + val dataDir = Constant.getDataDir(packageName) + if (ShellManager.tarCF(backupDataDir.absolutePath, dataDir)) { return backupDataDir } return null } + + fun backupAppAndroidData(dateDir: String, packageName: String): String? { + val backupDataDir = File( + getCurrentAppBackupDir(packageName, dateDir), ANDROID_DATA_TAR + ) + val dataDir = + Environment.getExternalStorageDirectory().absolutePath + "/Android/data/" + packageName + if (!File(dataDir).exists()) { + return "" + } + if (ShellManager.tarCF(backupDataDir.absolutePath, dataDir)) { + return backupDataDir.absolutePath + } + return null + } + + fun saveBackupInfo( + apkInfo: ApkInfo, + backupAppItemInfo: BackupAppItemInfo, + tarFile: String + ): Boolean { + val apkFile = apkInfo.applicationInfo.publicSourceDir + val tmpApkFile = File(getAppBackupDir(apkInfo.applicationInfo.packageName), "tmp.apk") + ShellManager.cpFile(apkFile, tmpApkFile.absolutePath) + runCatching { + ApkFile(tmpApkFile).allIcons.find { it.isFile }?.data?.let { + File(getAppBackupDir(apkInfo.applicationInfo.packageName), ICON_PNG).writeBytes(it) + } + } + tmpApkFile.delete() + backupAppItemInfo.androidDataFile = ANDROID_DATA_TAR + backupAppItemInfo.dataFile = DATA_TAR + val configFile = File(getAppBackupDir(apkInfo.applicationInfo.packageName), CONFIG_JSON) + val info = configFile.safeRead().fromJson() ?: BackupAppInfo() + info.appName = apkInfo.getAppName() + info.packageName = apkInfo.applicationInfo.packageName + info.backupMap[tarFile] = backupAppItemInfo + configFile.writeText(info.toJson().orEmpty()) + return true + } + + + fun zipBackupFile(packageName: String, dateDir: String): File? { + val target = File(getAppBackupDir(packageName), "$dateDir.tar") + val src = getCurrentAppBackupDir(packageName, dateDir).absolutePath + if (ShellManager.tarCF(target.absolutePath, src)) { + ShellManager.rmFile(src) + return target + } + return null + } + + fun getAllBackupInfo(): Single> { + return safeCreateSingle { + val list = arrayListOf() + backupRootDir.listFiles()?.forEach { root -> + val configJson = File(root, CONFIG_JSON) + if (!configJson.exists()) { + return@forEach + } + val info = configJson.safeRead().fromJson() ?: return@forEach + val map = info.backupMap.filter { File(root, it.key).exists() } + if (map.isEmpty()) { + configJson.delete() + return@forEach + } + val icoFile = File(root, ICON_PNG).takeIf { it.exists() } + list.add(BackupAppData(info.appName, info.packageName, root, HashMap(map), icoFile)) + } + it.onSuccess(list) + } + } + + fun getBackupAppInfo(packageName: String): Single { + return safeCreateSingle { + val dir = backupRootDir.listFiles { _, name -> name == packageName }?.getOrNull(0) + val configJson = File(dir, CONFIG_JSON) + val info = configJson.safeRead().fromJson() + if (info == null) { + it.onError(Exception()) + return@safeCreateSingle + } + it.onSuccess(info) + } + } + + fun deleteBackupItem(packageName: String, tarFile: String): Single { + return safeCreateSingle { + val dir = backupRootDir.listFiles { _, name -> name == packageName }?.getOrNull(0) + val backupArch = dir?.let { File(it, tarFile) } + if (dir == null || !dir.exists() || backupArch?.exists() != true) { + it.onError(Exception()) + return@safeCreateSingle + } + backupArch.delete() + val configJson = File(dir, CONFIG_JSON) + val info = configJson.safeRead().fromJson() + if (info == null) { + it.onError(Exception()) + return@safeCreateSingle + } + val newInfo = info.copy( + backupMap = info.backupMap.apply { remove(tarFile) } + ) + configJson.writeText(newInfo.toJson().orEmpty()) + it.onSuccess(newInfo) + } + + } + + } \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/util/DateUtilsExt.kt b/app/src/main/java/com/wrbug/developerhelper/util/DateUtilsExt.kt index 3e6e0cd..6fc9513 100644 --- a/app/src/main/java/com/wrbug/developerhelper/util/DateUtilsExt.kt +++ b/app/src/main/java/com/wrbug/developerhelper/util/DateUtilsExt.kt @@ -1,9 +1,11 @@ package com.wrbug.developerhelper.util +import android.annotation.SuppressLint import java.text.SimpleDateFormat import java.util.* +@SuppressLint("SimpleDateFormat") fun Date.format(format: String="yyyy-MM-dd HH:mm:ss"): String { val simpleDateFormat = SimpleDateFormat(format) return simpleDateFormat.format(this) diff --git a/app/src/main/java/com/wrbug/developerhelper/util/DeviceUtils.kt b/app/src/main/java/com/wrbug/developerhelper/util/DeviceUtils.kt index b44b4c9..59742b1 100644 --- a/app/src/main/java/com/wrbug/developerhelper/util/DeviceUtils.kt +++ b/app/src/main/java/com/wrbug/developerhelper/util/DeviceUtils.kt @@ -3,9 +3,10 @@ package com.wrbug.developerhelper.util import android.content.Context import android.os.Build import android.provider.Settings -import android.text.TextUtils -import com.wrbug.developerhelper.basecommon.BaseApp -import com.wrbug.developerhelper.commonutil.ShellUtils +import android.util.DisplayMetrics +import android.view.WindowManager +import com.wrbug.developerhelper.base.BaseApp +import com.wrbug.developerhelper.commonutil.shell.ShellUtils object DeviceUtils { @@ -14,10 +15,27 @@ object DeviceUtils { } fun isFloatWindowOpened(): Boolean { - return isFloatWindowOpened(BaseApp.instance!!) + return isFloatWindowOpened(BaseApp.instance) } fun isFloatWindowOpened(context: Context): Boolean { return Build.VERSION.SDK_INT < Build.VERSION_CODES.M || Settings.canDrawOverlays(context) } + + + fun getScreenWidth(): Int { + val manager: WindowManager = + BaseApp.instance.getSystemService(Context.WINDOW_SERVICE) as WindowManager + val outMetrics = DisplayMetrics() + manager.defaultDisplay.getMetrics(outMetrics) + return outMetrics.widthPixels + } + + fun getScreenHeight(): Int { + val manager: WindowManager = + BaseApp.instance.getSystemService(Context.WINDOW_SERVICE) as WindowManager + val outMetrics = DisplayMetrics() + manager.defaultDisplay.getMetrics(outMetrics) + return outMetrics.heightPixels + } } \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/util/EnforceUtils.kt b/app/src/main/java/com/wrbug/developerhelper/util/EnforceUtils.kt deleted file mode 100644 index c79da14..0000000 --- a/app/src/main/java/com/wrbug/developerhelper/util/EnforceUtils.kt +++ /dev/null @@ -1,42 +0,0 @@ -package com.wrbug.developerhelper.util -import com.wrbug.developerhelper.commonutil.shell.ShellManager - -object EnforceUtils { - fun getEnforceType(packageName: String): EnforceType { - val files = ShellManager.getZipFileList(ShellManager.findApkDir(packageName)).toString().trim() - return when { - isIjiaMi(files) -> EnforceType.I_JIA_MI - is360(files) -> EnforceType.QI_HOO - isLeGu(files) -> EnforceType.LE_GU - isBangcle(files) -> EnforceType.BANGCLE - else -> EnforceType.UN_KNOWN - } - } - - private fun isIjiaMi(files: String): Boolean { - return files.contains("ijm") or files.contains("ijiami") - - } - - private fun is360(files: String): Boolean { - return files.contains("libjiagu.so") - } - - private fun isLeGu(files: String): Boolean { - return files.contains("tencent_stub") - } - - private fun isBangcle(files: String): Boolean { - return files.contains("bangcle") - } - - - enum class EnforceType(val type: String) { - QI_HOO("360"), - BAI_DU("百度"), - I_JIA_MI("爱加密"), - LE_GU("乐固"), - BANGCLE("梆梆"), - UN_KNOWN("未知/无加固"), - } -} \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/util/FileUtils.kt b/app/src/main/java/com/wrbug/developerhelper/util/FileUtils.kt deleted file mode 100644 index ca7532c..0000000 --- a/app/src/main/java/com/wrbug/developerhelper/util/FileUtils.kt +++ /dev/null @@ -1,96 +0,0 @@ -package com.wrbug.developerhelper.util - -import android.media.MediaMetadataRetriever -import android.net.Uri -import android.os.Build -import androidx.core.content.FileProvider -import com.wrbug.developerhelper.basecommon.BaseApp -import org.dom4j.Document -import org.dom4j.io.OutputFormat -import org.dom4j.io.XMLWriter - -import java.io.* - - -/** - * Created by wrbug on 2017/8/23. - */ -object FileUtils { - - fun inputstreamtofile(ins: InputStream, file: File) { - try { - if (file.exists()) { - file.createNewFile() - } - val os = FileOutputStream(file) - os.write(ins.readBytes()) - os.close() - ins.close() - } catch (t: Throwable) { - - } - - } - - - fun whiteXml(file: File, document: Document) { - try { - if (!file.exists()) { - file.createNewFile() - } - val format = OutputFormat.createPrettyPrint() - format.encoding = "utf-8" - val writer = XMLWriter(OutputStreamWriter(FileOutputStream(file), "utf-8"), format) - writer.write(document) - writer.close() - } catch (e: IOException) { - } - - } - - fun inputStream2String(ins: InputStream): String { - val out = StringBuffer() - val b = ByteArray(4096) - try { - var n: Int = ins.read(b) - while (n != -1) { - out.append(String(b, 0, n)) - n = ins.read(b) - } - } catch (e: IOException) { - return "" - } - - return out.toString() - } - - - // 根据文件后缀名获得对应的MIME类型。 - private fun getMimeType(filePath: String?): String { - val mmr = MediaMetadataRetriever() - var mime = "*/*" - if (filePath != null) { - try { - mmr.setDataSource(filePath) - mime = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_MIMETYPE) - } catch (e: IllegalStateException) { - return mime - } catch (e: IllegalArgumentException) { - return mime - } catch (e: RuntimeException) { - return mime - } - - } - return mime - } - -} - -fun File.toUri(): Uri? { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - FileProvider.getUriForFile(BaseApp.instance, "com.wrbug.developerhelper.fileprovider", this) - } else { - Uri.fromFile(this) - } -} diff --git a/app/src/main/java/com/wrbug/developerhelper/util/OutSharedPreferenceManager.kt b/app/src/main/java/com/wrbug/developerhelper/util/OutSharedPreferenceManager.kt index cc64336..5a6c852 100644 --- a/app/src/main/java/com/wrbug/developerhelper/util/OutSharedPreferenceManager.kt +++ b/app/src/main/java/com/wrbug/developerhelper/util/OutSharedPreferenceManager.kt @@ -1,37 +1,95 @@ package com.wrbug.developerhelper.util +import android.annotation.SuppressLint import android.content.Context +import com.wrbug.developerhelper.commonutil.Constant +import com.wrbug.developerhelper.commonutil.shell.ShellManager import com.wrbug.developerhelper.commonutil.toBoolean import com.wrbug.developerhelper.commonutil.toInt import com.wrbug.developerhelper.commonutil.toLong import com.wrbug.developerhelper.model.entity.SharedPreferenceItemInfo +import io.reactivex.rxjava3.core.Single import java.io.File -object OutSharedPreferenceManager { - fun saveToFile(context: Context, list: Array): File { - val name = System.currentTimeMillis().toString() - val sp = context.applicationContext.getSharedPreferences(name, Context.MODE_PRIVATE) - val edit = sp.edit() - for (sharedPreferenceItemInfo in list) { - when (sharedPreferenceItemInfo.type.toLowerCase()) { - "string" -> { - edit.putString(sharedPreferenceItemInfo.key, sharedPreferenceItemInfo.getValidValue()) - } - "int" -> { - edit.putInt(sharedPreferenceItemInfo.key, sharedPreferenceItemInfo.getValidValue().toInt()) - } - "long" -> { - edit.putLong(sharedPreferenceItemInfo.key, sharedPreferenceItemInfo.getValidValue().toLong()) - } - "boolean" -> { - edit.putBoolean(sharedPreferenceItemInfo.key, sharedPreferenceItemInfo.getValidValue().toBoolean()) - } - "float" -> { - edit.putFloat(sharedPreferenceItemInfo.key, sharedPreferenceItemInfo.getValidValue().toFloat()) - } +class OutSharedPreference(private val context: Context, private val filePath: String) { + private val fileName by lazy { + System.currentTimeMillis().toString() + } + private val tmpSpFile by lazy { + File("${Constant.getDataDir(context.packageName)}/shared_prefs/${fileName}.xml") + } + private val tmpFile by lazy { + File(context.cacheDir, "$fileName.xml") + } + + fun parse(): Single> { + return Single.just(filePath).map { + if (!ShellManager.catFile(it, tmpFile.absolutePath, "777")) { + return@map emptyArray() } + val xml = tmpFile.readText() + XmlUtil.parseSharedPreference(xml) } - edit.commit() - return File("/data/data/${context.packageName}/shared_prefs/$name.xml") + } + + @SuppressLint("ApplySharedPref") + fun saveToFile(context: Context, list: Array): Single { + return Single.just(list).map { + val sp = context.applicationContext.getSharedPreferences(fileName, Context.MODE_PRIVATE) + val edit = sp.edit() + for (sharedPreferenceItemInfo in list) { + when (sharedPreferenceItemInfo.type.lowercase()) { + "string" -> { + edit.putString( + sharedPreferenceItemInfo.key, + sharedPreferenceItemInfo.getValidValue() + ) + } + + "int" -> { + edit.putInt( + sharedPreferenceItemInfo.key, + sharedPreferenceItemInfo.getValidValue().toInt() + ) + } + + "long" -> { + edit.putLong( + sharedPreferenceItemInfo.key, + sharedPreferenceItemInfo.getValidValue().toLong() + ) + } + + "boolean" -> { + edit.putBoolean( + sharedPreferenceItemInfo.key, + sharedPreferenceItemInfo.getValidValue().toBoolean() + ) + } + + "float" -> { + edit.putFloat( + sharedPreferenceItemInfo.key, + sharedPreferenceItemInfo.getValidValue().toFloat() + ) + } + + "set" -> { + edit.putStringSet( + sharedPreferenceItemInfo.key, + sharedPreferenceItemInfo.getValidValue().split(",").toSet() + ) + } + } + } + edit.commit() + ShellManager.catFile(tmpSpFile.absolutePath, filePath, "666") + }.onErrorReturn { false } + } + + + fun deleteTmpFile() { + tmpSpFile.delete() + tmpFile.delete() } } \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/util/ResourceUtilsExt.kt b/app/src/main/java/com/wrbug/developerhelper/util/ResourceUtilsExt.kt index 4effcd8..a5083dc 100644 --- a/app/src/main/java/com/wrbug/developerhelper/util/ResourceUtilsExt.kt +++ b/app/src/main/java/com/wrbug/developerhelper/util/ResourceUtilsExt.kt @@ -1,12 +1,27 @@ package com.wrbug.developerhelper.util import android.content.Context -import com.wrbug.developerhelper.basecommon.BaseApp +import android.content.res.Configuration +import androidx.core.content.ContextCompat +import com.wrbug.developerhelper.base.BaseApp -fun Int.toResString(context: Context = BaseApp.instance): String { +fun Int.getString(context: Context = BaseApp.instance): String { return context.getString(this) } +fun Int.getColor(context: Context = BaseApp.instance): Int { + return ContextCompat.getColor(context, this) +} + +fun Int.getString(vararg formatArgs: Any): String { + return BaseApp.instance.getString(this, *formatArgs) +} + fun getString(resId: Int): String { return BaseApp.instance.getString(resId) +} + + +fun isPortrait(): Boolean { + return BaseApp.instance.resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT } \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/util/UpdateUtils.kt b/app/src/main/java/com/wrbug/developerhelper/util/UpdateUtils.kt index 3010f62..c991959 100644 --- a/app/src/main/java/com/wrbug/developerhelper/util/UpdateUtils.kt +++ b/app/src/main/java/com/wrbug/developerhelper/util/UpdateUtils.kt @@ -1,42 +1,11 @@ package com.wrbug.developerhelper.util -import com.wrbug.developerhelper.commonutil.HttpUtil -import com.wrbug.developerhelper.commonutil.shell.Callback import org.jetbrains.anko.doAsync -import org.jetbrains.anko.uiThread -import com.wrbug.developerhelper.commonutil.OkhttpUtils -import com.wrbug.developerhelper.model.entity.VersionInfo -import org.jsoup.Jsoup -import java.lang.Exception object UpdateUtils { - private const val URL = "https://www.coolapk.com/apk/com.wrbug.developerhelper" - fun checkUpdate(callback: Callback) { + fun checkUpdate() { doAsync { - try { - val document = Jsoup.connect(URL) - .sslSocketFactory(OkhttpUtils.createSSLSocketFactory()).get() - val versionName = document.getElementsByClass("list_app_info").text() ?: "" - val feature = document.getElementsByClass("apk_left_title_info").first().html().replace("
", "\n") - val size = document.getElementsByClass("apk_topba_message").html().split("/")[0].trim() - val updateTime = - document.getElementsByClass("apk_left_title_info")[2].html().split("
")[1].replace( - "更新时间:", - "" - ) - val info = VersionInfo() - info.versionName = versionName - info.feature = feature - info.size = size - info.updateDate = updateTime - info.downloadUrl = URL - uiThread { - callback.onSuccess(info) - } - } catch (e: Exception) { - uiThread { callback.onFailed() } - } } diff --git a/app/src/main/java/com/wrbug/developerhelper/util/ViewExt.kt b/app/src/main/java/com/wrbug/developerhelper/util/ViewExt.kt new file mode 100644 index 0000000..4a39531 --- /dev/null +++ b/app/src/main/java/com/wrbug/developerhelper/util/ViewExt.kt @@ -0,0 +1,94 @@ +package com.wrbug.developerhelper.util + +import android.app.ActionBar.LayoutParams +import android.os.SystemClock +import android.view.View +import android.view.ViewGroup +import android.widget.FrameLayout +import android.widget.ImageView +import android.widget.Toast +import androidx.core.view.isVisible +import com.airbnb.lottie.LottieAnimationView +import com.airbnb.lottie.LottieDrawable +import com.bumptech.glide.Glide +import com.wrbug.developerhelper.R +import com.wrbug.developerhelper.mmkv.ConfigKv +import com.wrbug.developerhelper.mmkv.manager.MMKVManager + +private val configKv by lazy { + MMKVManager.get(ConfigKv::class.java) +} + +fun View?.setOnDoubleCheckClickListener(duration: Long = 800, clickListener: (View) -> Unit) { + this?.setOnClickListener { + val time = SystemClock.elapsedRealtime() + val lastTime = (it.getTag(R.id.double_check_click) as? Long) ?: 0 + if (time - lastTime > duration) { + it.setTag(R.id.double_check_click, time) + clickListener(it) + } + } +} + + +fun View?.setOnRootCheckClickListener(clickListener: (View) -> Unit) { + setOnDoubleCheckClickListener { + if (!configKv.isOpenRoot()) { + Toast.makeText(it.context, R.string.open_root_notice, Toast.LENGTH_SHORT).show() + return@setOnDoubleCheckClickListener + } + clickListener(it) + } +} + +fun FrameLayout.startPageLoading() { + val lottieView = LottieAnimationView(context) + lottieView.id = R.id.lottie_loading + lottieView.setAnimation(R.raw.lottie_page_loading) + lottieView.playAnimation() + lottieView.repeatCount = LottieDrawable.INFINITE + removeAllViews() + isVisible = true + addView( + lottieView, + FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT) + ) +} + +fun FrameLayout.stopPageLoading() { + val lottieView: LottieAnimationView? = findViewById(R.id.lottie_loading) + lottieView?.cancelAnimation() + removeAllViews() + isVisible = false +} + + +fun ImageView.loadImage(url: Any?, default: Int? = null) { + Glide.with(this).load(url).apply { + if (default != null) { + error(default) + } + }.into(this) +} + + +inline var View.visible: Boolean + set(value) { + visibility = if (value) { + View.VISIBLE + } else { + View.GONE + } + } + get() = visibility == View.VISIBLE + + +inline var View.inVisible: Boolean + set(value) { + visibility = if (value) { + View.INVISIBLE + } else { + View.VISIBLE + } + } + get() = visibility == View.INVISIBLE \ No newline at end of file diff --git a/app/src/main/java/com/wrbug/developerhelper/util/XmlUtil.kt b/app/src/main/java/com/wrbug/developerhelper/util/XmlUtil.kt index 1347a91..4b89580 100644 --- a/app/src/main/java/com/wrbug/developerhelper/util/XmlUtil.kt +++ b/app/src/main/java/com/wrbug/developerhelper/util/XmlUtil.kt @@ -3,6 +3,7 @@ package com.wrbug.developerhelper.util import com.wrbug.developerhelper.model.entity.SharedPreferenceItemInfo import org.dom4j.DocumentHelper import org.dom4j.Element +import org.dom4j.tree.DefaultElement /** * xml相关的工具类 @@ -28,6 +29,11 @@ object XmlUtil { if (type == "string") { info.value = child.text info.newValue = child.text + } else if (type == "set") { + info.value = child.content().filterIsInstance(DefaultElement::class.java) + .joinToString(",") { it.text } + info.newValue = child.content().filterIsInstance(DefaultElement::class.java) + .joinToString(",") { it.text } } else { info.value = child.attributeValue("value") info.newValue = child.attributeValue("value") diff --git a/app/src/main/res/drawable/bg_round_8dp_white.xml b/app/src/main/res/drawable/bg_round_8dp_white.xml new file mode 100644 index 0000000..3edf0a7 --- /dev/null +++ b/app/src/main/res/drawable/bg_round_8dp_white.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_accessibility.xml b/app/src/main/res/drawable/ic_accessibility.xml new file mode 100644 index 0000000..c872987 --- /dev/null +++ b/app/src/main/res/drawable/ic_accessibility.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/ic_accessibility_666666.xml b/app/src/main/res/drawable/ic_accessibility_666666.xml deleted file mode 100644 index 0204f1a..0000000 --- a/app/src/main/res/drawable/ic_accessibility_666666.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_accessibility_black.xml b/app/src/main/res/drawable/ic_accessibility_black.xml deleted file mode 100644 index 3239379..0000000 --- a/app/src/main/res/drawable/ic_accessibility_black.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_application.xml b/app/src/main/res/drawable/ic_application.xml new file mode 100644 index 0000000..630ea85 --- /dev/null +++ b/app/src/main/res/drawable/ic_application.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_canceled.xml b/app/src/main/res/drawable/ic_canceled.xml new file mode 100644 index 0000000..99b4c15 --- /dev/null +++ b/app/src/main/res/drawable/ic_canceled.xml @@ -0,0 +1,21 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_chevron_left.xml b/app/src/main/res/drawable/ic_chevron_left.xml new file mode 100644 index 0000000..2fd7c1c --- /dev/null +++ b/app/src/main/res/drawable/ic_chevron_left.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_chevron_left_white_24dp.xml b/app/src/main/res/drawable/ic_chevron_left_24dp.xml similarity index 100% rename from app/src/main/res/drawable/ic_chevron_left_white_24dp.xml rename to app/src/main/res/drawable/ic_chevron_left_24dp.xml diff --git a/app/src/main/res/drawable/ic_chevron_right.xml b/app/src/main/res/drawable/ic_chevron_right.xml new file mode 100644 index 0000000..ce2744c --- /dev/null +++ b/app/src/main/res/drawable/ic_chevron_right.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_chevron_right_white_24dp.xml b/app/src/main/res/drawable/ic_chevron_right_24dp.xml similarity index 100% rename from app/src/main/res/drawable/ic_chevron_right_white_24dp.xml rename to app/src/main/res/drawable/ic_chevron_right_24dp.xml diff --git a/app/src/main/res/drawable/ic_copy.xml b/app/src/main/res/drawable/ic_copy.xml new file mode 100644 index 0000000..5b54247 --- /dev/null +++ b/app/src/main/res/drawable/ic_copy.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/ic_default_app_ico_place_holder.xml b/app/src/main/res/drawable/ic_default_app_ico_place_holder.xml new file mode 100644 index 0000000..0eaded5 --- /dev/null +++ b/app/src/main/res/drawable/ic_default_app_ico_place_holder.xml @@ -0,0 +1,21 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_fail.xml b/app/src/main/res/drawable/ic_fail.xml new file mode 100644 index 0000000..65ffd33 --- /dev/null +++ b/app/src/main/res/drawable/ic_fail.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_float_air_bubble_666666.xml b/app/src/main/res/drawable/ic_float_air_bubble_666666.xml deleted file mode 100644 index 31149a0..0000000 --- a/app/src/main/res/drawable/ic_float_air_bubble_666666.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_ic_empty_666666.xml b/app/src/main/res/drawable/ic_ic_empty.xml similarity index 100% rename from app/src/main/res/drawable/ic_ic_empty_666666.xml rename to app/src/main/res/drawable/ic_ic_empty.xml diff --git a/app/src/main/res/drawable/ic_ignore.xml b/app/src/main/res/drawable/ic_ignore.xml new file mode 100644 index 0000000..04dbaad --- /dev/null +++ b/app/src/main/res/drawable/ic_ignore.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/onboarding_indicator_selected.xml b/app/src/main/res/drawable/ic_marker.xml similarity index 59% rename from app/src/main/res/drawable/onboarding_indicator_selected.xml rename to app/src/main/res/drawable/ic_marker.xml index 3d6e9fa..2837621 100644 --- a/app/src/main/res/drawable/onboarding_indicator_selected.xml +++ b/app/src/main/res/drawable/ic_marker.xml @@ -1,6 +1,5 @@ - - + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_marker_active.xml b/app/src/main/res/drawable/ic_marker_active.xml new file mode 100644 index 0000000..011e399 --- /dev/null +++ b/app/src/main/res/drawable/ic_marker_active.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_marker_inactive.xml b/app/src/main/res/drawable/ic_marker_inactive.xml new file mode 100644 index 0000000..e8e5572 --- /dev/null +++ b/app/src/main/res/drawable/ic_marker_inactive.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_notification.xml b/app/src/main/res/drawable/ic_notification.xml new file mode 100644 index 0000000..b60ed3d --- /dev/null +++ b/app/src/main/res/drawable/ic_notification.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_root_666666.xml b/app/src/main/res/drawable/ic_root.xml similarity index 100% rename from app/src/main/res/drawable/ic_root_666666.xml rename to app/src/main/res/drawable/ic_root.xml diff --git a/app/src/main/res/drawable/ic_search_white_24dp.xml b/app/src/main/res/drawable/ic_search_24.xml similarity index 100% rename from app/src/main/res/drawable/ic_search_white_24dp.xml rename to app/src/main/res/drawable/ic_search_24.xml diff --git a/app/src/main/res/drawable/ic_sheldon_root.xml b/app/src/main/res/drawable/ic_sheldon_root.xml new file mode 100644 index 0000000..ef66512 --- /dev/null +++ b/app/src/main/res/drawable/ic_sheldon_root.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_shield_666666.xml b/app/src/main/res/drawable/ic_shield.xml similarity index 100% rename from app/src/main/res/drawable/ic_shield_666666.xml rename to app/src/main/res/drawable/ic_shield.xml diff --git a/app/src/main/res/drawable/ic_success.xml b/app/src/main/res/drawable/ic_success.xml new file mode 100644 index 0000000..309b83d --- /dev/null +++ b/app/src/main/res/drawable/ic_success.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_switch.xml b/app/src/main/res/drawable/ic_switch.xml new file mode 100644 index 0000000..fce0772 --- /dev/null +++ b/app/src/main/res/drawable/ic_switch.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_waiting.xml b/app/src/main/res/drawable/ic_waiting.xml new file mode 100644 index 0000000..b090eb1 --- /dev/null +++ b/app/src/main/res/drawable/ic_waiting.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_xposed_666666.xml b/app/src/main/res/drawable/ic_xposed_666666.xml deleted file mode 100644 index 247f7a8..0000000 --- a/app/src/main/res/drawable/ic_xposed_666666.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_xposed_custom_666666.xml b/app/src/main/res/drawable/ic_xposed_custom_666666.xml deleted file mode 100644 index 80f08b0..0000000 --- a/app/src/main/res/drawable/ic_xposed_custom_666666.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/onboarding_indicator_unselected.xml b/app/src/main/res/drawable/onboarding_indicator_unselected.xml deleted file mode 100644 index 7461fa9..0000000 --- a/app/src/main/res/drawable/onboarding_indicator_unselected.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/commonwidget/src/main/res/drawable/toast_bg_circle_cornor_rect.xml b/app/src/main/res/drawable/toast_bg_circle_cornor_rect.xml similarity index 100% rename from commonwidget/src/main/res/drawable/toast_bg_circle_cornor_rect.xml rename to app/src/main/res/drawable/toast_bg_circle_cornor_rect.xml diff --git a/app/src/main/res/drawable/widget_ic_no_data.xml b/app/src/main/res/drawable/widget_ic_no_data.xml new file mode 100644 index 0000000..35be377 --- /dev/null +++ b/app/src/main/res/drawable/widget_ic_no_data.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_app_backup_detail.xml b/app/src/main/res/layout/activity_app_backup_detail.xml new file mode 100644 index 0000000..6960008 --- /dev/null +++ b/app/src/main/res/layout/activity_app_backup_detail.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_app_recover.xml b/app/src/main/res/layout/activity_app_recover.xml new file mode 100644 index 0000000..6b9438b --- /dev/null +++ b/app/src/main/res/layout/activity_app_recover.xml @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_backup_app.xml b/app/src/main/res/layout/activity_backup_app.xml new file mode 100644 index 0000000..bc844e7 --- /dev/null +++ b/app/src/main/res/layout/activity_backup_app.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_database_edit.xml b/app/src/main/res/layout/activity_database_edit.xml index bfacaa4..4492843 100644 --- a/app/src/main/res/layout/activity_database_edit.xml +++ b/app/src/main/res/layout/activity_database_edit.xml @@ -25,7 +25,8 @@ android:padding="20dp" android:text="@string/table_data_is_empty" android:textSize="18sp" - android:visibility="gone" /> + android:visibility="gone" + app:layout_constraintTop_toTopOf="parent" /> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 17742be..7325ea0 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,41 +1,35 @@ - + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + tools:context=".ui.activity.main.MainActivity"> - - + - - + + - - - - - - + app:layout_behavior="@string/appbar_scrolling_view_behavior"> + android:paddingHorizontal="24dp"> @@ -43,20 +37,18 @@ android:id="@+id/accessibilitySettingView" android:layout_width="match_parent" android:layout_height="wrap_content" - android:onClick="@{()->presenter.onAccessibilityClick()}" app:checkable="false" - app:checked="@{mainVm.openAccessibility}" - app:src="@drawable/ic_accessibility_666666" + app:icoTint="@color/iconPrimary" + app:src="@drawable/ic_accessibility" app:title="@string/open_accessibility_service" /> @@ -75,21 +68,47 @@ android:id="@+id/rootSettingView" android:layout_width="match_parent" android:layout_height="wrap_content" - android:onClick="@{()->presenter.onRootClick()}" app:checkable="false" - app:checked="@{mainVm.openRoot}" - app:src="@drawable/ic_root_666666" + app:icoTint="@color/iconPrimary" + app:src="@drawable/ic_sheldon_root" app:summary="@string/open_root_permission_summary" app:title="@string/open_root_permission_title" /> + app:checkable="false" + app:icoTint="@color/iconPrimary" + app:src="@drawable/ic_notification" + app:summary="@string/open_notification_summary" + app:title="@string/open_notification_title" + tools:visibility="visible" /> + + + + + + - - \ No newline at end of file + + diff --git a/app/src/main/res/layout/activity_shared_preference_edit.xml b/app/src/main/res/layout/activity_shared_preference_edit.xml index 3bec598..8f95392 100644 --- a/app/src/main/res/layout/activity_shared_preference_edit.xml +++ b/app/src/main/res/layout/activity_shared_preference_edit.xml @@ -17,5 +17,19 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" - app:layout_behavior="@string/appbar_scrolling_view_behavior" /> + app:layout_behavior="@string/appbar_scrolling_view_behavior" + tools:listitem="@layout/item_shared_preference_info" /> + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_shell_app_manager.xml b/app/src/main/res/layout/activity_shell_app_manager.xml deleted file mode 100644 index 5b1cc8a..0000000 --- a/app/src/main/res/layout/activity_shell_app_manager.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/activity_xposed_setting.xml b/app/src/main/res/layout/activity_xposed_setting.xml deleted file mode 100644 index 8b6e95f..0000000 --- a/app/src/main/res/layout/activity_xposed_setting.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_apk_info.xml b/app/src/main/res/layout/dialog_apk_info.xml index 7d8a32a..e3d19ea 100644 --- a/app/src/main/res/layout/dialog_apk_info.xml +++ b/app/src/main/res/layout/dialog_apk_info.xml @@ -1,4 +1,5 @@ + - \ No newline at end of file + + diff --git a/app/src/main/res/layout/dialog_backup_app.xml b/app/src/main/res/layout/dialog_backup_app.xml new file mode 100644 index 0000000..9211711 --- /dev/null +++ b/app/src/main/res/layout/dialog_backup_app.xml @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_backup_app_select.xml b/app/src/main/res/layout/dialog_backup_app_select.xml new file mode 100644 index 0000000..0083b0a --- /dev/null +++ b/app/src/main/res/layout/dialog_backup_app_select.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_guide.xml b/app/src/main/res/layout/fragment_guide.xml index e67d07d..4e15b75 100644 --- a/app/src/main/res/layout/fragment_guide.xml +++ b/app/src/main/res/layout/fragment_guide.xml @@ -1,4 +1,5 @@ @@ -48,5 +49,4 @@ tools:text="Your awesome description about the app" /> - - \ No newline at end of file + diff --git a/app/src/main/res/layout/item_backup_app_info.xml b/app/src/main/res/layout/item_backup_app_info.xml new file mode 100644 index 0000000..6f9cb2e --- /dev/null +++ b/app/src/main/res/layout/item_backup_app_info.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_backup_detail_info.xml b/app/src/main/res/layout/item_backup_detail_info.xml new file mode 100644 index 0000000..55a8d33 --- /dev/null +++ b/app/src/main/res/layout/item_backup_detail_info.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_empty_data.xml b/app/src/main/res/layout/item_empty_data.xml new file mode 100644 index 0000000..c90c36a --- /dev/null +++ b/app/src/main/res/layout/item_empty_data.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_info_loading.xml b/app/src/main/res/layout/item_info_loading.xml new file mode 100644 index 0000000..89661e5 --- /dev/null +++ b/app/src/main/res/layout/item_info_loading.xml @@ -0,0 +1,11 @@ + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_loading_data.xml b/app/src/main/res/layout/item_loading_data.xml new file mode 100644 index 0000000..54f2089 --- /dev/null +++ b/app/src/main/res/layout/item_loading_data.xml @@ -0,0 +1,8 @@ + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_recover_app_time_line.xml b/app/src/main/res/layout/item_recover_app_time_line.xml new file mode 100644 index 0000000..cb22188 --- /dev/null +++ b/app/src/main/res/layout/item_recover_app_time_line.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_shared_preference_info.xml b/app/src/main/res/layout/item_shared_preference_info.xml index 44c755d..6b6eb94 100644 --- a/app/src/main/res/layout/item_shared_preference_info.xml +++ b/app/src/main/res/layout/item_shared_preference_info.xml @@ -16,8 +16,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" - android:textColor="@color/item_title_text" - android:textSize="16sp" + android:textAppearance="@style/TextAppearance.ItemTitle" tools:text="标题" /> + android:textColor="@color/material_color_light_blue_500" + android:visibility="gone" /> diff --git a/app/src/main/res/layout/item_shell_app_info.xml b/app/src/main/res/layout/item_shell_app_info.xml index 3e0a3d0..c24e1f3 100644 --- a/app/src/main/res/layout/item_shell_app_info.xml +++ b/app/src/main/res/layout/item_shell_app_info.xml @@ -1,5 +1,6 @@ - + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + tools:background="@color/material_color_red_500" /> - \ No newline at end of file + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_view_info.xml b/app/src/main/res/layout/item_view_info.xml index a92cd12..57ce62b 100644 --- a/app/src/main/res/layout/item_view_info.xml +++ b/app/src/main/res/layout/item_view_info.xml @@ -1,28 +1,45 @@ - + android:paddingHorizontal="24dp"> - \ No newline at end of file + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_float_window_button.xml b/app/src/main/res/layout/layout_float_window_button.xml index 4d1c2c5..c52392b 100644 --- a/app/src/main/res/layout/layout_float_window_button.xml +++ b/app/src/main/res/layout/layout_float_window_button.xml @@ -1,8 +1,7 @@ diff --git a/commonwidget/src/main/res/layout/layout_toast_flexible.xml b/app/src/main/res/layout/layout_toast_flexible.xml similarity index 100% rename from commonwidget/src/main/res/layout/layout_toast_flexible.xml rename to app/src/main/res/layout/layout_toast_flexible.xml diff --git a/app/src/main/res/layout/layout_toolbar.xml b/app/src/main/res/layout/layout_toolbar.xml index 5532922..9b16398 100644 --- a/app/src/main/res/layout/layout_toolbar.xml +++ b/app/src/main/res/layout/layout_toolbar.xml @@ -1,13 +1,13 @@ - \ No newline at end of file diff --git a/app/src/main/res/layout/view_app_bar.xml b/app/src/main/res/layout/view_app_bar.xml new file mode 100644 index 0000000..ba968b3 --- /dev/null +++ b/app/src/main/res/layout/view_app_bar.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/view_app_data_info.xml b/app/src/main/res/layout/view_app_data_info.xml index bca1bbc..104f635 100644 --- a/app/src/main/res/layout/view_app_data_info.xml +++ b/app/src/main/res/layout/view_app_data_info.xml @@ -17,8 +17,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Apk目录" - android:textColor="@color/item_title_text" - android:textSize="16sp" /> + android:textAppearance="@style/TextAppearance.ItemTitle" /> + android:textAppearance="@style/TextAppearance.ItemTitle" /> + android:textAppearance="@style/TextAppearance.ItemTitle" /> + android:textAppearance="@style/TextAppearance.ItemTitle" /> + android:textAppearance="@style/TextAppearance.ItemTitle" /> - + android:paddingTop="24dp" + app:constraint_referenced_ids="backupAppBtn,restoreAppBtn,restartAppBtn,stopAppBtn,deleteAppDataBtn,uninstallAppBtn" + app:flow_verticalGap="16dp" + app:flow_wrapMode="aligned" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + android:text="@string/backup_app_file" + app:layout_constraintWidth_percent="0.45" /> + android:text="@string/restore_app_file" + app:layout_constraintWidth_percent="0.45" /> + app:layout_constraintWidth_percent="0.45" /> + app:layout_constraintWidth_percent="0.45" /> + app:layout_constraintWidth_percent="0.45" /> - - + app:layout_constraintWidth_percent="0.45" /> \ No newline at end of file diff --git a/app/src/main/res/layout/view_backup_progress.xml b/app/src/main/res/layout/view_backup_progress.xml new file mode 100644 index 0000000..102ea64 --- /dev/null +++ b/app/src/main/res/layout/view_backup_progress.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/view_empty_view.xml b/app/src/main/res/layout/view_empty_view.xml new file mode 100644 index 0000000..6b19298 --- /dev/null +++ b/app/src/main/res/layout/view_empty_view.xml @@ -0,0 +1,31 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/view_layout_info.xml b/app/src/main/res/layout/view_layout_info.xml index 381235f..531797c 100644 --- a/app/src/main/res/layout/view_layout_info.xml +++ b/app/src/main/res/layout/view_layout_info.xml @@ -3,12 +3,17 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/layoutInfoContainer" android:layout_width="match_parent" - android:layout_height="300dp"> + android:layout_height="match_parent" + android:background="@android:color/white"> - + android:paddingVertical="8dp" + tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout"> - + tools:src="@drawable/ic_accessibility" /> - + app:layout_constraintTop_toTopOf="@+id/titleTv" /> + + - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/raw/lottie_list_loading.json b/app/src/main/res/raw/lottie_list_loading.json new file mode 100644 index 0000000..8fa429d --- /dev/null +++ b/app/src/main/res/raw/lottie_list_loading.json @@ -0,0 +1 @@ +{"v":"5.7.4","fr":29.9700012207031,"ip":0,"op":48.0000019550801,"w":400,"h":400,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1L 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[197.852,289,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[-100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-198,-154],[200,-154]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.928477328431,0.15922648112,0.723155182483,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[114.784,101.093],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":21,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":29,"s":[50]},{"t":36.0000014663101,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":21,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":29,"s":[25]},{"t":36.0000014663101,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":21.0000008553475,"op":55.0000022401959,"st":21.0000008553475,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 1L 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[197.852,289,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[-100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-198,-154],[200,-154]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.928477328431,0.15922648112,0.723155182483,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[114.784,101.093],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":17,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":25,"s":[50]},{"t":32.0000013033867,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":17,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":25,"s":[25]},{"t":32.0000013033867,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":17.0000006924242,"op":51.0000020772726,"st":17.0000006924242,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 1L","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[197.852,289,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-198,-154],[200,-154]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.928477328431,0.15922648112,0.723155182483,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[114.784,101.093],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":13,"s":[50]},{"t":20.0000008146167,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":13,"s":[25]},{"t":20.0000008146167,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":5.00000020365417,"op":39.0000015885026,"st":5.00000020365417,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Shape Layer 2R 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[197.852,339,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-198,-154],[200,-154]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.482111194087,0.119379574645,0.946568627451,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[114.784,101.093],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":17,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":25,"s":[50]},{"t":32.0000013033867,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":17,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":25,"s":[25]},{"t":32.0000013033867,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":17.0000006924242,"op":52.0000021180034,"st":17.0000006924242,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Shape Layer 2R 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[197.852,339,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-198,-154],[200,-154]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.482111194087,0.119379574645,0.946568627451,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[114.784,101.093],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":22,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[50]},{"t":37.0000015070409,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":22,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[25]},{"t":37.0000015070409,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":22.0000008960784,"op":57.0000023216576,"st":22.0000008960784,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Shape Layer 2R","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[197.852,339,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[-100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-198,-154],[200,-154]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.482111194087,0.119379574645,0.946568627451,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[114.784,101.093],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":9,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":17,"s":[50]},{"t":24.00000097754,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":9,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":17,"s":[25]},{"t":24.00000097754,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":9.00000036657752,"op":44.0000017921567,"st":9.00000036657752,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"Shape Layer 3L 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[197.852,389,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[-100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-198,-154],[200,-154]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.096529941933,0.306382272758,0.935631127451,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[114.784,101.093],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":22,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[50]},{"t":37.0000015070409,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":22,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[25]},{"t":37.0000015070409,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":22.0000008960784,"op":56.0000022809268,"st":22.0000008960784,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Shape Layer 3L 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[197.852,389,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[-100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-198,-154],[200,-154]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.096529941933,0.306382272758,0.935631127451,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[114.784,101.093],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":18,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":26,"s":[50]},{"t":33.0000013441176,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":18,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":26,"s":[25]},{"t":33.0000013441176,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":18.000000733155,"op":52.0000021180034,"st":18.000000733155,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"Shape Layer 3L","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[197.852,389,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-198,-154],[200,-154]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.096529941933,0.306382272758,0.935631127451,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[114.784,101.093],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":13,"s":[50]},{"t":20.0000008146167,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":13,"s":[25]},{"t":20.0000008146167,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":5.00000020365417,"op":39.0000015885026,"st":5.00000020365417,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"Shape Layer 4R 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[197.852,439,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-198,-154],[200,-154]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.069122576246,0.9015625,0.230790366378,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[114.784,101.093],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":13,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":21,"s":[50]},{"t":28.0000011404634,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":13,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":21,"s":[25]},{"t":28.0000011404634,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":13.0000005295009,"op":48.0000019550801,"st":13.0000005295009,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"Shape Layer 4R 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[197.852,439,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-198,-154],[200,-154]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.069122576246,0.9015625,0.230790366378,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[114.784,101.093],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":22,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[50]},{"t":37.0000015070409,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":22,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[25]},{"t":37.0000015070409,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":22.0000008960784,"op":57.0000023216576,"st":22.0000008960784,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":"Shape Layer 4R","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[197.852,439,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[-100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-198,-154],[200,-154]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.069122576246,0.9015625,0.230790366378,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[114.784,101.093],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":3,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":11,"s":[50]},{"t":18.000000733155,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":3,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":11,"s":[25]},{"t":18.000000733155,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":3.00000012219251,"op":38.0000015477717,"st":3.00000012219251,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":"Shape Layer 5L 4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[197.852,179,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[-100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-198,-154],[200,-154]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.078822124706,0.739201085708,0.917202818627,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[114.784,101.093],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":12,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":20,"s":[50]},{"t":27.0000010997325,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":12,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":20,"s":[25]},{"t":27.0000010997325,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":12.00000048877,"op":46.0000018736184,"st":12.00000048877,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":"Shape Layer 5L 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[197.852,179,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[-100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-198,-154],[200,-154]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.078822124706,0.739201085708,0.917202818627,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[114.784,101.093],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":19,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":27,"s":[50]},{"t":34.0000013848484,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":19,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":27,"s":[25]},{"t":34.0000013848484,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":19.0000007738859,"op":53.0000021587343,"st":19.0000007738859,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":"Shape Layer 5L 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[197.852,179,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-198,-154],[200,-154]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.078822124706,0.739201085708,0.917202818627,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[114.784,101.093],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":1,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":9,"s":[50]},{"t":16.0000006516934,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":1,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":9,"s":[25]},{"t":16.0000006516934,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":1.00000004073083,"op":35.0000014255792,"st":1.00000004073083,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":"Shape Layer 5L","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[197.852,179,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-198,-154],[200,-154]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.078822124706,0.739201085708,0.917202818627,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[114.784,101.093],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":8,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":16,"s":[50]},{"t":23.0000009368092,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":8,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":16,"s":[25]},{"t":23.0000009368092,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":8.00000032584668,"op":42.0000017106951,"st":8.00000032584668,"bm":0},{"ddd":0,"ind":17,"ty":4,"nm":"Shape Layer 6R 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[197.852,239,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-198,-154],[200,-154]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.524494904163,0.884911151961,0.087406652114,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[114.784,101.093],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":15,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":23,"s":[50]},{"t":30.0000012219251,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":15,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":23,"s":[25]},{"t":30.0000012219251,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":15.0000006109625,"op":50.0000020365418,"st":15.0000006109625,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":"Shape Layer 6R 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[197.852,239,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-198,-154],[200,-154]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.524494904163,0.884911151961,0.087406652114,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[114.784,101.093],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":22,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[50]},{"t":37.0000015070409,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":22,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[25]},{"t":37.0000015070409,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":22.0000008960784,"op":57.0000023216576,"st":22.0000008960784,"bm":0},{"ddd":0,"ind":19,"ty":4,"nm":"Shape Layer 6R","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[197.852,239,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[-100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-198,-154],[200,-154]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.524494904163,0.884911151961,0.087406652114,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[114.784,101.093],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":13,"s":[50]},{"t":20.0000008146167,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":5,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":13,"s":[25]},{"t":20.0000008146167,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":5.00000020365417,"op":40.0000016292334,"st":5.00000020365417,"bm":0},{"ddd":0,"ind":20,"ty":4,"nm":"Shape Layer 7L 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[197.852,489,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[-100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-198,-154],[200,-154]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.611406034582,0.891253063725,0.090217530494,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[114.784,101.093],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[1],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":13,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0],"y":[0]},"t":21,"s":[50]},{"t":28.0000011404634,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":13,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"t":21,"s":[25]},{"t":28.0000011404634,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":13.0000005295009,"op":47.0000019143492,"st":13.0000005295009,"bm":0},{"ddd":0,"ind":21,"ty":4,"nm":"Shape Layer 7L 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[197.852,489,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[-100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-198,-154],[200,-154]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.611406034582,0.891253063725,0.090217530494,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[114.784,101.093],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[1],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":17,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0],"y":[0]},"t":25,"s":[50]},{"t":32.0000013033867,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":17,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"t":25,"s":[25]},{"t":32.0000013033867,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":17.0000006924242,"op":51.0000020772726,"st":17.0000006924242,"bm":0},{"ddd":0,"ind":22,"ty":4,"nm":"Shape Layer 7L","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[197.852,489,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-198,-154],[200,-154]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.611406034582,0.891253063725,0.090217530494,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[114.784,101.093],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[1],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":4,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0],"y":[0]},"t":12,"s":[50]},{"t":19.0000007738859,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":4,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.333],"y":[0]},"t":12,"s":[25]},{"t":19.0000007738859,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":4.00000016292334,"op":38.0000015477717,"st":4.00000016292334,"bm":0},{"ddd":0,"ind":23,"ty":4,"nm":"Shape Layer 8R 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[197.852,532,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-198,-154],[200,-154]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.954534313725,0.324577391381,0.102267328898,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[114.784,101.093],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":12,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":20,"s":[50]},{"t":27.0000010997325,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":12,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":20,"s":[25]},{"t":27.0000010997325,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":12.00000048877,"op":47.0000019143492,"st":12.00000048877,"bm":0},{"ddd":0,"ind":24,"ty":4,"nm":"Shape Layer 8R 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[197.852,532,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-198,-154],[200,-154]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.954534313725,0.324577391381,0.102267328898,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[114.784,101.093],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":22,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[50]},{"t":37.0000015070409,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":22,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[25]},{"t":37.0000015070409,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":22.0000008960784,"op":57.0000023216576,"st":22.0000008960784,"bm":0},{"ddd":0,"ind":25,"ty":4,"nm":"Shape Layer 8R","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[197.852,532,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[-100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-198,-154],[200,-154]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.954534313725,0.324577391381,0.102267328898,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[114.784,101.093],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":6,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":14,"s":[50]},{"t":21.0000008553475,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":6,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":14,"s":[25]},{"t":21.0000008553475,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":6.00000024438501,"op":41.0000016699642,"st":6.00000024438501,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/app/src/main/res/raw/lottie_loading.json b/app/src/main/res/raw/lottie_loading.json new file mode 100644 index 0000000..6347c41 --- /dev/null +++ b/app/src/main/res/raw/lottie_loading.json @@ -0,0 +1 @@ +{"nm":"Main Scene","ddd":0,"h":100,"w":180,"meta":{"g":"@lottiefiles/creator 1.25.0"},"layers":[{"ty":0,"nm":" Comp 1","sr":1,"st":0,"op":31.00000126265584,"ip":4.00000016292334,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[63,24]},"s":{"a":0,"k":[250,250]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":200,"h":100,"refId":"precomp_Comp 1_ff1747d4-671c-4717-aeaa-23476b2a6335","ind":1}],"v":"5.7.0","fr":29.9700012207031,"op":31,"ip":0,"assets":[{"nm":"","id":"comp_0_85618a3e-3dd7-43d3-a8a7-28422c05ed7d","layers":[{"ty":4,"nm":"Shape Layer 2","sr":1,"st":-4.00000016292334,"op":90.00000366577514,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[-32.5,5,0],"ix":1},"s":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[20,20,100],"t":9.000000162923339},{"s":[100,100,100],"t":18.00000073315504}],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[72,74.8,0],"t":10.000000162923339},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[72,62.8,0],"t":16.00000016292334},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[72,66.1,0],"t":22.00000016292334},{"o":{"x":0.167,"y":0},"i":{"x":0.667,"y":1},"s":[72,64.5,0],"t":28.00000016292334},{"s":[72,64.8,0],"t":34.00000138484844}],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[0],"t":9.000000162923339},{"s":[100],"t":18.00000073315504}],"ix":11}},"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Ellipse 1","ix":1,"cix":2,"np":3,"it":[{"ty":"el","bm":0,"hd":false,"mn":"ADBE Vector Shape - Ellipse","nm":"Ellipse Path 1","d":1,"p":{"a":0,"k":[0,0],"ix":3},"s":{"a":0,"k":[15,15],"ix":2}},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"c":{"a":0,"k":[1,1,1],"ix":3}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.498,0.4902,0.5176],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[-32.618,4.916],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":1},{"ty":4,"nm":"Shape Layer 1","sr":1,"st":0,"op":94.00000382869844,"ip":4.00000016292334,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[-32.5,5,0],"ix":1},"s":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100,100,100],"t":6.00000016292334},{"s":[120,120,100],"t":15.000000610962541}],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[72,64.75,0],"t":4.00000016292334},{"s":[72,44.8,0],"t":13.00000052950086}],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":4.00000016292334},{"s":[0],"t":13.00000052950086}],"ix":11}},"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Ellipse 1","ix":1,"cix":2,"np":3,"it":[{"ty":"el","bm":0,"hd":false,"mn":"ADBE Vector Shape - Ellipse","nm":"Ellipse Path 1","d":1,"p":{"a":0,"k":[0,0],"ix":3},"s":{"a":0,"k":[15,15],"ix":2}},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"c":{"a":0,"k":[1,1,1],"ix":3}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.498,0.4902,0.5176],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[-32.618,4.916],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":2}]},{"nm":"Comp 1","id":"precomp_Comp 1_ff1747d4-671c-4717-aeaa-23476b2a6335","fr":29.9700012207031,"layers":[{"ty":0,"nm":"单个点","sr":1,"st":0,"op":94.00000382869844,"ip":4.00000016292334,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[50,50,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[49.75,38.5,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"w":100,"h":100,"refId":"comp_0_85618a3e-3dd7-43d3-a8a7-28422c05ed7d","ind":1},{"ty":0,"nm":"单个点","sr":1,"st":2.00000008146167,"op":96.00000391016015,"ip":6.00000024438501,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[50,50,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[74.75,38.5,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"w":100,"h":100,"refId":"comp_0_85618a3e-3dd7-43d3-a8a7-28422c05ed7d","ind":2},{"ty":4,"nm":"Shape Layer 3","sr":1,"st":0,"op":7.000000285115849,"ip":4.00000016292334,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[-32.5,5,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[96.75,53.25,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Ellipse 1","ix":1,"cix":2,"np":3,"it":[{"ty":"el","bm":0,"hd":false,"mn":"ADBE Vector Shape - Ellipse","nm":"Ellipse Path 1","d":1,"p":{"a":0,"k":[0,0],"ix":3},"s":{"a":0,"k":[15,15],"ix":2}},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"c":{"a":0,"k":[1,1,1],"ix":3}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.498,0.4902,0.5176],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[-32.618,4.916],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":3},{"ty":4,"nm":"Shape Layer 2","sr":1,"st":0,"op":10.000000407308349,"ip":4.00000016292334,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[-32.5,5,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[121.75,53.25,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Ellipse 1","ix":1,"cix":2,"np":3,"it":[{"ty":"el","bm":0,"hd":false,"mn":"ADBE Vector Shape - Ellipse","nm":"Ellipse Path 1","d":1,"p":{"a":0,"k":[0,0],"ix":3},"s":{"a":0,"k":[15,15],"ix":2}},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"c":{"a":0,"k":[1,1,1],"ix":3}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.498,0.4902,0.5176],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[-32.618,4.916],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":4},{"ty":0,"nm":"单个点","sr":1,"st":4.00000016292334,"op":98.00000399162184,"ip":8.00000032584668,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[50,50,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[99.75,38.5,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"w":100,"h":100,"refId":"comp_0_85618a3e-3dd7-43d3-a8a7-28422c05ed7d","ind":5}]}]} \ No newline at end of file diff --git a/app/src/main/res/raw/lottie_page_loading.json b/app/src/main/res/raw/lottie_page_loading.json new file mode 100644 index 0000000..016f5fd --- /dev/null +++ b/app/src/main/res/raw/lottie_page_loading.json @@ -0,0 +1 @@ +{"v":"5.5.7","meta":{"g":"LottieFiles AE 0.1.21","a":"Syed Mubarak","k":"Loading","d":"Loading Animation","tc":"#FFFFFF"},"fr":29.9700012207031,"ip":0,"op":71.0000028918893,"w":1080,"h":1080,"nm":"Loading2","ddd":0,"assets":[{"id":"image_0","w":1080,"h":1080,"u":"","p":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABDgAAAQ4CAYAAADsEGyPAAAgAElEQVR4XuzdT6sdV7of4FXaGtxBwO5PYDU3cIdWk0kQp463ByGZBKs/gWVIRglY/gDBx5C51ZCMEmj5E7QMGSSZ9NZZmwOBQEuzEC60PMi4pdGdeKtCqbeu/0unalftXe9azwFzB7f+rPd5X7mt36la1SQ/BAgQIECAAAECBAgQIECAAIHgAk3w9Vs+AQIECBAgQIAAAQIECBAgQCAJOAwBAQIECBAgQIAAAQIECBAgEF5AwBG+hQogQIAAAQIECBAgQIAAAQIEBBxmgAABAgQIECBAgAABAgQIEAgvIOAI30IFECBAgAABAgQIECBAgAABAgIOM0CAAAECBAgQIECAAAECBAiEFxBwhG+hAggQIECAAAECBAgQIECAAAEBhxkgQIAAAQIECBAgQIAAAQIEwgsIOMK3UAEECBAgQIAAAQIECBAgQICAgMMMECBAgAABAgQIECBAgAABAuEFBBzhW6gAAgQIECBAgAABAgQIECBAQMBhBggQIECAAAECBAgQIECAAIHwAgKO8C1UAAECBAgQIECAAAECBAgQICDgMAMECBAgQIAAAQIECBAgQIBAeAEBR/gWKoAAAQIECBAgQIAAAQIECBAQcJgBAgQIECBAgAABAgQIECBAILyAgCN8CxVAgAABAgQIECBAgAABAgQICDjMAAECBAgQIECAAAECBAgQIBBeQMARvoUKIECAAAECBAgQIECAAAECBAQcZoAAAQIECBAgQIAAAQIECBAILyDgCN9CBRAgQIAAAQIECBAgQIAAAQICDjNAgAABAgQIECBAgAABAgQIhBcQcIRvoQIIECBAgAABAgQIECBAgAABAYcZIECAAAECBAgQIECAAAECBMILCDjCt1ABBAgQIECAAAECBAgQIECAgIDDDBAgQIAAAQIECBAgQIAAAQLhBQQc4VuoAAIECBAgQIAAAQIECBAgQEDAYQYIECBAgAABAgQIECBAgACB8AICjvAtVAABAgQIECBAgAABAgQIECAg4DADBAgQIECAAAECBAgQIECAQHgBAUf4FiqAAAECBAgQIECAAAECBAgQEHCYAQIECBAgQIAAAQIECBAgQCC8gIAjfAsVQIAAAQIECBAgQIAAAQIECAg4zAABAgQIECBAgAABAgQIECAQXkDAEb6FCiBAgAABAgQIECBAgAABAgQEHGaAAAECBAgQIECAAAECBAgQCC8g4AjfQgUQIECAAAECBAgQIECAAAECAg4zQIAAAQIECBAgQIAAAQIECIQXEHCEb6ECCBAgQIAAAQIECBAgQIAAAQGHGSBAgAABAgQIECBAgAABAgTCCwg4wrdQAQQIECBAgAABAgQIECBAgICAwwwQIECAAAECBAgQIECAAAEC4QUEHOFbqAACBAgQIECAAAECBAgQIEBAwGEGCBAgQIAAAQIECBAgQIAAgfACAo7wLVQAAQIECBAgQIAAAQIECBAgIOAwAwQIECBAgAABAgQIECBAgEB4AQFH+BYqgAABAgQIECBAgAABAgQIEBBwmAECBAgQIECAAAECBAgQIEAgvICAI3wLFUCAAAECBAgQIECAAAECBAgIOMwAAQIECBAgQIAAAQIECBAgEF5AwBG+hQogQIAAAQIECBAgQIAAAQIEBBxmgAABAgQIECBAgAABAgQIEAgvIOAI30IFECBAgAABAgQIECBAgAABAgIOM0CAAAECBAgQIECAAAECBAiEFxBwhG+hAggQIECAAAECBAgQIECAAAEBhxkgQIAAAQIECBAgQIAAAQIEwgsIOMK3UAEECBAgQIAAAQIECBAgQICAgMMMECBAgAABAgQIECBAgAABAuEFBBzhW6gAAgQIECBAgAABAgQIECBAQMBhBggQIECAAAECBAgQIECAAIHwAgKO8C1UAAECBAgQIECAAAECBAgQICDgMAMECBAgQIAAAQIECBAgQIBAeAEBR/gWKoAAAQIECBAgQIAAAQIECBAQcJgBAgQIECBAgAABAgQIECBAILyAgCN8CxVAgAABAgQIECBAgAABAgQICDjMAAECBAgQIECAAAECBAgQIBBeQMARvoUKIECAAAECBAgQIECAAAECBAQcZoAAAQIECBAgQIAAAQIECBAILyDgCN9CBRAgQIAAAQIECBAgQIAAAQICDjNAgAABAgQIECBAgAABAgQIhBcQcIRvoQIIECBAgAABAgQIECBAgAABAYcZIECAAAECBAgQIECAAAECBMILCDjCt1ABBAgQIECAAAECBAgQIECAgIDDDBAgQIAAAQIECBAgQIAAAQLhBQQc4VuoAAIECBAgQIAAAQIECBAgQEDAYQYIECBAgAABAgQIECBAgACB8AICjvAtVAABAgQIECBAgAABAgQIECAg4DADBAgQIECAAAECBAgQIECAQHgBAUf4FiqAAAECBAgQIECAAAECBAgQEHCYAQIECBAgQIAAAQIECBAgQCC8gIAjfAsVQIAAAQIECBAgQIAAAQIECAg4zAABAgQIECBAgAABAgQIECAQXkDAEb6FCiBAgAABAgQIECBAgAABAgQEHGaAAAECBAgQIECAAAECBAgQCC8g4AjfQgUQIECAAAECBAgQIECAAAECAg4zQIAAAQIECBAgQIAAAQIECIQXEHCEb6ECCBAgQIAAAQIECBAgQIAAAQGHGSBAgAABAgQIECBAgAABAgTCCwg4wrdQAQQIECBAgAABAgQIECBAgICAwwwQIECAAAECBAgQIECAAAEC4QUEHOFbqAACBAgQIECAAAECBAgQIEBAwGEGCBAgQIAAAQIECBAgQIAAgfACAo7wLVQAAQIECBAgQIAAAQIECBAgIOAwAwQIECBAgAABAgQIECBAgEB4AQFH+BYqgAABAgQIECBAgAABAgQIEBBwmAECBAgQIECAAAECBAgQIEAgvICAI3wLFUCAAAECBAgQIECAAAECBAgIOMwAAQIECBAgQIAAAQIECBAgEF5AwBG+hQogQIAAAQIECBAgQIAAAQIEBBxmgAABAgQIECBAgAABAgQIEAgvIOAI30IFECBAgAABAgQIECBAgAABAgIOM0CAAAECBAgQIECAAAECBAiEFxBwhG+hAggQIECAAAECBAgQIECAAAEBhxkgQIAAAQIECBAgQIAAAQIEwgsIOMK3UAEECBAgQIAAAQIECBAgQICAgMMMECBAgAABAgQIECBAgAABAuEFBBzhW6gAAgQIECBAgAABAgQIECBAQMBhBggQIECAAAECBAgQIECAAIHwAgKO8C1UAAECBAgQIECAAAECBAgQICDgMAMECBAgQIAAAQIECBAgQIBAeAEBR/gWKoAAAQIECBAgQIAAAQIECBAQcJgBAgQIECBAgAABAgQIECBAILyAgCN8CxVAgAABAgQIECBAgAABAgQICDjMAAECBAgQIECAAAECBAgQIBBeQMARvoUKIECAAAECBAgQIECAAAECBAQcZoAAAQIECBAgQIAAAQIECBAILyDgCN9CBRAgQIAAAQIECBAgQIAAAQICDjNAgAABAgQIECBAgAABAgQIhBcQcIRvoQIIECBAgAABAgQIECBAgAABAYcZIECAAAECBAgQIECAAAECBMILCDjCt1ABBAgQIECAAAECBAgQIECAgIDDDBAgQIAAAQIECBAgQIAAAQLhBQQc4VuoAAIECBAgQIAAAQIECBAgQEDAYQYIECBAgAABAgQIECBAgACB8AICjvAtVAABAgQIECBAgAABAgQIECAg4DADBAgQIECAAAECBAgQIECAQHgBAUf4FiqAAAECBAgQIECAAAECBAgQEHCYAQIECBAgQIAAAQIECBAgQCC8gIAjfAsVQIAAAQIECBAgQIAAAQIECAg4zAABAgQIECBAgAABAgQIECAQXkDAEb6FCiBAgAABAgQIECBAgAABAgQEHGaAAAECBAgQIECAAAECBAgQCC8g4AjfQgUQIECAAAECBAgQIECAAAECAg4zQIAAAQIECBAgQIAAAQIECIQXEHCEb6ECCBAgQIAAAQIECBAgQIAAAQGHGSBAgAABAgQIECBAgAABAgTCCwg4wrdQAQQIECBAgAABAgQIECBAgICAwwwQIECAAAECBAgQIECAAAEC4QUEHOFbqAACBAgQIECAAAECBAgQIEBAwGEGCBAgQIAAAQIECBAgQIAAgfACAo7wLVQAAQIECBAgQIAAAQIECBAgIOAwAwQIECBAgAABAgQIECBAgEB4AQFH+BYqgAABAgQIECBAgAABAgQIEBBwmAECBAgQIECAAAECBAgQIEAgvICAI3wLFUCAAAECBAgQIECAAAECBAgIOMwAAQIECBAgQIAAAQIECBAgEF5AwBG+hQogQIAAAQIECBAgQIAAAQIEBBxmgAABAgQIECBAgAABAgQIEAgvIOAI30IFECBAgAABAgQIECBAgAABAgIOM0CAAAECBAgQIECAAAECBAiEFxBwhG+hAggQIECAAAECBAgQIECAAAEBhxkgQIAAAQIECBAgQIAAAQIEwgsIOMK3UAEECBAgQIAAAQIECBAgQICAgMMMECBAgAABAgQIECBAgAABAuEFBBzhW6gAAgQIECBAgAABAgQIECBAQMBhBggQIECAAAECBAgQIECAAIHwAgKO8C1UAAECBAgQIECAAAECBAgQICDgMAMECBAgQIAAAQIECBAgQIBAeAEBR/gWKoAAAQIECBAgQIAAAQIECBAQcJgBAgQIECBAgAABAgQIECBAILyAgCN8CxVAgAABAgQIECBAgAABAgQICDjMAAECBAgQIECAAAECBAgQIBBeQMARvoUKIECAAAECBAgQIECAAAECBAQcZoAAAQIECBAgQIAAAQIECBAILyDgCN9CBRAgQIAAAQIECBAgQIAAAQICDjNAgAABAgQIECBAgAABAgQIhBcQcIRvoQIIECBAgAABAgQIECBAgAABAYcZIECAAAECBAgQIECAAAECBMILCDjCt1ABBAgQIECAAAECBAgQIECAgIDDDBAgQIAAAQIECBAgQIAAAQLhBQQc4VuoAAIECBAgQIAAAQIECBAgQEDAYQYIECBAgAABAgQIECBAgACB8AICjvAtVAABAgQIECBAgAABAgQIECAg4DADBAgQIECAAAECBAgQIECAQHgBAUf4FiqAAAECBAgQIECAAAECBAgQEHCYAQIECBAgQIAAAQIECBAgQCC8gIAjfAsVQIAAAQIECBAgQIAAAQIECAg4zAABAgQIECBAgAABAgQIECAQXkDAEb6FCiBAgAABAgQIECBAgAABAgQEHGaAAAECBAgQIECAAAECBAgQCC8g4AjfQgUQIECAAAECBAgQIECAAAECAg4zQIAAAQIECBAgQIAAAQIECIQXEHCEb6ECCBAgQIAAAQIECBAgQIAAAQGHGSBAgAABAgQIECBAgAABAgTCCwg4wrdQAQQIECBAgAABAgQIECBAgICAwwwQIECAAAECBAgQIECAAAEC4QUEHOFbqAACBAgQIECAAAECBAgQIEBAwGEGCBAgQIAAAQIECBAgQIAAgfACAo7wLVQAAQIECBAgQIAAAQIECBAgIOAwAwQIECBAgAABAgQIECBAgEB4AQFH+BYqgAABAgQIECBAgAABAgQIEBBwmAECBAgQIECAAAECBAgQIEAgvICAI3wLFUCAAAECBAgQIECAAAECBAgIOMwAAQIECBAgQIAAAQIECBAgEF5AwBG+hQogQIAAAQIECBAgQIAAAQIEBBxmgAABAgQIECBAgAABAgQIEAgvIOAI30IFECBAgAABAgQIECBAgAABAgIOM0CAAAECBAgQIECAAAECBAiEFxBwhG+hAggQIECAAAECBAgQIECAAAEBhxkgQIAAAQIECBAgQIAAAQIEwgsIOMK3UAEECBAgQIAAAQIECBAgQICAgMMMECBAgAABAgQIECBAgAABAuEFBBzhW6gAAgQIECBAgAABAgQIECBAQMBhBggQIECAAAECBAgQIECAAIHwAgKO8C1UAAECBAgQIECAAAECBAgQICDgMAMECBAgQIAAAQIECBAgQIBAeAEBR/gWKoAAAQIECBAgQIAAAQIECBAQcJgBAgQIECBAgAABAgQIECBAILyAgCN8CxVAgAABAgQIECBAgAABAgQICDjMAAECBAgQIECAAAECBAgQIBBeQMARvoUKIECAAAECBAgQIECAAAECBAQcZoAAAQIECBAgQIAAAQIECBAILyDgCN9CBRAgQIAAAQIECBAgQIAAAQICDjNAgAABAgQIECBAgAABAgQIhBcQcIRvoQIIECBAgAABAgQIECBAgAABAYcZIECAAAECBAgQIECAAAECBMILCDjCt1ABBAgQIECAAAECBAgQIECAgIDDDBAgQIAAAQIECBAgQIAAAQLhBQQc4VuoAAIECBAgQIAAAQIECBAgQEDAYQYIECBAgAABAgQIECBAgACB8AICjvAtVAABAgQIECBAgAABAgQIECAg4DADBAgQIECAAAECBAgQIECAQHgBAUf4FiqAAAECBAgQIECAAAECBAgQEHCYAQIECBAgQIAAAQIECBAgQCC8gIAjfAsVQIAAAQIECBAgQIAAAQIECAg4zAABAgQIECBAgAABAgQIECAQXkDAEb6FCiBAgAABAgQIECBAgAABAgQEHGaAAAECBAgQIECAAAECBAgQCC8g4AjfQgUQIECAAAECBAgQIECAAAECAg4zQIAAAQIECBAgQIAAAQIECIQXEHCEb6ECCBAgQIAAAQIECBAgQIAAAQGHGSBAgAABAgQIECBAgAABAgTCCwg4wrdQAQQIECBAgAABAgQIECBAgICAwwwQIECAAAECBAgQIECAAAEC4QUEHOFbqAACBAgQIECAAAECBAgQIEBAwGEGCBAgQIAAAQIECBAgQIAAgfACAo7wLVQAAQIECBAgQIAAAQIECBAgIOAwAwQIECBAgAABAgQIECBAgEB4AQFH+BYqgAABAgQIECBAgAABAgQIEBBwmAECBAgQIECAAAECBAgQIEAgvICAI3wLFUCAAAECBAgQIECAAAECBAgIOMwAAQIECBAgQIAAAQIECBAgEF5AwBG+hQogQIAAAQIECBAgQIAAAQIEBBxmgAABAgQIECBAgAABAgQIEAgvIOAI30IFECBAgAABAgQIECBAgAABAgIOM0CAAAECBAgQIECAAAECBAiEFxBwhG+hAggQIECAAAECBAgQIECAAAEBhxkgQIAAAQIECBAgQIAAAQIEwgsIOMK3UAEECBAgQIAAAQIECBAgQICAgMMMECBAgAABAgQIECBAgAABAuEFBBzhW6gAAgQIECBAgAABAgQIECBAQMBhBggQIECAAAECBAgQIECAAIHwAgKO8C1UAAECBAgQIECAAAECBAgQICDgMAMECBAgQIAAAQIECBAgQIBAeAEBR/gWKoAAAQIECBAgQIAAAQIECBAQcJgBAgQIECBAgAABAgQIECBAILyAgCN8CxVAgAABAgQIECBAgAABAgQICDjMAAECBAgQIECAAAECBAgQIBBeQMARvoUKIECAAAECBAgQIECAAAECBAQcZoAAAQIECBAgQIAAAQIECBAILyDgCN9CBRAgQIAAAQIECBAgQIAAAQICDjNAgAABAgQIECBAgAABAgQIhBcQcIRvoQIIECBAgAABAgQIECBAgAABAYcZIECAAAECBAgQIECAAAECBMILCDjCt1ABBAgQIECAAAECBAgQIECAgIDDDBAgQIAAAQIECBAgQIAAAQLhBQQc4VuoAAIECBAgQIAAAQIECBAgQEDAYQYIECBAgAABAgQIECBAgACB8AICjvAtVAABAgQIECBAgAABAgQIECAg4DADBAgQIECAAAECBAgQIECAQHgBAUf4FiqAAAECBAgQIECAAAECBAgQEHCYAQIECBAgQIAAAQIECBAgQCC8gIAjfAsVQIAAAQIECBAgQIAAAQIECAg4zAABAgQIECBAgAABAgQIECAQXkDAEb6FCiBAgAABAgQIECBAgAABAgQEHGaAAAECBAgQIECAAAECBAgQCC8g4AjfQgUQIECAAAECBAgQIECAAAECAg4zQIAAAQIECBAgQIAAAQIECIQXEHCEb6ECCBAgQIAAAQIECBAgQIAAAQGHGSBAgAABAgQIECBAgAABAgTCCwg4wrdQAQQIECBAgAABAgQIECBAgICAwwwQIECAAAECBAgQIECAAAEC4QUEHOFbqAACBAgQIECAAAECBAgQIEBAwGEGCBAgQIAAAQIECBAgQIAAgfACAo7wLVQAAQIECBAgQIAAAQIECBAgIOAwAwQIECBAgAABAgQIECBAgEB4AQFH+BYqgAABAgQIECBAgAABAgQIEBBwmAECBAgQIECAAAECBAgQIEAgvICAI3wLFUCAAAECBAgQIECAAAECBAgIOMwAAQIECBAgQIAAAQIECBAgEF5AwBG+hQogQIAAAQIECBAgQIAAAQIEBBxmgAABAgQIECBAgAABAgQIEAgvIOAI30IFECBAgAABAgQIECBAgAABAgIOM0CAAAECBAgQIECAAAECBAiEFxBwhG+hAggQIECAAAECBAgQIECAAAEBhxkgQIAAAQIECBAgQIAAAQIEwgsIOMK3UAEECBAgQIAAAQIECBAgQICAgMMMECBAgAABAgQIECBAgAABAuEFBBzhW6gAAgQIECBAgAABAgQIECBAQMBhBggQIECAAAECBAgQIECAAIHwAgKO8C1UAAECBAgQIECAAAECBAgQICDgMAMECBAgQIAAAQIECBAgQIBAeAEBR/gWKoAAAQIECBAgQIAAAQIECBAQcJgBAgQIECBAgAABAgQIECBAILyAgCN8CxVAgAABAgQIECBAgAABAgQICDjMAAECBAgQIECAAAECBAgQIBBeQMARvoUKIECAAAECBAgQIECAAAECBAQcZoAAAQIECBAgQIAAAQIECBAILyDgCN9CBRAgQIAAAQIECBAgQIAAAQICDjNAgAABAgQIECBAgAABAgQIhBcQcIRvoQIIECBAgAABAgQIECBAgAABAYcZIECAAAECBAgQIECAAAECBMILCDjCt1ABBAgQIECAAAECBAgQIECAgIDDDBAgQIAAAQIECBAgQIAAAQLhBQQc4VuoAAIECBAgQIAAAQIECBAgQEDAYQYIECBAgAABAgQIECBAgACB8AICjvAtVAABAgQIECBAgAABAgQIECAg4DADBAgQIECAAAECBAgQIECAQHgBAUf4FiqAAAECBAgQIECAAAECBAgQEHCYAQIECBAgQIAAAQIECBAgQCC8gIAjfAsVQIAAAQIECBAgQIAAAQIECAg4zAABAgQIECBAgAABAgQIECAQXkDAEb6FCiBAgAABAgQIECBAgAABAgQEHGaAAAECBAgQIECAAAECBAgQCC8g4AjfQgUQIECAAAECBAgQIECAAAECAg4zQIAAAQIECBAgQIAAAQIECIQXEHCEb6ECCBAgQIAAAQIECBAgQIAAAQGHGSBAgAABAgQIECBAgAABAgTCCwg4wrdQAQQIECBAgAABAgQIECBAgICAwwwQIECAAAECBAgQIECAAAEC4QUEHOFbqAACBAgQIECAAAECBAgQIEBAwGEGCBAgQIAAAQIECBAgQIAAgfACAo7wLVQAAQIECBAgQIAAAQIECBAgIOAwAwQIECBAgAABAgQIECBAgEB4AQFH+BYqgAABAgQIECBAgAABAgQIEBBwmAECBAgQIECAAAECBAgQIEAgvICAI3wLFUCAAAECBAgQIECAAAECBAgIOMwAAQIECBAgQIAAAQIECBAgEF5AwBG+hQogQIAAAQIECBAgQIAAAQIEBBxmgAABAgQIECBAgAABAgQIEAgvIOAI30IFECBAgAABAgQIECBAgAABAgIOM0CAAAECBAgQIECAAAECBAiEFxBwhG+hAggQIECAAAECBAgQIECAAAEBhxkgQIAAAQIECBAgQIAAAQIEwgsIOMK3UAEECBAgQIAAAQIECBAgQICAgMMMECBAgAABAgQIECBAgAABAuEFBBzhW6gAAgQIECBAgAABAgQIECBAQMBhBggQIECAAAECBAgQIECAAIHwAgKO8C1UAAECBAgQIECAAAECBAgQICDgMAMECBAgQIAAAQIECBAgQIBAeAEBR/gWKoAAAQIECBAgQIAAAQIECBAQcJgBAgQIECBAgAABAgQIECBAILyAgCN8CxVAgAABAgQIECBAgAABAgQICDjMAAECBAgQIECAAAECBAgQIBBeQMARvoUKIECAAAECBAgQIECAAAECBAQcZoAAAQIECBAgQIAAAQIECBAILyDgCN9CBRAgQIAAAQIECBAgQIAAAQICDjNAgAABAgQIECBAgAABAgQIhBcQcIRvoQIIECBAgAABAgQIECBAgAABAYcZIECAAAECBAgQIECAAAECBMILCDjCt1ABBAgQIECAAAECBAgQIECAgIDDDBAgQIAAAQIECBAgQIAAAQLhBQQc4VuoAAIECBAgQIAAAQIECBAgQEDAYQYIECBAgAABAgQIECBAgACB8AICjvAtVAABAgQIECBAgAABAgQIECAg4DADBAgQIECAAAECBAgQIECAQHgBAUf4FiqAAAECBAgQIECAAAECBAgQEHCYAQIECBAgQIAAAQIECBAgQCC8gIAjfAsVQIAAAQIECBAgQIAAAQIECAg4zAABAgQIECBAgAABAgQI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AgQIECBAgEABAgKOApqoBAIECBAgQIAAAQIECBAgULuAgKP2CVA/AQIECBAgQIAAAQIECBAoQEDAUUATlUCAAAECBAgQIECAAAECBGoXEHDUPgHqJ0CAAAECBAgQIECAAAECBQgIOApoohIIECBAgAABAgQIECBAgEDtAgKO2idA/QQIECBAgAABAgQIECBAoAABAUcBTVQCAQIECBAgQIAAAQIECBCoXUDAUfsEqJ8AAQIECBAgQIAAAQIECBQgIOAooIlKIECAAAECBAgQIECAAAECtQsIOGqfAPUTIECAAAECBAgQIECAAIECBAQcBTRRCQQIECBAgAABAgQIECBAoHYBAUftE6B+AgQIECBAgAABAgQIECBQgICAo4AmKoEAAQIECBAgQIAAAQIECNQuIOCofQLUT4AAAQIECBAgQIAAAQIEChAQcBTQRCUQIECAAAECBAgQIECAAIHaBQQctU+A+gkQIECAAAECBAgQIECAQAECAo4CmqgEAgQIECBAgAABAgQIECBQu4CAo/YJUD8BAgQIECBAgAABAgQIEChAQMBRQBOVQIAAAQIECBAgQIAAAQIEahcQcNQ+AeonQIAAAQIECBAgQIAAAQIFCAg4CmiiEggQIECAAAECBAgQIECAQO0CAo7aJ0D9BAgQIECAAAECBAgQIECgAAEBRwFNVAIBAgQIECBAgAABAgQIEKhdQMBR+wSonwABAgQIECBAgAABAgQIFCAg4CigiUogQIAAAQIECBAgQIAAAQK1Cwg4ap8A9RMgQIAAAQIECBAgQIAAgQIEBBwFNFEJBAgQIECAAAECBAgQIECgdgEBR+0ToH4CBAgQIECAAAECBAgQIFCAgICjgCYqgQABAgQIECBAgAABAgQI1C4g4Kh9AtRPgAABAgQIECBAgAABAgQKEBBwFNBEJRAgQIAAAQIECBAgQIAAgdoFBBy1T4D6CRAgQIAAAQIECBAgQIBAAQICjgKaqAQCBAgQIECAAAECBAgQIFC7gICj9glQPwECBAgQIECAAAECBAgQKEBAwFFAE5VAgAABAgQIECBAgAABAgRqFxBw1D4B6idAgAABAgQIECBAgAABAgUICDgKaKISCBAgQIAAAQIECBAgQIBA7QICjtonQP0ECBAgQIAAAQIECBAgQKAAAQFHAU1UAgECBAgQIECAAAECBAgQqF1AwFH7BKifAAECBAgQIECAAAECBAgUICDgKKCJSiBAgAABAgQIECBAgAABArULCDhqnwD1EyBAgAABAgQIECBAgACBAgQEHAU0UQkECBAgQIAAAQIECBAgQKB2AQFH7ROgfgIECBAgQIAAAQIECBAgUICAgKOAJiqBAAECBAgQIECAAAECBAjULiDgqH0C1E+AAAECBAgQIECAAAECBAoQEHAU0EQlECBAgAABAgQIECBAgACB2gUEHLVPgPoJECBAgAABAgQIECBAgEABAgKOApqoBAIECBAgQIAAAQIECBAgULuAgKP2CVA/AQIECBAgQIAAAQIECBAoQEDAUUATlUCAAAECBAgQIECAAAECBGoXEHDUPgHqJ0CAAAECBAgQIECAAAECBQgIOApoohIIECBAgAABAgQIECBAgEDtAgKO2idA/QQIECBAgAABAgQIECBAoAABAUcBTVQCAQIECBAgQIAAAQIECBCoXUDAUfsEqJ8AAQIECBAgQIAAAQIECBQgIOAooIlKIECAAAECBAgQIECAAAECtQsIOGqfAPUTIECAAAECBAgQIECAAIECBAQcBTRRCQQIECBAgAABAgQIECBAoHYBAUftE6B+AgQIECBAgAABAgQIECBQgICAo4AmKoEAAQIECBAgQIAAAQIECNQuIOCofQLUT4AAAQIECBAgQIAAAQIEChAQcBTQRCUQIECAAAECBAgQIECAAIHaBQQctU+A+gkQIECAAAECBAgQIECAQAECAo4CmqgEAgQIECBAgAABAgQIECBQu4CAo/YJUD8BAgQIECBAgAABAgQIEChAQMBRQBOVQIAAAQIECBAgQIAAAQIEahcQcNQ+AeonQIAAAQIECBAgQIAAAQIFCAg4CmiiEggQIECAAAECBAgQIECAQO0CAo7aJ0D9BAgQIECAAAECBAgQIECgAAEBRwFNVAIBAgQIECBAgAABAgQIEKhdQMBR+wSonwABAgQIECBAgAABAgQIFCAg4CigiUogQIAAAQIECBAgQIAAAQK1Cwg4ap8A9RMgQIAAAQIECBAgQIAAgQIEBBwFNFEJBAgQIECAAAECBAgQIECgdgEBR+0ToH4CBAgQIECAAAECBAgQIFCAgICjgCYqgQABAgQIECBAgAABAgQI1C4g4Kh9AtRPgAABAgQIECBAgAABAgQKEBBwFNBEJRAgQIAAAQIECBAgQIAAgdoFBBy1T4D6CRAgQIAAAQIECBAgQIBAAQICjgKaqAQCBAgQIECAAAECBAgQIFC7gICj9glQPwECBAgQIECAAAECBAgQKEBAwFFAE5VAgAABAgQIECBAgAABAgRqFxBw1D4B6idAgAABAgQIECBAgAABAgUICDgKaKISCBAgQIAAAQIECBAgQIBA7QICjtonQP0ECBAgQIAAAQIECBAgQKAAAQFHAU1UAgECBAgQIECAAAECBAgQqF1AwFH7BKifAAECBAgQIECAAAECBAgUICDgKKCJSiBAgAABAgQIECBAgAABArULCDhqnwD1EyBAgAABAgQIECBAgACBAgQEHAU0UQkECBAgQIAAAQIECBAgQKB2AQFH7ROgfgIECBAgQIAAAQIECBAgUICAgKOAJiqBAAECBAgQIECAAAECBAjULiDgqH0C1E+AAAECBAgQIECAAAECBAoQEHAU0EQlECBAgAABAgQIECBAgACB2gUEHLVPgPoJECBAgAABAgQIECBAgEABAgKOApqoBAIECBAgQIAAAQIECBAgULuAgKP2CVA/AQIECBAgQIAAAQIECBAoQEDAUUATlUCAAAECBAgQIECAAAECBGoXEHDUPgHqJ0CAAAECBAgQIECAAAECBQgIOApoohIIECBAgAABAgQIECBAgEDtAgKO2idA/QQIECBAgAABAgQIECBAoAABAUcBTVQCAQIECBAgQIAAAQIECBCoXUDAUfsEqJ8AAQIECBAgQIAAAQIECBQgIOAooIlKIECAAAECBAgQIECAAAECtQsIOGqfAPUTIECAAAECBAgQIECAAIECBAQcBTRRCQQIECBAgAABAgQIECBAoHYBAUftE6B+AgQIECBAgAABAgQIECBQgICAo4AmKoEAAQIECBAgQIAAAQIECNQuIOCofQLUT4AAAQIECBAgQIAAAQIEChAQcBTQRCUQIECAAAECBAgQIECAAIHaBQQctU+A+gkQIECAAAECBAgQIECAQAECAo4CmqgEAgQIECBAgAABAgQIECBQu4CAo/YJUD8BAgQIECBAgAABAgQIEChAQMBRQBOVQIAAAQIECBAg8P/bsYMCAAAAAmL9W+vhFsF4IUCAAAECdQEHR30B8hMgQIAAAQIECBAgQIAAgQMBB8dBiSIQIECAAAECBAgQIECAAIG6gIOjvgD5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","e":1},{"id":"image_1","w":1080,"h":1080,"u":"","p":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABDgAAAQ4CAYAAADsEGyPAAAgAElEQVR4XuzdXZIU15kG4JOk7qVZgdAKxNwSla3yCoRXAFqB8AqEViC0goEVCK3Apc4Obo1WYLQCw72LnMh2y20hZPIU/ffmeTrCMRdzsvp8z/v573V1VVf8ECBAgAABAgQIECBAgAABAgTCBbrw+7s+AQIECBAgQIAAAQIECBAgQKAoOCwBAQIECBAgQIAAAQIECBAgEC+g4IiP0AAECBAgQIAAAQIECBAgQICAgsMOECBAgAABAgQIECBAgAABAvECCo74CA1AgAABAgQIECBAgAABAgQIKDjsAAECBAgQIECAAAECBAgQIBAvoOCIj9AABAgQIECAAAECBAgQIECAgILDDhAgQIAAAQIECBAgQIAAAQLxAgqO+AgNQIAAAQIECBAgQIAAAQIECCg47AABAgQIECBAgAABAgQIECAQL6DgiI/QAAQIECBAgAABAgQIECBAgICCww4QIECAAAECBAgQIECAAAEC8QIKjvgIDUCAAAECBAgQIECAAAECBAgoOOwAAQIECBAgQIAAAQIECBAgEC+g4IiP0AAECBAgQIAAAQIECBAgQICAgsMOECBAgAABAgQIECBAgAABAvECCo74CA1AgAABAgQIECBAgAABAgQIKDjsAAECBAgQIECAAAECBAgQIBAvoOCIj9AABAgQIECAAAECBAgQIECAgILDDhAgQIAAAQIECBAgQIAAAQLxAgqO+AgNQIAAAQIECBAgQIAAAQIECCg47AABAgQIECBAgAABAgQIECAQL6DgiI/QAAQIECBAgAABAgQIECBAgICCww4QIECAAAECBAgQIECAAAEC8QIKjvgIDUCAAAECBAgQIECAAAECBAgoOOwAAQIECBAgQIAAAQIECBAgEC+g4IiP0AAECBAgQIAAAQIECBAgQICAgsMOECBAgAABAgQIECBAgAABAvECCo74CA1AgAABAgQIECBAgAABAgQIKDjsAAECBAgQIECAAAECBAgQIBAvoOCIj9AABAgQIECAAAECBAgQIECAgILDDhAgQIAAAQIECBAgQIAAAQLxAgqO+AgNQIAAAQIECBAgQIAAAQIECCg47AABAgQIECBAgAABAgQIECAQL6DgiI/QAAQIECBAgAABAgQIECBAgICCww4QIECAAAECBAgQIECAAAEC8QIKjvgIDUCAAAECBAgQIECAAAECBAgoOOwAAQIECBAgQIAAAQIECBAgEC+g4IiP0AAECBAgQIAAAQIECBAgQICAgsMOECBAgAABAgQIECBAgAABAvECCo74CA1AgAABAgQIECBAgAABAgQIKDjsAAECBAgQIECAAAECBAgQIBAvoOCIj9AABAgQIECAAAECBAgQIECAgILDDhAgQIAAAQIECBAgQIAAAQLxAgqO+AgNQIAAAQIECBAgQIAAAQIECCg47AABAgQIECBAgAABAgQIECAQL6DgiI/QAAQIECBAgAABAgQIECBAgICCww4QIECAAAECBAgQIECAAAEC8QIKjvgIDUCAAAECBAgQIECAAAECBAgoOOwAAQIECBAgQIAAAQIECBAgEC+g4IiP0AAECBAgQIAAAQIECBAgQICAgsMOECBAgAABAgQIECBAgAABAvECCo74CA1AgAABAgQIECBAgAABAgQIKDjsAAECBAgQIECAAAECBAgQIBAvoOCIj9AABAgQIECAAAECBAgQIECAgILDDhAgQIAAAQIECBAgQIAAAQLxAgqO+AgNQIAAAQIECBAgQIAAAQIECCg47AABAgQIECBAgAABAgQIECAQL6DgiI/QAAQIECBAgAABAgQIECBAgICCww4QIECAAAECBAgQIECAAAEC8QIKjvgIDUCAAAECBAgQIECAAAECBAgoOOwAAQIECBAgQIAAAQIECBAgEC+g4IiP0AAECBAgQIAAAQIECBAgQICAgsMOECBAgAABAgQIECBAgAABAvECCo74CA1AgAABAgQIECBAgAABAgQIKDjsAAECBAgQIECAAAECBAgQIBAvoOCIj9AABAgQIECAAAECBAgQIECAgILDDhAgQIAAAQIECBAgQIAAAQLxAgqO+AgNQIAAAQIECBAgQIAAAQIECCg47AABAgQIECBAgAABAgQIECAQL6DgiI/QAAQIECBAgAABAgQIECBAgICCww4QIECAAAECBAgQIECAAAEC8QIKjvgIDUCAAAECBAgQIECAAAECBAgoOOwAAQIECBAgQIAAAQIECBAgEC+g4IiP0AAECBAgQIAAAQIECBAgQICAgsMOECBAgAABAgQIECBAgAABAvECCo74CA1AgAABAgQIECBAgAABAgQIKDjsAAECBAgQIECAAAECBAgQIBAvoOCIj9AABAgQIECAAAECBAgQIECAgILDDhAgQIAAAQIECBAgQIAAAQLxAgqO+AgNQIAAAQIECBAgQIAAAQIECCg47AABAgQIECBAgAABAgQIECAQL6DgiI/QAAQIECBAgAABAgQIECBAgICCww4QIECAAAECBAgQIECAAAEC8QIKjvgIDUCAAAECBAgQIECAAAECBAgoOOwAAQIECBAgQIAAAQIECBAgEC+g4IiP0AAECBAgQIAAAQIECBAgQICAgsMOECBAgAABAgQIECBAgAABAvECCo74CA1AgAABAgQIECBAgAABAgQIKDjsAAECBAgQIECAAAECBAgQIBAvoOCIj9AABAgQIECAAAECBAgQIECAgILDDhAgQIAAAQIECBAgQIAAAQLxAgqO+AgNQIAAAQIECBAgQIAAAQIECCg47AABAgQIECBAgAABAgQIECAQL6DgiI/QAAQIECBAgAABAgQIECBAgICCww4QIECAAAECBAgQIECAAAEC8QIKjvgIDUCAAAECBAgQIECAAAECBAgoOOwAAQIECBAgQIAAAQIECBAgEC+g4IiP0AAECBAgQIAAAQIECBAgQICAgsMOECBAgAABAgQIECBAgAABAvECCo74CA1AgAABAgQIECBAgAABAgQIKDjsAAECBAgQIECAAAECBAgQIBAvoOCIj9AABAgQIECAAAECBAgQIECAgILDDhAgQIAAAQIECBAgQIAAAQLxAgqO+AgNQIAAAQIECBAgQIAAAQIECCg47AABAgQIECBAgAABAgQIECAQL6DgiI/QAAQIECBAgAABAgQIECBAgICCww4QIECAAAECBAgQIECAAAEC8QIKjvgIDUCAAAECBAgQIECAAAECBAgoOOwAAQIECBAgQIAAAQIECBAgEC+g4IiP0AAECBAgQIAAAQIECBAgQICAgsMOECBAgAABAgQIECBAgAABAvECCo74CA1AgAABAgQIECBAgAABAgQIKDjsAAECBAgQIECAAAECBAgQIBAvoOCIj9AABAgQIECAAAECBAgQIECAgILDDhAgQIAAAQIECBAgQIAAAQLxAgqO+AgNQIAAAQIECBAgQIAAAQIECCg47AABAgQIECBAgAABAgQIECAQL6DgiI/QAAQIECBAgAABAgQIECBAgICCww4QIECAAAECBAgQIECAAAEC8QIKjvgIDUCAAAECBAgQIECAAAECBAgoOOwAAQIECBAgQIAAAQIECBAgEC+g4IiP0AAECBAgQIAAAQIECBAgQICAgsMOECBAgAABAgQIECBAgAABAvECCo74CA1AgAABAgQIECBAgAABAgQIKDjsAAECBAgQIECAAAECBAgQIBAvoOCIj9AABAgQIECAAAECBAgQIECAgILDDhAgQIAAAQIECBAgQIAAAQLxAgqO+AgNQIAAAQIECBAgQIAAAQIECCg47AABAgQIECBAgAABAgQIECAQL6DgiI/QAAQIECBAgAABAgQIECBAgICCww4QIECAAAECBAgQIECAAAEC8QIKjvgIDUCAAAECBAgQIECAAAECBAgoOOwAAQIECBAgQIAAAQIECBAgEC+g4IiP0AAECBAgQIAAAQIECBAgQICAgsMOECBAgAABAgQIECBAgAABAvECCo74CA1AgAABAgQIECBAgAABAgQIKDjsAAECBAgQIECAAAECBAgQIBAvoOCIj9AABAgQIECAAAECBAgQIECAgILDDhAgQIAAAQIECBAgQIAAAQLxAgqO+AgNQIAAAQIECBAgQIAAAQIECCg47AABAgQIECBAgAABAgQIECAQL6DgiI/QAAQIECBAgAABAgQIECBAgICCww4QIECAAAECBAgQIECAAAEC8QIKjvgIDUCAAAECBAgQIECAAAECBAgoOOwAAQIECBAgQIAAAQIECBAgEC+g4IiP0AAECBAgQIAAAQIECBAgQICAgsMOECBAgAABAgQIECBAgAABAvECCo74CA1AgAABAgQIECBAgAABAgQIKDjsAAECBAgQIECAAAECBAgQIBAvoOCIj9AABAgQIECAAAECBAgQIECAgILDDhAgQIAAAQIECBAgQIAAAQLxAgqO+AgNQIAAAQIECBAgQIAAAQIECCg47AABAgQIECBAgAABAgQIECAQL6DgiI/QAAQIECBAgAABAgQIECBAgICCww4QIECAAAECBAgQIECAAAEC8QIKjvgIDUCAAAECBAgQIECAAAECBAgoOOwAAQIECBAgQIAAAQIECBAgEC+g4IiP0AAECBAgQIAAAQIECBAgQICAgsMOECBAgAABAgQIECBAgAABAvECCo74CA1AgAABAgQIECBAgAABAgQIKDjsAAECBAgQIECAAAECBAgQIBAvoOCIj9AABAgQIECAAAECBAgQIECAgILDDhAgQIAAAQIECBAgQIAAAQLxAgqO+AgNQIAAAQIECBAgQIAAAQIECCg47AABAgQIECBAgAABAgQIECAQL6DgiI/QAAQIECBAgAABAgQIECBAgICCww4QIECAAAECBAgQIECAAAEC8QIKjvgIDUCAAAECBAgQIECAAAECBAgoOOwAAQIECBAgQIAAAQIECBAgEC+g4IiP0AAECBAgQIAAAQIECBAgQICAgsMOECBAgAABAgQIECBAgAABAvECCo74CA1AgAABAgQIECBAgAABAgQIKDjsAAECBAgQIECAAAECBAgQIBAvoOCIj9AABAgQIECAAAECBAgQIECAgILDDhAgQIAAAQIECBAgQIAAAQLxAgqO+AgNQIAAAQIECBAgQIAAAQIECCg47AABAgQIECBAgAABAgQIECAQL6DgiI/QAAQIECBAgAABAgQIECBAgICCww4QIECAAAECBAgQIECAAAEC8QIKjvgIDUCAAAECBAgQIECAAAECBAgoOOwAAQIECBAgQIAAAQIECBAgEC+g4IiP0AAECBAgQIAAAQIECBAgQICAgsMOECBAgAABAgQIECBAgAABAvECCo74CA1AgAABAgQIECBAgAABAgQIKDjsAAECBAgQIECAAAECBAgQIBAvoOCIj9AABAgQIECAAAECBAgQIECAgILDDhAgQIAAAQIECBAgQIAAAQLxAgqO+AgNQIAAAQIECBAgQIAAAQIECCg47AABAgQIECBAgAABAgQIECAQL6DgiI/QAAQIECBAgAABAgQIECBAgICCww4QIECAAAECBAgQIECAAAEC8QIKjvgIDUCAAAECBAgQIECAAAECBAgoOOwAAQIECBAgQIAAAQIECBAgEC+g4IiP0AAECBAgQIAAAQIECBAgQICAgsMOECBAgAABAgQIECBAgAABAvECCo74CA1AgAABAgQIECBAgAABAgQIKDjsAAECBAgQIECAAAECBAgQIBAvoOCIj9AABAgQIECAAAECBAgQIECAgILDDhAgQIAAAQIECBAgQIAAAQLxAgqO+AgNQIAAAQIECBAgQIAAAQIECCg47AABAgQIECBAgAABAgQIECAQL6DgiI/QAAQIECBAgAABAgQIECBAgICCww4QIECAAAECBAgQIECAAAEC8QIKjvgIDUCAAAECBAgQIECAAAECBAgoOOwAAQIECBAgQIAAAQIECBAgEC+g4IiP0AAECBAgQIAAAQIECBAgQICAgsMOECBAgAABAgQIECBAgAABAvECCo74CA1AgAABAgQIECBAgAABAgQIKDjsAAECBAgQIECAAAECBAgQIBAvoOCIj9AABAgQIECAAAECBAgQIECAgILDDhAgQIAAAQIECBAgQIAAAQLxAgqO+AgNQIAAAQIECBAgQIAAAQIECCg47AABAgQIECBAgAABAgQIECAQL6DgiI/QAAQIECBAgAABAgQIECBAgICCww4QIECAAAECBAgQIECAAAEC8QIKjvgIDUCAAAECBAgQIECAAAECBAgoOOwAAQIECBAgQIAAAQIECBAgEC+g4IiP0AAECBAgQIAAAQIECBAgQICAgsMOECBAgAABAgQIECBAgAABAvECCo74CA1AgAABAgQIECBAgAABAgQIKDjsAAECBAgQIECAAAECBAgQIBAvoOCIj9AABAgQIECAAAECBAgQIECAgILDDhAgQIAAAQIECBAgQIAAAQLxAgqO+AgNQIAAAQIECBAgQIAAAQIECCg47AABAgQIECBAgAABAgQIECAQL6DgiI/QAAQIECBAgAABAgQIECBAgICCww4QIECAAAECBAgQIECAAAEC8QIKjvgIDUCAAAECBAgQIECAAAECBAgoOOwAAQIECBAgQIAAAQIECBAgEC+g4IiP0AAECBAgQIAAAQIECBAgQICAgsMOECBAgAABAgQIECBAgAABAvECCo74CA1AgAABAgQIECBAgAABAgQIKDjsAAECBAgQIECAAAECBAgQIBAvoOCIj9AABAgQIECAAAECBAgQIECAgILDDhAgQIAAAQIECBAgQIAAAQLxAgqO+AgNQIAAAQIECBAgQIAAAQIECCg47AABAgQIECBAgAABAgQIECAQL6DgiI/QAAQIECBAgAABAgQIECBAgICCww4QIECAAAECBAgQIECAAAEC8QIKjvgIDUCAAAECBAgQIECAAAECBAgoOOwAAQIECBAgQIAAAQIECBAgEC+g4IiP0AAECBAgQIAAAQIECBAgQICAgsMOECBAgAABAgQIECBAgAABAvECCo74CA1AgAABAgQIECBAgAABAgQIKDjsAAECBAgQIECAAAECBAgQIBAvoOCIj9AABAgQIECAAAECBAgQIECAgILDDhAgQIAAAQIECBAgQIAAAQLxAgqO+AgNQIAAAQIECBAgQIAAAQIECCg47AABAgQIECBAgAABAgQIECAQL6DgiI/QAAQIECBAgAABAgQIECBAgICCww4QIECAAAECBAgQIECAAAEC8QIKjvgIDUCAAAECBAgQIECAAAECBAgoOOwAAQIECBAgQIAAAQIECBAgEC+g4IiP0AAECBAgQIAAAQIECBAgQICAgsMOECBAgAABAgQIECBAgAABAvECCo74CA1AgAABAgQIECBAgAABAgQIKDjsAAECBAgQIECAAAECBAgQIBAvoOCIj9AABAgQIECAAAECBAgQIECAgILDDhAgQIAAAQIECBAgQIAAAQLxAgqO+AgNQIAAAQIECBAgQIAAAQIECCg47AABAgQIECBAgAABAgQIECAQL6DgiI/QAAQIECBAgAABAgQIECBAgICCww4QIECAAAECBAgQIECAAAEC8QIKjvgIDUCAAAECBAgQIECAAAECBAgoOOwAAQIECBAgQIAAAQIECBAgEC+g4IiP0AAECBAgQIAAAQIECBAgQICAgsMOECBAgAABAgQIECBAgAABAvECCo74CA1AgAABAgQIECBAgAABAgQIKDjsAAECBAgQIECAAAECBAgQIBAvoOCIj9AABAgQIECAAAECBAgQIECAgILDDhAgQIAAAQIECBAgQIAAAQLxAgqO+AgNQIAAAQIECBAgQIAAAQIECCg47AABAgQIECBAgAABAgQIECAQL6DgiI/QAAQIECBAgAABAgQIECBAgICCww4QIECAAAECBAgQIECAAAEC8QIKjvgIDUCAAAECBAgQIECAAAECBAgoOOwAAQIECBAgQIAAAQIECBAgEC+g4IiP0AAECBAgQIAAAQIECBAgQICAgsMOECBAgAABAgQIECBAgAABAvECCo74CA1AgAABAgQIECBAgAABAgQIKDjsAAECBAgQIECAAAECBAgQIBAvoOCIj9AABAgQIECAAAECBAgQIECAgILDDhAgQIAAAQIECBAgQIAAAQLxAgqO+AgNQIAAAQIECBAgQIAAAQIECCg47AABAgQIECBAgAABAgQIECAQL6DgiI/QAAQIECBAgAABAgQIECBAgICCww4QIECAAAECBAgQIECAAAEC8QIKjvgIDUCAAAECBAgQIECAAAECBAgoOOwAAQIECBAgQIAAAQIECBAgEC+g4IiP0AAECBAgQIAAAQIECBAgQICAgsMOECBAgAABAgQIECBAgAABAvECCo74CA1AgAABAgQIECBAgAABAgQIKDjsAAECBAgQIECAAAECBAgQIBAvoOCIj9AABAgQIECAAAECBAgQIECAgILDDhAgQIAAAQIECBAgQIAAAQLxAgqO+AgNQIAAAQIECBAgQIAAAQIECCg47AABAgQIECBAgAABAgQIECAQL6DgiI/QAAQIECBAgAABAgQIECBAgICCww4QIECAAAECBAgQIECAAAEC8QIKjvgIDUCAAAECBAgQIECAAAECBAgoOOwAAQIECBAgQIAAAQIECBAgEC+g4IiP0AAECBAgQIAAAQIECBAgQICAgsMOECBAgAABAgQIECBAgAABAvECCo74CA1AgAABAgQIECBAgAABAgQIKDjsAAECBAgQIECAAAECBAgQIBAvoOCIj9AABAgQIECAAAECBAgQIECAgILDDhAgQIAAAQIECBAgQIAAAQLxAgqO+AgNQIAAAQIECBAgQIAAAQIECCg47AABAgQIECBAgAABAgQIECAQL6DgiI/QAAQIECBAgAABAgQIECBAgICCww4QIECAAAECBAgQIECAAAEC8QIKjvgIDUCAAAECBAgQIECAAAECBAgoOOwAAQIECBAgQIAAAQIECBAgEC+g4IiP0AAECBAgQIAAAQIECBAgQICAgsMOECBAgAABAgQIECBAgAABAvECCo74CA1AgAABAgQIECBAgAABAgQIKDjsAAECBAgQIECAAAECBAgQIBAvoOCIj9AABAgQIECAAAECBAgQIECAgILDDhAgQIAAAQIECBAgQIAAAQLxAgqO+AgNQIAAAQIECBAgQIAAAQIECCg47AABAgQIECBAgAABAgQIECAQL6DgiI/QAAQIECBAgAABAgQIECBAgICCww4QIECAAAECBAgQIECAAAEC8QIKjvgIDUCAAAECBAgQIECAAAECBAgoOOwAAQIECBAgQIAAAQIECBAgEC+g4IiP0AAECBAgQIAAAQIECBAgQICAgsMOECBAgAABAgQIECBAgAABAvECCo74CA1AgAABAgQIECBAgAABAgQIKDjsAAECBAgQIECAAAECBAgQIBAvoOCIj9AABAgQIECAAAECBAgQIECAgILDDhAgQIAAAQIECBAgQIAAAQLxAgqO+AgNQIAAAQIECBAgQIAAAQIECCg47AABAgQIECBAgAABAgQIECAQL6DgiI/QAAQIECBAgAABAgQIECBAgICCww4QIECAAAECBAgQIECAAAEC8QIKjvgIDUCAAAECBAgQIECAAAECBAgoOOwAAQIECBAgQIAAAQIECBAgEC+g4IiP0AAECBAgQIAAAQIECBAgQICAgsMOECBAgAABAgQIECBAgAABAvECCo74CA1AgAABAgQIECBAgAABAgQIKDjsAAECBAgQIECAAAECBAgQIBAvoOCIj9AABAgQIECAAAECBAgQIECAgILDDhAgQIAAAQIECBAgQIAAAQLxAgqO+AgNQIAAAQIECBAgQIAAAQIECCg47AABAgQIECBAgAABAgQIECAQL6DgiI/QAAQIECBAgAABAgQIECBAgICCww4QIECAAAECBAgQIECAAAEC8QIKjvgIDUCAAAECBAgQIECAAAECBAgoOOwAAQIECBAgQIAAAQIECBAgEC+g4IiP0AAECBAgQIAAAQIECBAgQICAgsMOECBAgAABAgQIECBAgAABAvECCo74CA1AgAABAgQIECBAgAABAgQIKDjsAAECBAgQIECAAAECBAgQIBAvoOCIj9AABAgQIECAAAECBAgQIECAgILDDhAgQIAAAQIECBAgQIAAAQLxAgqO+AgNQIAAAQIECBAgQIAAAQIECCg47AABAgQIECBAgAABAgQIECAQL6DgiI/QAAQIECBAgAABAgQIECBAgICCww4QIECAAAECBAgQIECAAAEC8QIKjvgIDUCAAAECBAgQIECAAAECBAgoOOwAAQIECBAgQIAAAQIECBAgEC+g4IiP0AAECBAgQIAAAQIECBAgQICAgsMOECBAgAABAgQIECBAgAABAvECCo74CA1AgAABAgQIECBAgAABAgQIKDjsAAECBAgQIECAAAECBAgQIBAvoOCIj9AABAgQIECAAAECBAgQIECAgILDDhAgQIAAAQIECBAgQIAAAQLxAgqO+AgNQIAAAQIECBAgQIAAAQIECCg47AABAgQIECBAgAABAgQIECAQL6DgiI/QAAQIECBAgAABAgQIECBAgICCww4QIECAwJUJbDab7ZX9Mr+IQJjARx999HK3270Mu7brEiBAgACBGyOg4LgxUbgIAQIE1iOw3W5v7/f77TRNd7quu1NK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AAECBAgQIEBAwWEHCBAgQIAAAQIECBAgQIAAgXgBBUd8hAYgQIAAAQIECBAgQIAAAQIEFBx2gAABAgQIECBAgAABAgQIEIgXUHDER2gAAgQIECBAgAABAgQIECBAQMFhBwgQIECAAAECBAgQIECAAIF4AQVHfIQGIECAAAECBAgQIECAAAECBBQcdoAAAQIECBAgQIAAAQIECBCIF1BwxEdoAAIECBAgQIAAAQIECBAgQEDBYQcIECBAgAABAgQIECBAgACBeAEFR3yEBiBAgAABAgQIECBAgAABAgQUHHaAAAECBAgQIECAAAECBAgQiBdQcMRHaAACBAgQIECAAAECBAgQIEBAwWEHCBAgQIAAAQIECBAgQIAAgXgBBUd8hAYgQIAAAQIECBAgQIAAAQIEFBx2gAABAgQIECBAgAABAgQIEIgXUHDER2gAAgQIECBAgAABAgQIECBAQMFhBwgQIECAAAECBAgQIECAAIF4AQVHfIQGIECAAAECBAgQIECAAAECBBQcdoAAAQIECBAgQIAAAQIECBCIF1BwxEdoAAIECBAgQIAAAQIECBAgQEDBYQcIECBAgAABAgQIECBAgACBeAEFR3yEBiBAgAABAgQIECBAgAABAgQUHHaAAAECBAgQIECAAAECBAgQiBdQcMRHaAACBAgQIECAAAECBAgQIEBAwWEHCBAgQIAAAQIECBAgQIAAgXgBBUd8hAYgQIAAAQIECBAgQIAAAQIEFBx2gAABAgQIECBAgAABAgQIEIgXUHDER2gAAgQIECBAgAABAgQIECBAQMFhBwgQIECAAAECBAgQIECAAIF4AQVHfIQGIECAAAECBAgQIECAAAECBBQcdoAAAQIECBAgQIAAAQIECBCIF1BwxEdoAAIECBAgQIAAAQIECBAgQEDBYQcIECBAgAABAgQIECBAgACBeAEFR3yEBiBAgAABAgQIECBAgAABAgQUHHaAAAECBAgQIECAAAECBAgQiBdQcMRHaAACBAgQIECAAAECBAgQIEBAwWEHCBAgQIAAAQIECBAgQIAAgXgBBUd8hAYgQIAAAQIECBAgQIAAAQIEFBx2gAABAgQIECBAgAABAgQIEIgXUHDER2gAAgQIECBAgAABAgQIECBAQMFhBwgQIECAAAECBAgQIECAAIF4AQVHfIQGIECAAAECBAgQIECAAAECBBQcdoAAAQIECBAgQIAAAQIECBCIF1BwxEdoAAIECBAgQIAAAQIECBAgQEDBYQcIECBAgAABAgQIECBAgACBeAEFR3yEBiBAgAABAgQIECBAgAABAgQUHHaAAAECBAgQIECAAAECBAgQiBdQcMRHaAACBAgQIECAAAECBAgQIEBAwWEHCBAgQIAAAQIECBAgQIAAgXgBBUd8hAYgQIAAAQIECBAgQIAAAQIEFBx2gAABAgQIECBAgAABAgQIEIgXUHDER2gAAgQIECBAgAABAgQIECBAQMFhBwgQIECAAAECBAgQIECAAIF4AQVHfIQGIECAAAECBAgQIECAAAECBBQcdoAAAQIECBAgQIAAAQIECBCIF1BwxEdoAAIECBAgQIAAAQIECBAgQEDBYQcIECBAgAABAgQIECBAgACBeAEFR3yEBiBAgAABAgQIECBAgAABAgQUHHaAAAECBAgQIECAAAECBAgQiBdQcMRHaAACBAgQIECAAAECBAgQIEBAwWEHCBAgQIAAAQIECBAgQIAAgXgBBUd8hAYgQIAAAQIECBAgQIAAAQIEFBx2gAABAgQIECBAgAABAgQIEIgXUHDER2gAAgQIECBAgAABAgQIECBAQMFhBwgQIECAAAECBAgQIECAAIF4AQVHfIQGIECAAAECBAgQIECAAAECBBQcdoAAAQIECBAgQIAAAQIECBCIF1BwxEdoAAIECBAgQIAAAQIECBAgQEDBYQcIECBAgAABAgQIECBAgACBeAEFR3yEBiBAgAABAgQIECBAgAABAgQUHHaAAAECBAgQIECAAAECBAgQiBdQcMRHaAACBAgQIECAAAECBAgQIEBAwWEHCBAgQIAAAQIECBAgQIAAgXgBBUd8hAYgQIAAAQIECBAgQIAAAQIEFBx2gAABAgQIECBAgAABAgQIEIgXUHDER2gAAgQIECBAgAABAgQIECBAQMFhBwgQIECAAAECBAgQIECAAIF4AQVHfIQGIECAAAECBAgQIECAAAECBBQcdoAAAQIECBAgQIAAAQIECBCIF1BwxEdoAAIECBAgQIAAAQIECBAgQEDBYQcIECBAgAABAgQIECBAgACBeAEFR3yEBiBAgAABAgQIECBAgAABAgQUHHaAAAECBAgQIECAAAECBAgQiBdQcMRHaAACBAgQIECAAAECBAgQIEBAwWEHCBAgQIAAAQIECBAgQIAAgXgBBUd8hAYgQIAAAQIECBAgQIAAAQIEFBx2gAABAgQIECBAgAABAgQIEIgXUHDER2gAAgQIECBAgAABAgQIECBAQMFhBwgQIECAAAECBAgQIECAAIF4AQVHfIQGIECAAAECBAgQIECAAAECBBQcdoAAAQIECBAgQIAAAQIECBCIF1BwxEdoAAIECBAgQIAAAQIECBAgQEDBYQcIECBAgAABAgQIECBAgACBeAEFR3yEBiBAgAABAgQIECBAgAABAgQUHHaAAAECBAgQIECAAAECBAgQiBdQcMRHaAACBAgQIECAAAECBAgQIEBAwWEHCBAgQIAAAQIECBAgQIAAgXgBBUd8hAYgQIAAAQIECBAgQIAAAQIEFBx2gAABAgQIECBAgAABAgQIEIgXUHDER2gAAgQIECBAgAABAgQIECBAQMFhBwgQIECAAAECBAgQIECAAIF4AQVHfIQGIECAAAECBAgQIECAAAECBBQcdoAAAQIECBAgQIAAAQIECBCIF1BwxEdoAAIECBAgQIAAAQIECBAgQEDBYQcIECBAgAABAgQIECBAgACBeAEFR3yEBiBAgAABAgQIECBAgAABAgQUHHaAAAECBAgQIECAAAECBAgQiBdQcMRHaAACBAgQIECAAAECBAgQIEBAwWEHCBAgQIAAAQIECBAgQIAAgXgBBUd8hAYgQIAAAQIECBAgQIAAAQIEFBx2gAABAgQIECBAgAABAgQIEIgXUHDER2gAAgQIECBAgAABAgQIECBAQMFhBwgQIECAAAECBAgQIECAAIF4AQVHfIQGIECAAAECBAgQIECAAAECBBQcdoAAAQIECBAgQIAAAQIECBCIF1BwxEdoAAIECBAgQIAAAQIECBAgQEDBYQcIECBAgAABAgQIECBAgACBeAEFR3yEBiBAgAABAgQIECBAgAABAgQUHHaAAAECBAgQIECAAAECBAgQiBdQcMRHaAACBAgQIECAAAECBAgQIEBAwWEHCBAgQIAAAQIECBAgQIAAgXgBBUd8hAYgQIAAAQIECBAgQIAAAQIEFBx2gAABAgQIECBAgAABAgQIEIgXUHDER2gAAgQIECBAgAABAgQIECBAQMFhBwgQIECAAAECBAgQIECAAIF4AQVHfIQGIECAAAECBAgQIECAAAECBBQcdoAAAQIECBAgQIAAAQIECBCIF1BwxEdoAAIECBAgQIAAAQIECBAgQEDBYQcIECBAgAABAgQIECBAgACBeAEFR3yEBiBAgAABAgQIECBAgAABAgQUHHaAAAECBAgQIECAAAECBAgQiBdQcMRHaAACBAgQIECAAAECBAgQIEBAwWEHCBAgQIAAAQIECBAgQIAAgXgBBUd8hAYgQIAAAQIECBAgQIAAAQIEFBx2gAABAgQIECBAgAABAgQIEIgXUHDER2gAAgQIECBAgAABAgQIECBAQMFhBwgQIECAAAECBAgQIECAAIF4AQVHfIQGIECAAAECBAgQIECAAAECBBQcdoAAAQIECBAgQIAAAQIECBCIF1BwxEdoAAIECBAgQIAAAQIECBAgQEDBYQcIECBAgAABAgQIECBAgACBeAEFR3yEBiBAgAABAgQIECBAgAABAgQUHHaAAAECBAgQIECAAAECBAgQiBdQcMRHaAACBAgQIECAAAECBAgQIEBAwWEHCBAgQIAAAQIECBAgQIAAgXgBBUd8hAYgQIAAAQIECBAgQIAAAQIEFBx2gAABAgQIECBAgAABAgQIEIgXUHDER2gAAgQIECBAgAABAgQIECBAQMFhBwgQIECAAAECBAgQIECAAIF4AQVHfIQGIECAAAECBAgQIECAAAECBBQcdoAAAQIECBAgQIAAAQIECBCIF1BwxEdoAAIECBAgQIAAAQIECBAgQEDBYQcIECBAgAABAgQIECBAgACBeAEFR3yEBiBAgAABAgQIECBAgAABAgQUHHaAAAECBAgQIECAAAECBAgQiBdQcMRHaAACBAgQIECAAAECBAgQIEBAwWEHCBAgQIAAAQIECBAgQIAAgXgBBUd8hAYgQIAAAQIECBAgQIAAAQIEFBx2gAABAgQIECBAgAABAgQIEIgXUHDER2gAAgQIECBAgAABAgQIECBAQMFhBwgQIECAAAECBAgQIECAAIF4AQVHfIQGIECAAAECBAgQIECAAAECBBQcdoAAAQIECBAgQIAAAQIECBCIF1BwxEdoAAIECBAgQIAAAQIECBAgQEDBYQcIECBAgAABAgQIECBAgACBeAEFR3yEBiBAgAABAgQIECBAgAABAgQUHHaAAAECBAgQIECAAAECBAgQiBdQcMRHaAACBAgQIECAAAECBAgQIEBAwWEHCBAgQIAAAQIECBAgQIAAgXgBBUd8hAYgQIAAAQIECBAgQIAAAQIEFBx2gAABAgQIECBAgAABAgQIEIgXUHDER2gAAgQIECBAgAABAgQIECBAQMFhBwgQIECAAAECBAgQIECAAIF4AQVHfIQGIECAAAECBAgQIECAAAECBBQcdoAAAQIECBAgQIAAAQIECBCIF1BwxEdoAAIECBAgQIAAAQIECBAgQEDBYQcIECBAgAABAgQIECBAgACBeAEFR3yEBiBAgAABAgQIECBAgAABAgQUHHaAAAECBAgQIECAAAECBAgQiBdQcMRHaAACBAgQIECAAAECBAgQIEBAwWEHCBAgQIAAAQIECBAgQIAAgXgBBUd8hAYgQIAAAQIECBAgQIAAAQIEFBx2gAABAgQIECBAgAABAgQIEIgXUHDER2gAAgQIECBAgAABAgQIECBAQMFhBwgQIECAAAECBAgQIECAAIF4AQVHfIQGIECAAAECBAgQIECAAAECBBQcdoAAAQIECBAgQIAAAQIECBCIF1BwxEdoAAIECBAgQIAAAQIECBAgQEDBYQcIECBAgAABAgQIECBAgACBeAEFR3yEBiBAgAABAgQIECBAgAABAgQUHHaAAAECBAgQIECAAAECBAgQiBdQcMRHaAACBAgQIECAAAECBAgQIEBAwWEHCBAgQIAAAQIECBAgQIAAgXgBBUd8hAYgQIAAAQIECBAgQIAAAQIEFBx2gAABAgQIECBAgAABAgQIEIgXUHDER2gAAgQIECBAgAABAgQIECBAQMFhBwgQIECAAAECBAgQIECAAIF4AQVHfIQGIECAAAECBAgQIECAAAECBBQcdoAAAQIECBAgQIAAAQIECBCIF1BwxEdoAAIECBAgQIAAAQIECBAgQEDBYQcIECBAgAABAgQIECBAgACBeAEFR3yEBiBAgAABAgQIECBAgAABAgQUHHaAAAECBAgQIECAAAECBAgQiBdQcMRHaAACBAgQIECAAAECBAgQIEBAwWEHCBAgQIAAAQIECBAgQIAAgXgBBUd8hAYgQIAAAQIECBAgQIAAAQIEFBx2gAABAgQIECBAgAABAgQIEIgXUHDER2gAAgQIECBAgAABAgQIECBAQMFhBwgQIECAAAECBAgQIECAAIF4AQVHfIQGIECAAAECBAgQIECAAAECBBQcdoAAAQIECBAgQIAAAQIECBCIF1BwxEdoAAIECBAgQIAAAQIECBAgQEDBYQcIECBAgAABAgQIECBAgACBeAEFR3yEBiBAgAABAgQIECBAgAABAgQUHHaAAAECBAgQIECAAAECBAgQiBdQcMRHaAACBAgQIECAAAECBAgQIEBAwWEHCBAgQIAAAQIECBAgQIAAgXgBBUd8hAYgQIAAAQIECBAgQIAAAQIEFBx2gAABAgQIECBAgAABAgQIEIgXUHDER2gAAgQIECBAgAABAgQIECBAQMFhBwgQIECAAAECBAgQIECAAIF4AQVHfIQGIECAAAECBAgQIECAAAECBBQcdoAAAQIECBAgQIAAAQIECBCIF1BwxEdoAAIECBAgQIAAAQIECBAgQEDBYQcIECBAgAABAgQIECBAgACB/2/HjmkAAAAQhvl3PR1LKoHCxV7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGKKR1pAAAI3klEQVSvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF7AwbGvUAACBAgQIECAAAECBAgQIEDAwWEDBAgQIECAAAECBAgQIECAwF4gdmkykxIWeegAAAAASUVORK5CYII=","e":1},{"id":"image_2","w":1080,"h":1080,"u":"","p":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABDgAAAQ4CAYAAADsEGyPAAAgAElEQVR4Xuzdf4xl53kf9vc9596ZoahaQyAN16ldDeumWsGNNazTcJ3U1RAJqg2CVKMijagoNndRJFq1ELSEY0u2a3kZwVYiO6a0iWwkMEARbaIi/4T6j0BRaJkgCeu6zTJIE6o/wGWbOoyQ1rsOJc7uzj1vce7uypS4uzNz5/44zzmfAQjZ4j3nvM/neeZo7veee05OfggQIECAAAECBAgQIECAAAECwQVy8PVbPgECBAgQIECAAAECBAgQIEAgCTgMAQECBAgQIECAAAECBAgQIBBeQMARvoUKIECAAAECBAgQIECAAAECBAQcZoAAAQIECBAgQIAAAQIECBAILyDgCN9CBRAgQIAAAQIECBAgQIAAAQICDjNAgAABAgQIECBAgAABAgQIhBcQcIRvoQIIECBAgAABAgQIECBAgAABAYcZIECAAAECBAgQIECAAAECBMILCDjCt1ABBAgQIECAAAECBAgQIECAgIDDDBAgQIAAAQIECBAgQIAAAQLhBQQc4VuoAAIECBAgQIAAAQIECBAgQEDAYQYIECBAgAABAgQIECBAgACB8AICjvAtVAABAgQIECBAgAABAgQIECAg4DADBAgQIECAAAECBAgQIECAQHgBAUf4FiqAAAECBAgQIECAAAECBAgQEHCYAQIECBAgQIAAAQIECBAgQCC8gIAjfAsVQIAAAQIECBAgQIAAAQIECAg4zAABAgQIECBAgAABAgQIECAQXkDAEb6FCiBAgAABAgQIECBAgAABAgQEHGaAAAECBAgQIECAAAECBAgQCC8g4AjfQgUQIECAAAECBAgQIECAAAECAg4zQIAAAQIECBAgQIAAAQIECIQXEHCEb6ECCBAgQIAAAQIECBAgQIAAAQGHGSBAgAABAgQIECBAgAABAgTCCwg4wrdQAQQIECBAgAABAgQIECBAgICAwwwQIECAAAECBAgQIECAAAEC4QUEHOFbqAACBAgQIECAAAECBAgQIEBAwGEGCBAgQIAAAQIECBAgQIAAgfACAo7wLVQAAQIECBAgQIAAAQIECBAgIOAwAwQIECBAgAABAgQIECBAgEB4AQFH+BYqgAABAgQIECBAgAABAgQIEBBwmAECBAgQIECAAAECBAgQIEAgvICAI3wLFUCAAAECBAgQIECAAAECBAgIOMwAAQIECBAgQIAAAQIECBAgEF5AwBG+hQogQIAAAQIECBAgQIAAAQIEBBxmgAABAgQIECBAgAABAgQIEAgvIOAI30IFECBAgAABAgQIECBAgAABAgIOM0CAAAECBAgQIECAAAECBAiEFxBwhG+hAggQIECAAAECBAgQIECAAAEBhxkgQIAAAQIECBAgQIAAAQIEwgsIOMK3UAEECBAgQIAAAQIECBAgQICAgMMMECBAgAABAgQIECBAgAABAuEFBBzhW6gAAgQIECBAgAABAgQIECBAQMBhBggQIECAAAECBAgQIECAAIHwAgKO8C1UAAECBAgQIECAAAECBAgQICDgMAMECBAgQIAAAQIECBAgQIBAeAEBR/gWKoAAAQIECBAgQIAAAQIECBAQcJgBAgQIECBAgAABAgQIECBAILyAgCN8CxVAgAABAgQIECBAgAABAgQICDjMAAECBAgQIECAAAECBAgQIBBeQMARvoUKIECAAAECBAgQIECAAAECBAQcZoAAAQIECBAgQIAAAQIECBAILyDgCN9CBRAgQIAAAQIECBAgQIAAAQICDjNAgAABAgQIECBAgAABAgQIhBcQcIRvoQIIECBAgAABAgQIECBAgAABAYcZIECAAAECBAgQIECAAAECBMILCDjCt1ABBAgQIECAAAECBAgQIECAgIDDDBAgQIAAAQIECBAgQIAAAQLhBQQc4VuoAAIECBAgQIAAAQIECBAgQEDAYQYIECBAgAABAgQIECBAgACB8AICjvAtVAABAgQIECBAgAABAgQIECAg4DADBAgQIECAAAECBAgQIECAQHgBAUf4FiqAAAECBAgQIECAAAECBAgQEHCYAQIECBAgQIAAAQIECBAgQCC8gIAjfAsVQIAAAQIECBAgQIAAAQIECAg4zAABAgQIECBAgAABAgQIECAQXkDAEb6FCiBAgAABAgQIECBAgAABAgQEHGaAAAECBAgQIECAAAECBAgQCC8g4AjfQgUQIECAAAECBAgQIECAAAECAg4zQIAAAQIECBAgQIAAAQIECIQXEHCEb6ECCBAgQIAAAQIECBAgQIAAAQGHGSBAgAABAgQIECBAgAABAgTCCwg4wrdQAQQIECBAgAABAgQIECBAgICAwwwQIECAAAECBAgQIECAAAEC4QUEHOFbqAACBAgQIECAAAECBAgQIEBAwGEGCBAgQIAAAQIECBAgQIAAgfACAo7wLVQAAQIECBAgQIAAAQIECBAgIOAwAwQIECBAgAABAgQIECBAgEB4AQFH+BYqgAABAgQIECBAgAABAgQIEBBwmAECBAgQIECAAAECBAgQIEAgvICAI3wLFUCAAAECBAgQIECAAAECBAgIOMwAAQIECBAgQIAAAQIECBAgEF5AwBG+hQogQIAAAQIECBAgQIAAAQIEBBxmgAABAgQIECBAgAABAgQIEAgvIOAI30IFECBAgAABAgQIECBAgAABAgIOM0CAAAECBAgQIECAAAECBAiEFxBwhG+hAggQIECAAAECBAgQIECAAAEBhxkgQIAAAQIECBAgQIAAAQIEwgsIOMK3UAEECBAgQIAAAQIECBAgQICAgMMMECBAgAABAgQIECBAgAABAuEFBBzhW6gAAgQIECBAgAABAgQIECBAQMBhBggQIECAAAECBAgQIECAAIHwAgKO8C1UAAECBAgQIECAAAECBAgQICDgMAMECBAgQIAAAQIECBAgQIBAeAEBR/gWKoAAAQIECBAgQIAAAQIECBAQcJgBAgQIECBAgAABAgQIECBAILyAgCN8CxVAgAABAgQIECBAgAABAgQICDjMAAECBAgQIECAAAECBAgQIBBeQMARvoUKIECAAAECBAgQIECAAAECBAQcZoAAAQIECBAgQIAAAQIECBAILyDgCN9CBRAgQIAAAQIECBAgQIAAAQICDjNAgAABAgQIECBAgAABAgQIhBcQcIRvoQIIECBAgAABAgQIECBAgAABAYcZIECAAAECBAgQIECAAAECBMILCDjCt1ABBAgQIECAAAECBAgQIECAgIDDDBAgQIAAAQIECBAgQIAAAQLhBQQc4VuoAAIECBAgQIAAAQIECBAgQEDAYQYIECBAgAABAgQIECBAgACB8AICjvAtVAABAgQIECBAgAABAgQIECAg4DADBAgQIECAAAECBAgQIECAQHgBAUf4FiqAAAECBAgQIECAAAECBAgQEHCYAQIECBAgQIAAAQIECBAgQCC8gIAjfAsVQIAAAQIECBAgQIAAAQIECAg4zAABAgQIECBAgAABAgQIECAQXkDAEb6FCiBAgAABAgQIECBAgAABAgQEHGaAAAECBAgQIECAAAECBAgQCC8g4AjfQgUQIECAAAECBAgQIECAAAECAg4zQIAAAQIECBAgQIAAAQIECIQXEHCEb6ECCBAgQIAAAQIECBAgQIAAAQGHGSBAgAABAgQIECBAgAABAgTCCwg4wrdQAQQIECBAgAABAgQIECBAgICAwwwQIECAAAECBAgQIECAAAEC4QUEHOFbqAACBAgQIECAAAECBAgQIEBAwGEGCBAgQIAAAQIECBAgQIAAgfACAo7wLVQAAQIECBAgQIAAAQIECBAgIOAwAwQIECBAgAABAgQIECBAgEB4AQFH+BYqgAABAgQIECBAgAABAgQIEBBwmAECBAgQIECAAAECBAgQIEAgvICAI3wLFUCAAAECBAgQIECAAAECBAgIOMwAAQIECBAgQIAAAQIECBAgEF5AwBG+hQogQIAAAQIECBAgQIAAAQIEBBxmgAABAgQIECBAgAABAgQIEAgvIOAI30IFECBAgAABAgQIECBAgAABAgIOM0CAAAECBAgQIECAAAECBAiEFxBwhG+hAggQIECAAAECBAgQIECAAAEBhxkgQIAAAQIECBAgQIAAAQIEwgsIOMK3UAEECBAgQIAAAQIECBAgQICAgMMMECBAgAABAgQIECBAgAABAuEFBBzhW6gAAgQIECBAgAABAgQIECBAQMBhBggQIECAAAECBAgQIECAAIHwAgKO8C1UAAECBAgQIECAAAECBAgQICDgMAMECBAgQIAAAQIECBAgQIBAeAEBR/gWKoAAAQIECBAgQIAAAQIECBAQcJgBAgQIECBAgAABAgQIECBAILyAgCN8CxVAgAABAgQIECBAgAABAgQICDjMAAECBAgQIECAAAECBAgQIBBeQMARvoUKIECAAAECBAgQIECAAAECBAQcZoAAAQIECBAgQIAAAQIECBAILyDgCN9CBRAgQIAAAQIECBAgQIAAAQICDjNAgAABAgQIECBAgAABAgQIhBcQcIRvoQIIECBAgAABAgQIECBAgAABAYcZIECAAAECBAgQIECAAAECBMILCDjCt1ABBAgQIECAAAECBAgQIECAgIDDDBAgQIAAAQIECBAgQIAAAQLhBQQc4VuoAAIECBAgQIAAAQIECBAgQEDAYQYIECBAgAABAgQIECBAgACB8AICjvAtVAABAgQIECBAgAABAgQIECAg4DADBAgQIECAAAECBAgQIECAQHgBAUf4FiqAAAECBAgQIECAAAECBAgQEHCYAQIECBAgQIAAAQIECBAgQCC8gIAjfAsVQIAAAQIECBAgQIAAAQIECAg4zAABAgQIECBAgAABAgQIECAQXkDAEb6FCiBAgAABAgQIECBAgAABAgQEHGaAAAECBAgQIECAAAECBAgQCC8g4AjfQgUQIECAAAECBAgQIECAAAECAg4zQIAAAQIECBAgQIAAAQIECIQXEHCEb6ECCBAgQIAAAQIECBAgQIAAAQGHGSBAgAABAgQIECBAgAABAgTCCwg4wrdQAQQIECBAgAABAgQIECBAgICAwwwQIECAAAECBAgQIECAAAEC4QUEHOFbqAACBAgQIECAAAECBAgQIEBAwGEGCBAgQIAAAQIECBAgQIAAgfACAo7wLVQAAQIECBAgQIAAAQIECBAgIOAwAwQIECBAgAABAgQIECBAgEB4AQFH+BYqgAABAgQIECBAgAABAgQIEBBwmAECBAgQIECAAAECBAgQIEAgvICAI3wLFUCAAAECBAgQIECAAAECBAgIOMwAAQIECBAgQIAAAQIECBAgEF5AwBG+hQogQIAAAQIECBAgQIAAAQIEBBxmgAABAgQIECBAgAABAgQIEAgvIOAI30IFECBAgAABAgQIECBAgAABAgIOM0CAAAECBAgQIECAAAECBAiEFxBwhG+hAggQIECAAAECBAgQIECAAAEBhxkgQIAAAQIECBAgQIAAAQIEwgsIOMK3UAEECBAgQIAAAQIECBAgQICAgMMMECBAgAABAgQIECBAgAABAuEFBBzhW6gAAgQIECBAgAABAgQIECBAQMBhBggQIECAAAECBAgQIECAAIHwAgKO8C1UAAECBAgQIECAAAECBAgQICDgMAMECBAgQIAAAQIECBAgQIBAeAEBR/gWKoAAAQIECBAgQIAAAQIECBAQcJgBAgQIECBAgAABAgQIECBAILyAgCN8CxVAgAABAgQIECBAgAABAgQICDjMAAECBAgQIECAAAECBAgQIBBeQMARvoUKIECAAAECBAgQIECAAAECBAQcZoAAAQIECBAgQIAAAQIECBAILyDgCN9CBRAgQIAAAQIECBAgQIAAAQICDjNAgAABAgQIECBAgAABAgQIhBcQcIRvoQIIECBAgAABAgQIECBAgAABAYcZIECAAAECBAgQIECAAAECBMILCDjCt1ABBAgQIECAAAECBAgQIECAgIDDDBAgQIAAAQIECBAgQIAAAQLhBQQc4VuoAAIECBAgQIAAAQIECBAgQEDAYQYIECBAgAABAgQIECBAgACB8AICjvAtVAABAgQIECBAgAABAgQIECAg4DADBAgQIECAAAECBAgQIECAQHgBAUf4FiqAAAECBAgQIECAAAECBAgQEHCYAQIECBAgQIAAAQIECBAgQCC8gIAjfAsVQIAAAQIECBAgQIAAAQIECAg4zAABAgQIECBAgAABAgQIECAQXkDAEb6FCiBAgAABAgQIECBAgAABAgQEHGaAAAECBAgQIECAAAECBAgQCC8g4AjfQgUQIECAAAECBAgQIECAAAECAg4zQIAAAQIECBAgQIAAAQIECIQXEHCEb6ECCBAgQIAAAQIECBAgQIAAAQGHGSBAgAABAgQIECBAgAABAgTCCwg4wrdQAQQIECBAgAABAgQIECBAgICAwwwQIECAAAECBAgQIECAAAEC4QUEHOFbqAACBAgQIECAAAECBAgQIEBAwGEGCBAgQIAAAQIECBAgQIAAgfACAo7wLVQAAQIECBAgQIAAAQIECBAgIOAwAwQIECBAgAABAgQIECBAgEB4AQFH+BYqgAABAgQIECBAgAABAgQIEBBwmAECBAgQIECAAAECBAgQIEAgvICAI3wLFUCAAAECBAgQIECAAAECBAgIOMwAAQIECBAgQIAAAQIECBAgEF5AwBG+hQogQIAAAQIECBAgQIAAAQIEBBxmgAABAgQIECBAgAABAgQIEAgvIOAI30IFECBAgAABAgQIECBAgAABAgIOM0CAAAECBAgQIECAAAECBAiEFxBwhG+hAggQIECAAAECBAgQIECAAAEBhxkgQIAAAQIECBAgQIAAAQIEwgsIOMK3UAEECBAgQIAAAQIECBAgQICAgMMMECBAgAABAgQIECBAgAABAuEFBBzhW6gAAgQIECBAgAABAgQIECBAQMBhBggQIECAAAECBAgQIECAAIHwAgKO8C1UAAECBAgQIECAAAECBAgQICDgMAMECBAgQIAAAQIECBAgQIBAeAEBR/gWKoAAAQIECBAgQIAAAQIECBAQcJgBAgQIECBAgAABAgQIECBAILyAgCN8CxVAgAABAgQIECBAgAABAgQICDjMAAECBAgQIECAAAECBAgQIBBeQMARvoUKIECAAAECBAgQIECAAAECBAQcZoAAAQIECBAgQIAAAQIECBAILyDgCN9CBRAgQIAAAQIECBAgQIAAAQICDjNAgAABAgQIECBAgAABAgQIhBcQcIRvoQIIECBAgAABAgQIECBAgAABAYcZIECAAAECBAgQIECAAAECBMILCDjCt1ABBAgQIECAAAECBAgQIECAgIDDDBAgQIAAAQIECBAgQIAAAQLhBQQc4VuoAAIECBAgQIAAAQIECBAgQEDAYQYIECBAgAABAgQIECBAgACB8AICjvAtVAABAgQIECBAgAABAgQIECAg4DADBAgQIECAAAECBAgQIECAQHgBAUf4FiqAAAECBAgQIECAAAECBAgQEHCYAQIECBAgQIAAAQIECBAgQCC8gIAjfAsVQIAAAQIECBAgQIAAAQIECAg4zAABAgQIECBAgAABAgQIECAQXkDAEb6FCiBAgAABAgQIECBAgAABAgQEHGaAAAECBAgQIECAAAECBAgQCC8g4AjfQgUQIECAAAECBAgQIECAAAECAg4zQIAAAQIECBAgQIAAAQIECIQXEHCEb6ECCBAgQIAAAQIECBAgQIAAAQGHGSBAgAABAgQIECBAgAABAgTCCwg4wrdQAQQIECBAgAABAgQIECBAgICAwwwQIECAAAECBAgQIECAAAEC4QUEHOFbqAACBAgQIECAAAECBAgQIEBAwGEGCBAgQIAAAQIECBAgQIAAgfACAo7wLVQAAQIECBAgQIAAAQIECBAgIOAwAwQIECBAgAABAgQIECBAgEB4AQFH+BYqgAABAgQIECBAgAABAgQIEBBwmAECBAgQIECAAAECBAgQIEAgvICAI3wLFUCAAAECBAgQIECAAAECBAgIOMwAAQIECBAgQIAAAQIECBAgEF5AwBG+hQogQIAAAQIECBAgQIAAAQIEBBxmgAABAgQIECBAgAABAgQIEAgvIOAI30IFECBAgAABAgQIECBAgAABAgIOM0CAAAECBAgQIECAAAECBAiEFxBwhG+hAggQIECAAAECBAgQIECAAAEBhxkgQIAAAQIECBAgQIAAAQIEwgsIOMK3UAEECBAgQIAAAQIECBAgQICAgMMMECBAgAABAgQIECBAgAABAuEFBBzhW6gAAgQIECBAgAABAgQIECBAQMBhBggQIECAAAECBAgQIECAAIHwAgKO8C1UAAECBAgQIECAAAECBAgQICDgMAMECBAgQIAAAQIECBAgQIBAeAEBR/gWKoAAAQIECBAgQIAAAQIECBAQcJgBAgQIECBAgAABAgQIECBAILyAgCN8CxVAgAABAgQIECBAgAABAgQICDjMAAECBAgQIECAAAECBAgQIBBeQMARvoUKIECAAAECBAgQIECAAAECBAQcZoAAAQIECBAgQIAAAQIECBAILyDgCN9CBRAgQIAAAQIECBAgQIAAAQICDjNAgAABAgQIECBAgAABAgQIhBcQcIRvoQIIECBAgAABAgQIECBAgAABAYcZIECAAAECBAgQIECAAAECBMILCDjCt1ABBAgQIECAAAECBAgQIECAgIDDDBAgQIAAAQIECBAgQIAAAQLhBQQc4VuoAAIECBAgQIAAAQIECBAgQEDAYQYIECBAgAABAgQIECBAgACB8AICjvAtVAABAgQIECBAgAABAgQIECAg4DADBAgQIECAAAECBAgQIECAQHgBAUf4FiqAAAECBAgQIECAAAECBAgQEHCYAQIECBAgQIAAAQIECBAgQCC8gIAjfAsVQIAAAQIECBAgQIAAAQIECAg4zAABAgQIECBAgAABAgQIECAQXkDAEb6FCiBAgAABAgQIECBAgAABAgQEHGaAAAECBAgQIECAAAECBAgQCC8g4AjfQgUQIECAAAECBAgQIECAAAECAg4zQIAAAQIECBAgQIAAAQIECIQXEHCEb6ECCBAgQIAAAQIECBAgQIAAAQGHGSBAgAABAgQIECBAgAABAgTCCwg4wrdQAQQIECBAgAABAgQIECBAgICAwwwQIECAAAECBAgQIECAAAEC4QUEHOFbqAACBAgQIECAAAECBAgQIEBAwGEGCBAgQIAAAQIECBAgQIAAgfACAo7wLVQAAQIECBAgQIAAAQIECBAgIOAwAwQIECBAgAABAgQIECBAgEB4AQFH+BYqgAABAgQIECBAgAABAgQIEBBwmAECBAgQIECAAAECBAgQIEAgvICAI3wLFUCAAAECBAgQIECAAAECBAgIOMwAAQIECBAgQIAAAQIECBAgEF5AwBG+hQogQIAAAQIECBAgQIAAAQIEBBxmgAABAgQIECBAgAABAgQIEAgvIOAI30IFECBAgAABAgQIECBAgAABAgIOM0CAAAECBAgQIECAAAECBAiEFxBwhG+hAggQIECAAAECBAgQIECAAAEBhxkgQIAAAQIECBAgQIAAAQIEwgsIOMK3UAEECBAgQIAAAQIECBAgQICAgMMMECBAgAABAgQIECBAgAABAuEFBBzhW6gAAgQIECBAgAABAgQIECBAQMBhBggQIECAAAECBAgQIECAAIHwAgKO8C1UAAECBAgQIECAAAECBAgQICDgMAMECBAgQIAAAQIECBAgQIBAeAEBR/gWKoAAAQIECBAgQIAAAQIECBAQcJgBAgQIECBAgAABAgQIECBAILyAgCN8CxVAgAABAgQIECBAgAABAgQICDjMAAECBAgQIECAAAECBAgQIBBeQMARvoUKIECAAAECBAgQIECAAAECBAQcZoAAAQIECBAgQIAAAQIECBAILyDgCN9CBRAgQIAAAQIECBAgQIAAAQICDjNAgAABAgQIECBAgAABAgQIhBcQcIRvoQIIECBAgAABAgQIECBAgAABAYcZIECAAAECBAgQIECAAAECBMILCDjCt1ABBAgQIECAAAECBAgQIECAgIDDDBAgQIAAAQIECBAgQIAAAQLhBQQc4VuoAAIECBAgQIAAAQIECBAgQEDAYQYIECBAgAABAgQIECBAgACB8AICjvAtVAABAgQIECBAgAABAgQIECAg4DADBAgQIECAAAECBAgQIECAQHgBAUf4FiqAAAECBAgQIECAAAECBAgQEHCYAQIECBAgQIAAAQIECBAgQCC8gIAjfAsVQIAAAQIECBAgQIAAAQIECAg4zAABAgQIECBAgAABAgQIECAQXkDAEb6FCiBAgAABAgQIECBAgAABAgQEHGaAAAECBAgQIECAAAECBAgQCC8g4AjfQgUQIECAAAECBAgQIECAAAECAg4zQIAAAQIECBAgQIAAAQIECIQXEHCEb6ECCBAgQIAAAQIECBAgQIAAAQGHGSBAgAABAgQIECBAgAABAgTCCwg4wrdQAQQIECBAgAABAgQIECBAgICAwwwQIECAAAECBAgQIECAAAEC4QUEHOFbqAACBAgQIECAAAECBAgQIEBAwGEGCBAgQIAAAQIECBAgQIAAgfACAo7wLVQAAQIECBAgQIAAAQIECBAgIOAwAwQIECBAgAABAgQIECBAgEB4AQFH+BYqgAABAgQIECBAgAABAgQIEBBwmAECBAgQIECAAAECBAgQIEAgvICAI3wLFUCAAAECBAgQIECAAAECBAgIOMwAAQIECBAgQIAAAQIECBAgEF5AwBG+hQogQIAAAQIECBAgQIAAAQIEBBxmgAABAgQIECBAgAABAgQIEAgvIOAI30IFECBAgAABAgQIECBAgAABAgIOM0CAAAECBAgQIECAAAECBAiEFxBwhG+hAggQIJSAE/8AACAASURBVECAAAECBAgQIECAAAEBhxkgQIAAAQIECBAgQIAAAQIEwgsIOMK3UAEECBAgQIAAAQIECBAgQICAgMMMECBAgAABAgQIECBAgAABAuEFBBzhW6gAAgQIECBAgAABAgQIECBAQMBhBggQIECAAAECBAgQIECAAIHwAgKO8C1UAAECBAgQIECAAAECBAgQICDgMAMECBAgQIAAAQIECBAgQIBAeAEBR/gWKoAAAQIECBAgQIAAAQIECBAQcJgBAgQIECBAgAABAgQIECBAILyAgCN8CxVAgAABAgQIECBAgAABAgQICDjMAAECBAgQIECAAAECBAgQIBBeQMARvoUKIECAAAECBAgQIECAAAECBAQcZoAAAQIECBAgQIAAAQIECBAILyDgCN9CBRAgQIAAAQIECBAgQIAAAQICDjNAgAABAgQIECBAgAABAgQIhBcQcIRvoQIIECBAgAABAgQIECBAgAABAYcZIECAAAECBAgQIECAAAECBMILCDjCt1ABBAgQIECAAAECBAgQIECAgIDDDBAgQIAAAQIECBAgQIAAAQLhBQQc4VuoAAIECBAgQIAAAQIECBAgQEDAYQYIECBAgAABAgQIECBAgACB8AICjvAtVAABAgQIECBAgAABAgQIECAg4DADBAgQIECAAAECBAgQIECAQHgBAUf4FiqAAAECBAgQIECAAAECBAgQEHCYAQIECBAgQIAAAQIECBAgQCC8gIAjfAsVQIAAAQIECBAgQIAAAQIECAg4zAABAgQIECBAgAABAgQIECAQXkDAEb6FCiBAgAABAgQIECBAgAABAgQEHGaAAAECBAgQIECAAAECBAgQCC8g4AjfQgUQIECAAAECBAgQIECAAAECAg4zQIAAAQIECBAgQIAAAQIECIQXEHCEb6ECCBAgQIAAAQIECBAgQIAAAQGHGSBAgAABAgQIECBAgAABAgTCCwg4wrdQAQQIECBAgAABAgQIECBAgICAwwwQIECAAAECBAgQIECAAAEC4QUEHOFbqAACBAgQIECAAAECBAgQIEBAwGEGCBAgQIAAAQIECBAgQIAAgfACAo7wLVQAAQIECBAgQIAAAQIECBAgIOAwAwQIECBAgAABAgQIECBAgEB4AQFH+BYqgAABAgQIECBAgAABAgQIEBBwmAECBAgQIECAAAECBAgQIEAgvICAI3wLFUCAAAECBAgQIECAAAECBAgIOMwAAQIECBAgQIAAAQIECBAgEF5AwBG+hQogQIAAAQIECBAgQIAAAQIEBBxmgAABAgQIECBAgAABAgQIEAgvIOAI30IFECBAgAABAgQIECBAgAABAgIOM0CAAAECBAgQIECAAAECBAiEFxBwhG+hAggQIECAAAECBAgQIECAAAEBhxkgQIAAAQIECBAgQIAAAQIEwgsIOMK3UAEECBAgQIAAAQIECBAgQICAgMMMECBAgAABAgQIECBAgAABAuEFBBzhW6gAAgQIECBAgAABAgQIECBAQMBhBggQIECAAAECBAgQIECAAIHwAgKO8C1UAAECBAgQIECAAAECBAgQICDgMAMECBAgQIAAAQIECBAgQIBAeAEBR/gWKoAAAQIECBAgQIAAAQIECBAQcJgBAgQIECBAgAABAgQIECBAILyAgCN8CxVAgAABAgQIECBAgAABAgQICDjMAAECBAgQIECAAAECBAgQIBBeQMARvoUKIECAAAECBAgQIECAAAECBAQcZoAAAQIECBAgQIAAAQIECBAILyDgCN9CBRAgQIAAAQIECBAgQIAAAQICDjNAgAABAgQIECBAgAABAgQIhBcQcIRvoQIIECBAgAABAgQIECBAgAABAYcZIECAAAECBAgQIECAAAECBMILCDjCt1ABBAgQIECAAAECBAgQIECAgIDDDPRSoHwtjdLV9M5DFXc17eWzae9Qr/UiAgQ6K/C23/ubaSNN0sahF1zSG+mBtP/t12+mN/Ljb/n/D70jLyRAoEsCbzs3fDO9M9VpdOg1TtJ+ejC94dxwaDEvJECAwMoEBBwro3fgWQTKV9LW7e2+8z+rtJGadGKWfb5tm5yuppKuTv/7kl5POe19+z/30uvCkLko2wmBIwuUv5M2017aTDmdSGUaXNw6D/zu/3/kfR5pgyq9nppvh6FXvuMc4dxwJEovJjBPgZWdG+78fXCrmCvf/nthI13NH7r9d8Q8C7UvAgQIEDhQQMBxIJEXrEJgGmQ0t9/I3HrzstnktLmKtdztmFX7h0wbgrRveNoQxJubrrTGOnoiMD0HtL/7bXCZ02ZzJ8zocH3VnTc7zg0d7pKlRReIdG747r8V8kfSrWDUDwECBAgsTEDAsTBaOz6sQHk2baa1229mcjoR4Y3MXUOPW1d9XJmGHlW6kv90ev2wBl5HYMgC5dm0kdanV2NMQ42o54B79bBybhjyeKv9GAJ9PDfcDj3avw+upOvpiqtCjzEgNiVAgMBdBAQcxmLpAtM/WNbSyUlOW/nWG5rOXJkxV4yc9kpKV+pboYfAY664dhZdoPytdHI/pa0qT4ON+Xy9LAqKc0OUTlnnCgQGeG54vSnpyiinV1zhsYKBc0gCBHonIODoXUu7WVD52+nE/iSdrFI6Obg3M3daUtLV24HHK/nPpFe62SmrIrAYgW8Hm3U6mcv0PODHucEMEEjODW8Zgjvh5yS9km6kV1zd4ReEAAECRxcQcBzdzBaHFJiGGvtpexpqlJ5epXFIi7e9rP0jpkyv7vBHzKyGtuu8wPSNyzhtN3XanttNgDtf9fEXWFJ6xbnh+I720F2Bb4caKZ3Mtz748HMXAecCY0GAAIGjCwg4jm5mi/sIvDXUaIQah5qV9saEd/6IcWXHoci8qOMCN/7mrWCzeONy7E7lnC63YYdzw7Ep7aADAs4Nszcht8FnTpedC2Y3tCUBAsMQEHAMo88LrbL9JObmWjqZqnQqN9Wwvks/Z9mSm6vtHzGj6+mlfNYj5ubMa3cLFGjDzcl+OjUNNUrVPsLVzzwFcrOXUrrs3DBPVPtahkB7I/H99XQqpbTt3DAHceeCOSDaBQECfRYQcPS5uwuuzRuaxQKX1FwpOb207n4di4W292MJtJ/Itm9ccqram4X6WYJAe25ow461j6bLSzicQxCYScC5YSa2I23k74QjcXkxAQIDERBwDKTR8yzz+t+a3iTwlDc081S9975KadrHz740vpkuu+HYcswd5f4C7VVb+6N0quS0nXPVz6cgBRiC9tyQS7o82p9e8dVe4eGHwEoFpld0jqeh5ynnhuW1wt8Jy7N2JAIEui8g4Oh+jzqzwumnMSXtFG9oVtKTqr0sdZJe8mZmJfwOmtL0aQdtsJHqdKrxNZTOzMT03ODrK53pxxAX4tzQja77O6EbfbAKAgRWKyDgWK1/iKMLNjrYptxcXrueLrlPRwd708Mltd+hv7GedlKp2k9m/XRZwLmhy93p3doEG91sqaCjm32xKgIEliMg4FiOc8ijtMFGU9JOLi5B724Dm8tr++kFl6d3t0ORV9a+edkbpVNVTqfcHDBaJ6fnBiFotLYFWa9zQ5BG5WavKemlDV9jC9IwyyRAYB4CAo55KPZsH3eCjSLYCNHZ9pMaf8CEaFWYRb71zYuvooRp2z0W2lzy5iZ6D7u1/jf/m7TThp7ODd3qy/1W0/6dMEnp0jv+bHopzqqtlAABArMJCDhmc+vlVm9+JW2lJu2kxtMQIja4/QMm5/SCJytE7F531izg7E4v5rUSb27mJTns/bQ3GC9NOu3Dj7hzkNtH0VfpBU9ni9tDKydA4GABAcfBRr1/xfT79aO00yTfr+9Ds3NpXi+T9MIDZ1P7KMlO/5SvpJ23LPCt//fv/td5enPbW0/qyOlqKnd5NOZb//s6Xc1/2uMzj9r4N55NJ+o6nU5ZwHlUuyivb9/clJvp+S6cG8rfSZtpb/q0jfbn/ueBnNpHEL/7bc4lvfi2/y6ny6mk9slTd37a8+Ctc6Fzw0yj2v6NsFenXeeGmfi6uVFprmxM0vNdvI/XEc8N778H8LW7/K3wu+eCW+eEK84J3RxPqyJwXAEBx3EFg2//rf82nWr/uMyeiBC8k29ffpOay+9Y8f05yt9O22kyDSd2Uk6bqaTt6X+m9L4lgb88fbPzu296LrV/1OSPdD/8WZLP9Mko10dpp6SqPRf4GYBAKc0r1yfphYfOfkcQsJDKb4eYbUDR/nPrPLC83/9713QnHHFuuKfRna+qpVTdPXxeyMTY6XIFmksP/Fhq/3dx6T8dOzfcCUTuhCD+Vlj6RDgggfkJCDjmZxlqT28+m7ZynU6XXJ0ItXCLPZJAufX4yKV873b6x0pJ201O2zlNP5ldVohxJJO3vPjFktKVqr0ipKRL+aPDu+qjveR80qTTbiQ86wjF3W4R54aA54B7NfDFUtLlqv2Ed6DnhvZvhDJOu84NcX/HD7vy0n5tZcFXdgU/N9w5H1xqPyzxAclhJ8vrCKxOQMCxOvuVHLn9ROabo7STG5/WrqQBKzpolZsre5P0/Lw+sW0vWU7r7dea0k7O08vLux5mHFa+DT0uTUOPHj+Gtz0PvFmn3VKqk4eF8bp+Chzn3NC+aZmeA259xeRel4r3BW4w5wZ/I/RlZI9WR6malx689eSl9oORY/30/Nzw2vTvhDS98uWSwONYo2JjAgsREHAshLWbO51+InPrTc2t+xn4GZRATs1eqme/muPmV9JOVdrvYfcq0LjvDJSUXsztHzJNer4vV3hcfzadnFRpt6RqY1C/AIq9p8Bhzw3lK2mrKWm3DTVLSh8cMmkfzw3+RhjyRN+qfXoT0snR79Mz8HPDa23QUUp6fvRn0vOmiACB1QsIOFbfg4WvwFUbCycOdYCSmys3D3E1R3uVxmTt1puZlNJuSuldoQqd/2KvpZSeLynmHzGu2pj/QPRtj3c7N5S/Of3a2Znb54C33+izbwiz1RP+3OCqjdka39etDnM1h3PD3bufc/pq06Tn6xvdvIlrX2dWXQTeKiDg6Pk8+ESm5w2esbySmr3qLldz3Ak1UpV2Uxn2J7QH0E7f0LT/RPjE5neeTSerKu1mV23M+BsznM3ac8PaKP0/OacfzreCTaHG0dof7twwau/H5crOo3V5AK9ur+ZoJum/e+fZ9PqdctsrNfZLOu/ccMgByOmrSdhxSCwvIzA/AQHH/Cw7t6d//exoJ1WNu593rjPdWVDO+UqaTF5Y30gnc/n2p7RDv1LjqA2avqFpSvrCWsduVPrbz6bNUV2fTqW418ZRuzqw19cpbdR12a5y2c45PTyw8hdV7q0rO3L68vgjq3lSxb0Ka6/oeqOud50bFtX6/uy3Svk310eT35NzOl/6c7+tZTeos+eCZUM4HoFlCAg4lqG85GO0b2rW6uqJpiRPSFmyfaTD1SltVlXZruvpE0+EGnNoXknptZTKF8bX05fzEh7Beb8ltwFnrppTJSX32phDb/u6i1GVtqo22PDGZaEt7tK54VvPplNNVbX3UXFuWGjXY+/cuWEx/evSuWAxFdorgdULCDhW34O5rqC9FD1V1W72h8tcXfu0s6pKW6PcflLbmyefdLI9JefnUmmWflXHbz+btsZ1tVtKcjPhTk5GNxbVXq0xytMnoAg3l9wS54YlgzvckQRunxtO5eRKriPBzfDi9lwwyc2FBz6SrsywuU0IELiHgICjR6PxO8/WuzmV9tN4PwTeJlCPy/YopVPJ5efLno4Xc1MujH9ssZeov/FsOtGk+nROZWvZBTpeDIFcpY1RVU5VVTqVUlqPseper/LFkssX1hf85IX2qs46Tf8+cG7o9TjNXtxbzg2u6Jyd8ThbLuXvhOMs0LYEIgkIOCJ16x5rnX7PPlVPNMlXUnrQzrmXMB6X7dontXN3nWGH7ddXLqx9NH15hm3vucntNy87Rbg5T9Ze7auu0mbdfh1NsNHVvjo3dLUzPV9XXaWNuiqnnBs60+ilhJ6dqdZCCCxIQMCxINhl7ba9HL1O1RO+S7ss8TjHGdfTNzTtTWZdgt6ttr2W8/GDDsFGt5raxdVMbxw6KjtVTo91cX3W9DYB5wZDsRSB2+eGU9WtqzpdzbUU9SMd5OWmlPMPLPjKzyOtyIsJBBIQcARq1ncv9dqz7WXG1enAJVj6AgQEGwtAXcwuX0ulnF//senjZg/9I9g4NNVgX+jNS/jWzxR0ODeE7/vCC3BuWDjxXA9QSnqxpHJB0DFXVjsbgICAI2CT28e7/etUn3ZJesDmLXDJdXvz0LrsumJjgciL2PUh/4C5dbVWve33fhFN6M8+R3U51X4lzaeyvejpy+UQn+Levv/OKeeGXvR8YUWM2qs6czrt3LAw4sXtuP07YVTOuBnp4ojtuV8CAo5g/WzDjWupOlPcbyNY5xa33FGVToyqcjrn9O7FHcWelyDw1VKX89/9B8xvP9s+xrfeTm4QuIQWxD3EnYCz8pW0uE2818rv8eamfWraJNWnnBv61/J5VtSeG8a3/kZ4eJ77ta8VCOT8xfUbzYVVP4Z+BZU7JIEjCQg4jsS12he3n9LcTNUTKXn842o70Y2jTy81rX2/vhvdmM8qSkrXcspf2NtvvpDSaDulpr2jvce9zoe3l3sZpbRZ19M3L+/pZYGKeotAfjpVzbN7N0aPODcYjIME2r8R2qs6nRsOkor176d/J7RXdv34fG9YHkvBagncX0DAEWRC2nBjv5leubERZMmWuUCBUZVOjkbTr6O4OdgCnVe16/YPmP39/PykSVdWtQbH7b7AeFR2PP2g+32a5wrbc8NkP7+w36RX5rlf++qXwHg0fTJKe5NxfyP0q7VvrebFSVXOv/Oj6XJ/S1QZgdkEBByzuS11q+kl6k3Vvpn1M3CBqkqbtz+R8XWUIcxCSV+/McnPlybtDaFcNR5OoK7SifrWJ7MuOT8cWf9e5dzQv57OoaLb5wZfWZ2DZZRdlJSffvDHmgtR1mudBJYhIOBYhvIxjiHcOAZezzYdtc+qr30i07O2Hqac6zcn+fnGJ7aHser9a9qrNqqc3t/7QhV4GAHnhsMoDeQ1zg0DafTdy3y5mZQz7zzrao5BT4Hivy0g4OjwMPy/vz46nXJzqsNLtLQlCIzbT2tz2c2VT2uXwN3dQ5T09b32ao7iao7uNmlxK3MeWJxt+D07N4Rv4XEKcG44jl6/ti0lP/3gk67m6FdXVTOLgIBjFrUlbPOvfr3ezTm3Nxj0M2CBtWpyqqrTBwZMoPTvFLi+P8nP7zeV798PaDLGVdmp68ZVGwPq+QylOjfMgBZ9E+eG6B1cyPpfLq7mWAisncYREHB0sFfCjQ42ZclLqnL7ZIRmt0oe/bpk+hCHKyl9/eakcjVHiG7NvshRVbaq7PGOswsOb8v23LA/qV5oSro6vOqHU3HO5cS4ch+e4XT8yJVeSzldeOePt09k80NgeAICjo71XLjRsYasYDl1VU6OKk9IWQF9tENenzTl0n5TvxRt4dZ7f4Gc08Yol52qKo+xIjCDgHPDDGgRNnFuiNCl7qyxpPLV/aaceeiswLM7XbGSZQgIOJahfMhjtOFGKb6Wckiu3r0s12ljrW5OVym9r3fFKWhhAk1Kr00m+dL+JHuk7MKUl7fjtdFku67yaY93XJ55X49USvqX+01+wbmhHx12buhHH1dQxWulqc78G2f3L63g2A5JYCUCAo6VsL/9oMKNjjRiRcuo63KivWrDYx9X1IAeHLak9PLNSXWpmfikJmI7q7psjStfR4nYu66v+fZX2l5wbuh6p+6+vum5oS472VdWYzawI6vOOT3lKysdaYZlLFxAwLFw4oMPINw42KjPrxjVk+2RT2z73OJl1nZ9UvJL+5P8kqetLJN99mNVddoc5+Z0zuk9s+/FlgQOFpiU/KJzw8FOXXnF7XPDTs6u6uxKT8Kvo5Sv7idfWQnfRwUcKCDgOJBosS8QbizWt+t7H4+a3cofL11vU8T1XW+a/NLNSXZJake7d/tGwjt+/zvaoP4ua3pu2G+EoF1tsXNDVzvTk3Xl9HLVNGfeeTZd7klFyiDwNgEBxwqH4hu/PjpdSjq1wiU49IoE2huFrdfNGV9JWVEDBnLYktK12zcifcUVHd1oep3T5qhuBBvdaMeQV3F90oaggo7OzIBzQ2daMYSFXEsln/mes5Pnh1CsGocnIOBYUc+/8TfG2yVPn5ThZ2ACo1xOjOpyxk0EB9b41ZZ7vSnezKyyBe2bl7oSbKyyB459VwHnhhUPhnPDihsw7MM//a6zzYVhE6i+jwICjhV0dRpuFOHGCuhXfshxPdmu6/zBlS/EAoYq0N6j43J7ifrEzUiXMgN1XbZGVTnlHhtL4XaQ2QWuNyW9MmmqS84NsyMeZcv23FBXZdvX1I6i5rULEHjuXWeb9kM3PwR6IyDgWHIr/8Wvpa2qGjmRLNm9C4cbj8vpKpfHurAWayDQlPT1W0GHx8vOexraRz6P0uRkrnIbbDw87/3bH4FFCrTnhkmTLzeT/MoijzPUfbcfdDg3DLX7na375ZKanYfOegpbZztkYUcSEHAciet4L/7Gl9KJZlSdKanaON6ebB1JoH2zs1Y1p31KE6lrw1lre5+Opikv3Sz15TJJe8OpfP6Vjupyoq7KqSqnk76CNn9fe1yuwK17+OTLTcmXXdVxPPu6TpvtlVxVLtvODceztPXCBNqQY/ehs+nKwo5gxwSWJCDgWBL0q8+mjQduVOeFG0sC78hh7txMNPkUtyMdsYz7CZT2qo5SXrk5qd1d/ZCj0n5/vqonJ+uUt/2eHxLNy8IJ3Dk37De1GxYfsnvt//63j4F3bjgkmJd1QeBaytMrOfwN0IVuWMPMAgKOmekOv2Ebbqxfr87kXJ04/FZeGV2gfeMzrpsnvOmJ3slBrn/6ffymya/sNy5T/+4JaN+41NXkZJ3zSffWGOTvx5CLdm64T/edG4b8q9Gb2oUcvWnlcAsRcCyh97/11+vd3H6652cwAlUuJ8YjT0oZTMP7Xej1UtKV9sqOyYA/vW1/p+u62WrP5e6r0e+BV93hBe5c2THkc8Odq7hymgae7z68nlcS6K5ALvnsQ//F5MvdXaGVEbi3gIBjwdPxL35ttJOqtLPgw9h9hwSm4YbHwHaoI5YyT4GS0r8sTX6lSenKpOnvDUrbT2KrXLbqXNo3LVsppXfN09G+CPRNYHpuKPnKraex9P/cUOW0VeXS3m/HuaFvw6yeqUBOQg6jEFNAwLHAvv3WX187WZrmiQUewq47JlDX5cSoTmdySusdW5rlEFiIQCnptfYKj9JUr09yudIEvVFp+yls+9jGlEt7lcYJV2ksZFzsdEAC/Ts3pBM5pS3nhgENsVJTSens73Elh0kIJiDgWFDDXn02ba7fGJ0rTfLElAUZd2234yqfrEbNrnCja52xnmUKtE9eSCW/3pTyemqq15vcXJ1M8uvLXMNBxxqN8lZKzWZOabP9FDbl6RsXoeRBcP49gWMIODccA8+mBFYoIORYIb5DzyQg4JiJ7f4btTcVHV8fn8mluKnoAny7uMtRXbbrKn2wi2uzJgJdEGgvX08l7ZV06xF0JVXT/5xMytVS0tV5rrGuyolcVRupNJs5p/afjVRSe1XGpsvJ5yltXwSOL/Dd54amqV7PVdpb5Lmh/fCpqpr2nODccPwW2sMQBLIrOYbQ5r7UKOBYQCd/61fr3Ul2U9EF0HZyl+O6bI+EG53sjUWFE2hvaDrT1R5u7heu1xZM4CgCzg1H0fJaAosQEHIsQtU+FyAg4Jgz6j//0ni7VGV3zru1u44KjKq8NaqbJzu6PMsiQIAAAQIECBAgMBeBktPZ3+ueHHOxtJPFCQg45mj76pfSiVE1OpOS+27MkbWzu2ovgx/Xqe237+53tksWRoAAAQIECBAgMC8BIce8JO1nUQICjjnK/vNfHZ8r7rsxR9Hu7qoelRPjSrjR3Q5ZGQECBAgQIECAwGIE8uP/5p/bv7SYfdsrgeMJCDiO5/ftrf/vL41ONymdmtPu7KbDAuNROTESbnS4Q5ZGgAABAgQIECCwQIFrqVQ7v/fP37y8wGPYNYGZBAQcM7F950av/trGVtXst19V8NNzgbpKm+O6nPO1lJ43WnkECBAgQIAAAQL3E7iWk5DDiHRPQMBxzJ60j4St3hyfS6W0jx/002OBKqeN8aicySk93OMylUaAAAECBAgQIEDgMAKvrY0n2w+dne/j3g9zYK8hcC8BAccxZ+P/+tV6txSPhD0mY+c3n4YbdTmTs3Cj882yQAIECBAgQIAAgSUJlJfXxs2OkGNJ3A5zoICA40Cie7/g1b+6djJVzRPH2IVNgwisj5rdKuf3BVmuZRIgQIAAAQIECBBYikDO6bmH/9zE1/WXou0gBwkIOA4Suse/b7+akr45Ppeyr6bMSBhms7W6nK5zeizMgi2UAAECBAgQIECAwDIFSv7iiXP755d5SMcicDcBAceMc/Hql0anU1N5asqMflE2G42a7VFVPhhlvdZJgAABAgQIECBAYCUCOZ393j8/+fJKju2gBG4LCDhmGIX2qSlpv3EZ1gx2kTapq3JiPCofi7RmayVAgAABAgQIECCwIoFrk5x3vs/jY1fE77CtgIBjhjl49a+Nz6eSPTVlBrswm+S0sT5q2svs1sOs2UIJECBAgAABAgQIrFbg2vX1/a1HPFlltV0Y8NEFHEds/qt/dbTTpGrniJt5eTCBjXFzzuNggzXNcgkQIECAAAECBFYvUNKLv+/j+94vrb4Tg1yBgOMIbX/1mbTZjEbnUqo2jrCZlwYTWJs+MSV5YkqwvlkuAQIECBAgQIBANwRKSl/8Pjcd7UYzBrYKAccRGv5//LW1J3JJJ4+wiZcGE5jeVDQnNxUN1jfLJUCAAAECBAgQ6JZAzuVDv+9jk+e7tSqr6buAgOOQyJRBhAAAIABJREFUHX71mY2tZuTGoofkCvmyKpcT47q0N491342QHbRoAgQIECBAgACBDglcq9No+3s/vnelQ2uylJ4LCDgO2eD/8+L4fOPGoofUiveyqr2paN2cSVV6ON7qrZgAAQIECBAgQIBAJwVe/r6P7293cmUW1UsBAcch2vq/PTPezlXePcRLvSSowPq42c3uuxG0e5ZNgAABAgQIECDQYYGnv//j+xc6vD5L65GAgOOAZr76bNrYf2N0PjVuLNqjuf+OUkajcnKUy4f7Wp+6CBAgQIAAAQIECKxUIKfHv//j+5dWugYHH4SAgOOANv/vz4x2UuWxsH39bahS2RyPyzn33ehrh9VFgAABAgQIECCwaoGc8mv7129uP/JUurrqtTh+vwUEHPfp7/TqjWuj80129UZffw02RuVMzuXdfa1PXQQIECBAgAABAgS6IJBz+uL3f3z/fBfWYg39FRBw3Ke3X39mbTdVyU1xejr/46o5VVfpAz0tT1kECBAgQIAAAQIEOiXQPjr23/4vPTq2U03p2WIEHPdo6KvPpM0b1ZqEsWcDf6ecuv1qyshXU3raXmURIECAAAECBAh0U+Bac2N/y1dVutmcPqxKwHGPLk6v3kiu3ujDkN+thrWxr6b0tbfqIkCAAAECBAgQ6K5ASemLj/xXvqrS3Q7FXpmA4y79a6/e2Euu3og92vde/XiUTo2qxldT+tpgdREgQIAAAQIECHRaoDTp8Uc+4akqnW5S0MUJOO7SuPbqjcbVG0FH+v7LrquyuVYnT03pZXcVRYAAAQIECBAgEETgtXLTU1WC9CrUMgUc39Wuf/RM2txw9UaoIT7KYtfH5Ymc0nuOso3XEiBAgAABAgQIECAwZ4GSn37kEzcuzHmvdjdwAQHHdw3AK7+yfrrkcmrgc9HL8kd1OllX5cO9LE5RBAgQIECAAAECBIIJ5Lp+5JGP710JtmzL7bCAgOMtzXn1QtrY+5618ymnjQ73zNJmEMglbayNp09NedcMm9uEAAECBAgQIECAAIH5C7z473zi5s78d2uPQxUQcLyl8//smY2dJjV+wXr427BWl506p/f3sDQlESBAgAABAgQIEAgrkFP+0COfuPF82AIsvFMCAo63tOOf/pX18yWVzU51yGKOLZCrsrk+Sp889o7sgAABAgQIECBAgACBeQu89gOfuLk1753a3zAFBBy3+/5Pnhlv5ybvDnMM+l31+ricyTm9u99Vqo4AAQIECBAgQIBAVIH89A+44WjU5nVq3QKO2+34X39lfC6VfKJT3bGYYwuMqrRV1+XJY+/IDggQIECAAAECBAgQWJTAtXpyc+uRp9LVRR3AfochIOBIKf2zZza29pvmzDBaPqwqN8blfHZj0WE1XbUECBAgQIAAAQLxBHJ67t/9xE3vyeJ1rlMrFnCklP7pL6/tTnLa7lRnLObYAuOq2a7r/MFj78gOCBAgQIAAAQIECBBYuMD+pH7kvU95bOzCoXt8gMEHHO2jYd94cO3TPe7xIEvLVdpYq8u5nD0WdpADoGgCBAgQIECAAIFwAiWl5/69T7qKI1zjOrTgwQccL/+V8amqyac71BNLmYPAeFR2qspjYedAaRcECBAgQIAAAQIEliaQm/L4739q/9LSDuhAvRIQcPzS+vnk0bC9Guq6ShvjUTmfUlrvVWGKIUCAAAECBAgQINBzgZLSiyc/eXOn52Uqb0ECgw44/snnHzwxyZNzC7K12xUJrLVXb+Tm/Ss6vMMSIECAAAECBAgQIHAMgaaUx9/rKo5jCA5300EHHP/4l9d2S6ncXLRH89/ee2O9bly90aOeKoUAAQIECBAgQGBYAqXkF08+dd1VHMNq+1yqHXTAcfnza5/OudqYi6SddEJgXLf33iiu3uhENyyCAAECBAgQIECAwGwCpVSPv/epPffimI1vsFsNNuD4x7+8dnJSqicG2/keFt5evbHh6o0edlZJBAgQIECAAAECgxMo6asnn7qxO7i6FXwsgcEGHC//0toTTVOdPJaejTsl0D45pXb1Rqd6YjEECBAgQIAAAQIEZheoHnnvU3tXZt/elkMTGGTA8bULaWPzgY1PD63Zfa93Y61pe+rJKX1vtPoIECBAgAABAgSGIvDce5+6cWYoxarz+AKDDDj+0efH26nULnc6/vx0Zg/jUbNdVemDnVmQhRAgQIAAAQIECBAgcGyBnFzFcWzEAe1gkAHH//JLa08UX0/p1Zivj5vzVUrv6lVRiiFAgAABAgQIECAwcIFS0tM/+BM3LgycQfmHFBhkwPE//+UNvyCHHJAILxvV6eSoaj4cYa3WSIAAAQIECBAgQIDA4QVKStdu5htbjz6Vrh5+K68cqsDgAo7f/MW1k7n29JQ+Dfz6qDmTc3p3n2pSCwECBAgQIECAAAECtwRKKWf//Z+4+WUeBA4SGF7A8ZfXdnOqtg+C8e9jCFS5bK7V5ZMxVmuVBAgQIECAAAECBAjMIPDyD/7EDe/hZoAb2ibDCzj+0tqnS6k2htbovta7Pm52q5ze19f61EWAAAECBAgQIECAQEqlqR7/Az+5d4kFgfsJDCrg+I3PP3giTybnjEQ/BHKVNtZHzXmPhu1HP1VBgAABAgQIECBA4F4CpaTnfugveGSsCbm/wKACjv/pcxs7KaX2Hz89EFirPRq2B21UAgECBAgQIECAAIFDCezXNx5ys9FDUQ32RYMKOH7zc+vnSsonBtvtnhW+Xk/O5So/3LOylEOAAAECBAgQIECAwF0ESi5nf8jNRs3GfQQGE3B87ULaeMf6xqdNQz8ERnU5Ma7Kx/pRjSoIECBAgAABAgQIEDiEwMs/9BfcbPQQToN9yWACjvbxsJPs8bB9mfS1ujld5fxYX+pRBwECBAgQIECAAAECBws0pXn00Z+6efngV3rFEAUGE3D8xufWT5eUTw2xyX2seWNU2qtx1vtYm5oIECBAgAABAgQIELiHQElffN9PXW8fNOCHwNsEBhNw/I+fWz+XGvff6MPvwGiUTo6q8uE+1KIGAgQIECBAgAABAgSOJPDa+37y+taRtvDiwQgMIuCY3n9j7P4bfZnqtXF5Iuf0nr7Uow4CBAgQIECAAAECBA4vUFJ+/NGf3Lt0+C28cigCgwg4/sEvrp3Mxf03+jDUVUob6+PyqT7UogYCBAgQIECAAAECBI4ukHN57n0/eePM0be0Rd8FBhFw/MPPbuykKu30vZlDqG9cl+26Sh8cQq1qJECAAAECBAgQIEDgrgLXHv2p65tsCHy3wDACjl/YaNM939PqwfyvjXw9pQdtVAIBAgQIECBAgACBYwnk7GsqxwLs6cZDCTgu9LR/gyqr/XrKeOTrKYNqumIJECBAgAABAgQI3EWglPTFH/60p6kYju8U6H3A8Ruff/DEzZuTcxofX2BUle2xr6fEb6QKCBAgQIAAAQIECBxf4LX/4FOepnJ8xn7tofcBx9//hQe3UzPZ7VfbhlnN+rjs5pzeN8zqVU2AAAECBAgQIECAwFsFqtw8+uhP3bxMhcAdgd4HHP/gs+unS8qntDy+wMZa+XRKaT1+JSogQIAAAQIECBAgQOD4AvnpH/7UntsRHB+yN3vofcDx99sbjDZuMBp9Yqs6bY2r8mT0OqyfAAECBAgQIECAAIG5Cbz8Bz99fXtue7Oj8AK9Dzj+3mc3JHrhxzSltVFzus75sR6UogQCBAgQIECAAAECBOYkUK9ff+jRp9LVOe3OboIL9Drg+NqFzc1RfeN88B5Zfvu9lNHkXM7pYRgECBAgQIAAAQIECBC4I5Bz+dAf/NSN54kQaAV6HnBsbI3q6oxWxxaoqrK5VpVPxq7C6gkQIECAAAECBAgQmLdASem5P/TTe97zzRs26P56HXD8vc9u7KSm2gnaG8u+LVCPyvaoKh8EQoAAAQIECBAgQIAAgbcKlJRee+yn97aoEGgFeh5wPHi6aYonqASf9fbxsFUuHg8bvI+WT4AAAQIECBAgQGARApOUHvmRn967soh922csgV4HHH/3L26cKaWS5sWaybetdmPctPdReVfwMiyfAAECBAgQIECAAIEFCOSSzv6hn9378gJ2bZfBBHodcLz4Fzc+nUq1EawnlvsWgen9N2r33zAUBAgQIECAAAECBAjcU+C5x37GfTjMR8+/ovLi0+/wiNjgUz6qy3bt/hvBu2j5BAgQIECAAAECBBYq8Nqpn3EfjoUKB9l5b6/g+NqFB09UuZwL0gfLvIfAWl12c+X+GwaEAAECBAgQIECAAIF7C6zf2Hjo0QtXrzIatkCPA46NrZQ8Ijb6eG+MmnM5p4ej12H9BAgQIECAAAECBAgsTiDn6kOP/cy3nl/cEew5gkCPA44HTqWUT0dogjXeQyCXjY1R+RQfAgQIECBAgAABAgQIHCDw9I/87J5bFAx8THoccGzspFTtDLy/ocuv67Q1qponQxdh8QQIECBAgAABAgQILEPgxT/8s3ve/y1DusPH6HHA8eDplMqpDttb2gECa3Wzk3N6PygCBAgQIECAAAECBAgcIHDtD//Xe5uUhi3Q44Bj40yTqq1htzd29et180TO6T2xq7B6AgQIECBAgAABAgSWIVDV6ZEf+em9K8s4lmN0U0DA0c2+WFVKaWPUnE8pvQsGAQIECBAgQIAAAQIEDhJoSnr8R39u79JBr/Pv+yvQ24Djf7jwwPmUskuUAs/uxqj5+cDLt3QCBAgQIECAAAECBJYpUNLTf+Tn3Gh0meRdO1Z/A47PvMMddLs2bUdYT1WlrXHtBqNHIPNSAgQIECBAgAABAsMWKPmr/9Fn3twdNsKwq+9twPHfCzhCT/Za3ZyqqvyB0EVYPAECBAgQIECAAAECyxR4+Ud/7s3tZR7QsbolIODoVj+s5rbA2qicrnJ6DAgBAgQIECBAgAABAgQOK/CjP/dmb9/jHtZgyK/rZfMvXEjVH2ne8ZkhNzZ67et1OZNzenf0OqyfAAECBAgQIECAAIHlCew35ZHHL3iSyvLEu3Wk3gYcPyLg6NakHXE17RNUcsqeoHJENy8nQIAAAQIECBAgMGSBnIsnqQx4AAQcA25+l0t/YFQ8QaXLDbI2AgQIECBAgAABAt0UeOo//sybX+jm0qxq0QICjkUL2/+RBeqUTozr8rEjb2gDAgQIECBAgAABAgQGLpCffv/Pf8sTNQc6Bf0NOPbdgyPqTNfTR8SWJ6Ou37oJECBAgAABAgQIEFiZwIvv//k3d1Z2dAdeqYCAY6X8Dn43gXrUnBplj4g1HQQIECBAgAABAgQIHE0gpyTgOBpZr17d24DjMVdwhB3UtVHZqVN6f9gCLJwAAQIECBAgQIAAgdUIlHJt5+m9zdUc3FFXLSDgWHUHHP9tAuuj8kRO6T1oCBAgQIAAAQIECBAgcFSBxy+82cv3uUd1GOLre9n4CxdS5QqOuOO8PipnckrvjluBlRMgQIAAAQIECBAgsDqB6nsfv/DN11d3fEdelYCAY1XyjntPgbVROZdLehgRAQIECBAgQIAAAQIEjiqQc/Xo4xe+efmo23l9fIHeBhz/oXtwhJ3OB+ry82EXb+EECBAgQIAAAQIECKxUQMCxUv6VHry/AceNd35mpbIOPrPAxngi4JhZz4YECBAgQIAAAQIEhi1QcvXH/9iFb74wbIVhVi/gGGbfO1t1XZWNcV0+1dkFWhgBAgQIECBAgAABAt0WyPnsH73wzS93e5FWtwgBAcciVO1zZoGcyub6qHxy5h3YkAABAgQIECBAgACBYQuUfPaPflbAMcQh6G3A8cO+ohJynutUNtcEHCF7Z9EECBAgQIAAAQIEuiCQBRxdaMNK1iDgWAm7g95LoA04xgIOA0KAAAECBAgQIECAwIwCOadf/GNPf+tnZ9zcZoEFBByBm9fHpVcpnVirm4/1sTY1ESBAgAABAgQIECCweIGS8sX/5LPf9LX3xVN37ggCjs61ZNgLqnPZGlXlyWErqJ4AAQIECBAgQIAAgdkFBByz28XesrcBx6N7HhMbcTTHtYAjYt+smQABAgQIECBAgEB3BAQc3enFclci4Fiut6MdICDgMCIECBAgQIAAAQIECBxPQMBxPL+4Wws44vaulytvA446+4pKL5urKAIECBAgQIAAAQLLEMj54gfcg2MZ0p07hoCjcy0Z9oKm9+CoBRzDngLVEyBAgAABAgQIEJhdIAs4ZscLvqWAI3gD+7Z8AUffOqoeAgQIECBAgAABAssVEHAs17tLR+ttwPE+Nxnt0pwdei2jXLbGnqJyaC8vJECAAAECBAgQIEDgOwVyyhc/8AseEzvEuehvwPEtT1GJONCjyldUIvbNmgkQIECAAAECBAh0RyBf/OMCju60Y4krEXAsEduhDhYQcBxs5BUECBAgQIAAAQIECNxPQMAx1PkQcAy18x2te1Tlrbpqnuzo8iyLAAECBAgQIECAAIGOC5SSLv6Jz/mKSsfbtJDl9Tbg+AO+orKQgVn0TtuAYyzgWDSz/RMgQIAAAQIECBDorYCAo7etPbAwAceBRF6wTIE24BgJOJZJ7lgECBAgQIAAAQIE+iXgCo5+9fMI1Qg4joDlpYsXqKq8tSbgWDy0IxAgQIAAAQIECBDoq4CAo6+dPbAuAceBRF6wTIE24Bhn9+BYprljESBAgAABAgQIEOiTQEnp4p90D44+tfTQtfQ24PjBNzwm9tBT0KEXTu/BUQs4OtQSSyFAgAABAgQIECAQSkDAEapdc12sgGOunHZ2XIHpPTgEHMdltD0BAgQIECBAgACBIQu4gmOg3RdwDLTxXS279hSVrrbGuggQIECAAAECBAhEEbj4J/+Sx8RGadY81yngmKemfR1boE55azTyFZVjQ9oBAQIECBAgQIAAgeEKXPxPBRyD7H5vA473ugdHyIEeCzhC9s2iCRAgQIAAAQIECHRIQMDRoWYscykCjmVqO9aBAgKOA4m8gAABAgQIECBAgACB+wsIOAY6If0NOH7HU1QizvT0HhxuMhqxddZMgAABAgQIECBAoBMCOScBRyc6sfxFCDiWb+6I9xFoAw5PUTEiBAgQIECAAAECBAjMLJDTxV334JiZL/KGAo7I3evh2gUcPWyqkggQIECAAAECBAgsU0DAsUztTh2rtwHHe37nez7TKWmLOZTAqCpbo8pTVA6F5UUECBAgQIAAAQIECLxNIKd0cffzHhM7xNEQcAyx6x2ueZTL1qguT3Z4iZZGgAABAgQIECBAgECHBW4FHG98ssNLtLQFCQg4FgRrt7MJCDhmc7MVAQIECBAgQIAAAQK3BAQcw52E3gYcv99XVEJOdRtwjCtXcIRsnkUTIECAAAECBAgQ6IJAThf/M1dwdKETS19DfwOOq+7BsfRpmsMBR3V7Dw4Bxxwo7YIAAQIECBAgQIDAIAVKThf/lIBjkL0XcAyy7d0tWsDR3d5YGQECBAgQIECAAIEIAgKOCF1azBoFHItxtdcZBaq6bK25gmNGPZsRIECAAAECBAgQICDgGO4M9Dbg+AFfUQk51dN7cHiKSsjeWTQBAgQIECBAgACBjghc/FO/7CkqHenFUpch4Fgqt4MdJCDgOEjIvydAgAABAgQIECBA4AABAcdAR0TAMdDGd7Xs6WNifUWlq+2xLgIECBAgQIAAAQLdFyjp4n/+K67g6H6j5r9CAcf8Te3xGAK1x8QeQ8+mBAgQIECAAAECBAgkAcdgh6C/Acf/5zGxEae6rsrWyD04IrbOmgkQIECAAAECBAh0QiALODrRh1UsorcBxyMCjlXM07GPORZwHNvQDggQIECAAAECBAgMWUDAMdzuCziG2/tOVt4GHLV7cHSyNxZFgAABAgQIECBAIIjAxQ+7B0eQVs13mQKO+Xra2zEF2q+ojAUcx1S0OQECBAgQIECAAIFBCwg4Btp+AcdAG9/VsusqeYpKV5tjXQQIECBAgAABAgQCCORUBBwB+rSIJfY24Hi3e3AsYl4Wvs9RlbbGuTy58AM5AAECBAgQIECAAAECvRSYBhzPeExsL5t7QFECjiF2vcM1twHHSMDR4Q5ZGgECBAgQIECAAIGuC5SLHxFwdL1JC1lffwOOf+UxsQuZmAXvdBpweEzsgpXtngABAgQIECBAgECfBQQcfe7u/WoTcAy18x2tuw04agFHR7tjWQQIECBAgAABAgS6L1BSufhRV3B0v1ELWGFvA47vdwXHAsZl8buc3oPDU1QWD+0IBAgQIECAAAECBHoqULKAo6etPbAsAceBRF6wTIFRSlujkZuMLtPcsQgQIECAAAECBAj0SkDA0at2HqUYAcdRtLx24QICjoUTOwABAgQIECBAgACBfgsIOPrd3/tUJ+AYbOu7WXiV0tbYPTi62RyrIkCAAAECBAgQIBBCoFz8s1/0mNgQrZrzInsbcPxb3/AUlTnPylJ2N70Hh4BjKdYOQoAAAQIECBAgQKCfAgKOfvb14KoEHAcbecUSBQQcS8R2KAIECBAgQIAAAQK9FBBw9LKthyhKwHEIJC9ZnkDtKSrLw3YkAgQIECBAgAABAj0UyFnA0cO2HqokAcehmLxoWQJtwDHymNhlcTsOAQIECBAgQIAAgf4J5HLxx92Do399PURFAo5DIHnJ8gTqnLZG7sGxPHBHIkCAAAECBAgQINA3AQFH3zp66Hp6G3B87zc2P3NoBS/sjMD4/2/vjnHjKMMADM9vOxVF3NH6IBQ+CVhEUYIUKVdBiwhIKXIIDpDrUCJ6dtEGaLG0Y4/mnXlqtN75nu+rXs2GcRQ4VrMND0KAAAECBAgQIEAgKCBwBJf2NI8scDyNo7/yRALX43j34mr67on+nD9DgAABAgQIECBAgMDOBMY0Hb796c/3OxvbuNM0bTdw/O4NjuKFX18f724EjuLqPDMBAgQIECBAgACBtQgcHgSOtexi0ecQOBbl9mWPCQgcjwn57wQIECBAgAABAgQIPCIgcOz0RDYbOL72BkfypG+uj3cvhp+oJJfnoQkQIECAAAECBAisQeA0HR5+9hOVNaxi6WcQOJYW933/K3AOHDcChyshQIAAAQIECBAgQOBCgdNpOnwvcFyo1/7YJgPHaZrGjw+3L9ur2efTv/zqr2/GGL/tc3pTEyBAgAABAgQIECAwV0DgmCvY/fwmA0d3HZ7807vb+9N0/EyCAAECBAgQIECAAAECFwl4g+Miti18SODYwhY3NMOXwHESODa0UqMQIECAAAECBAgQWFrAT1SWFl/J9wkcK1mEx/hH4OO72/srgcM5ECBAgAABAgQIECBwucDhlX+D43K98CcFjvDytvjoAscWt2omAgQIECBAgAABAosKCByLcq/nywSO9ezCk/z7Bsc4+omKYyBAgAABAgQIECBA4DKBMU2HVx/8b2Iv02t/SuBo729zT39+g0Pg2NxaDUSAAAECBAgQIEBgMQGBYzHq1X2RwLG6lez7gc6BY/IGx76PwPQECBAgQIAAAQIE5gkcXnuDY55g9NMCR3RxW33sj29v76fhJypb3a+5CBAgQIAAAQIECDy7wJgEjmdHXucXCBzr3Mtun+ocOE7j9Hm3AAYnQIAAAQIECBAgQGCWwBjj8PrDH+9n/REfTgoIHMm1bfehBY7t7tZkBAgQIECAAAECBJYQEDiWUF7ndwgc69zLbp/ql7e391fe4Njt/g1OgAABAgQIECBAYLbAGIc33uCYzVj8AwJHcWsbfuYvgWPyE5UNr9hoBAgQIECAAAECBJ5ZYBze/OonKs+MvMo/L3Csci37fahz4BgCx34PwOQECBAgQIAAAQIEZgqcpnH4QeCYqdj8uMDR3Ntmn1rg2OxqDUaAAAECBAgQIEBgEQGBYxHmVX6JwLHKtez3oc6BYzr5icp+L8DkBAgQIECAAAECBGYKDG9wzBTMflzgyK7OgxMgQIAAAQIECBAgQIAAAQL/CQgcboEAAQIECBAgQIAAAQIECBDICwgc+RUagAABAgQIECBAgAABAgQIEBA43AABAgQIECBAgAABAgQIECCQFxA48is0AAECBAgQIECAAAECBAgQICBwuAECBAgQIECAAAECBAgQIEAgLyBw5FdoAAIECBAgQIAAAQIECBAgQEDgcAMECBAgQIAAAQIECBAgQIBAXkDgyK/QAAQIECBAgAABAgQIECBAgIDA4QYIECBAgAABAgQIECBAgACBvIDAkV+hAQgQIECAAAECBAgQIECAAAGBww0QIECAAAECBAgQIECAAAECeQGBI79CAxAgQIAAAQIECBAgQIAAAQIChxsgQIAAAQIECBAgQIAAAQIE8gICR36FBiBAgAABAgQIECBAgAABAgQEDjdAgAABAgQIECBAgAABAgQI5AUEjvwKDUCAAAECBAgQIECAAAECBAgIHG6AAAECBAgQIECAAAECBAgQyAsIHPkVGoAAAQIECBAgQIAAAQIECBAQONwAAQIECBAgQIAAAQIECBAgkBcQOPIrNAABAgQIECBAgAABAgQIECAgcLgBAgQIECBAgAABAgQIECBAIC8gcORXaAACBAgQIECAAAECBAgQIEBA4HADBAgQIECAAAECBAgQIECAQF5A4Miv0AAECBAgQIAAAQIECBAgQICAwOEGCBAgQIAAAQIECBAgQIAAgbyAwJFfoQEIECBAgAABAgQIECBAgAABgcMNECBAgAABAgQIECBAgAABAnkBgSO/QgMQIECAAAECBAgQIECAAAECAocbIECAAAECBAgQIECAAAECBPICAkd+hQYgQIAAAQIECBAgQIAAAQIEBA43QIAAAQIECBAgQIAAAQIECOQFBI78Cg1AgAABAgQIECBAgAABAgQICBxugAABAgQIECBAgAABAgQIEMgLCBz5FRqAAAECBAgQIECAAAECBAgQEDjcAAECBAgQIECAAAECBAgQIJAXEDjyKzQAAQIECBAgQIAW8XlIAAAeVElEQVQAAQIECBAgIHC4AQIECBAgQIAAAQIECBAgQCAvIHDkV2gAAgQIECBAgAABAgQIECBAQOBwAwQIECBAgAABAgQIECBAgEBeQODIr9AABAgQIECAAAECBAgQIECAgMDhBggQIECAAAECBAgQIECAAIG8gMCRX6EBCBAgQIAAAQIECBAgQIAAAYHDDRAgQIAAAQIECBAgQIAAAQJ5AYEjv0IDECBAgAABAgQIECBAgAABAgKHGyBAgAABAgQIECBAgAABAgTyAgJHfoUGIECAAAECBAgQIECAAAECBAQON0CAAAECBAgQIECAAAECBAjkBQSO/AoNQIAAAQIECBAgQIAAAQIECAgcboAAAQIECBAgQIAAAQIECBDICwgc+RUagAABAgQIECBAgAABAgQIEBA43AABAgQIECBAgAABAgQIECCQFxA48is0AAECBAgQIECAAAECBAgQICBwuAECBAgQIECAAAECBAgQIEAgLyBw5FdoAAIECBAgQIAAAQIECBAgQEDgcAMECBAgQIAAAQIECBAgQIBAXkDgyK/QAAQIECBAgAABAgQIECBAgIDA4QYIECBAgAABAgQIECBAgACBvIDAkV+hAQgQIECAAAECBAgQIECAAAGBww0QIECAAAECBAgQIECAAAECeQGBI79CAxAgQIAAAQIECBAgQIAAAQIChxsgQIAAAQIECBAgQIAAAQIE8gICR36FBiBAgAABAgQIECBAgAABAgQEDjdAgAABAgQIECBAgAABAgQI5AUEjvwKDUCAAAECBAgQIECAAAECBAgIHG6AAAECBAgQIECAAAECBAgQyAsIHPkVGoAAAQIECBAgQIAAAQIECBAQONwAAQIECBAgQIAAAQIECBAgkBcQOPIrNAABAgQIECBAgAABAgQIECAgcLgBAgQIECBAgAABAgQIECBAIC8gcORXaAACBAgQIECAAAECBAgQIEBA4HADBAgQIECAAAECBAgQIECAQF5A4Miv0AAECBAgQIAAAQIECBAgQICAwOEGCBAgQIAAAQIECBAgQIAAgbyAwJFfoQEIECBAgAABAgQIECBAgAABgcMNECBAgAABAgQIECBAgAABAnkBgSO/QgMQIECAAAECBAgQIECAAAECAocbIECAAAECBAgQIECAAAECBPICAkd+hQYgQIAAAQIECBAgQIAAAQIEBA43QIAAAQIECBAgQIAAAQIECOQFBI78Cg1AgAABAgQIECBAgAABAgQICBxugAABAgQIECBAgAABAgQIEMgLCBz5FRqAAAECBAgQIECAAAECBAgQEDjcAAECBAgQIECAAAECBAgQIJAXEDjyKzQAAQIECBAgQIAAAQIECBAgIHC4AQIECBAgQIAAAQIECBAgQCAvIHDkV2gAAgQIECBAgAABAgQIECBAQOBwAwQIECBAgAABAgQIECBAgEBeQODIr9AABAgQIECAAAECBAgQIECAgMDhBggQIECAAAECBAgQIECAAIG8gMCRX6EBCBAgQIAAAQIECBAgQIAAAYHDDRAgQIAAAQIECBAgQIAAAQJ5AYEjv0IDECBAgAABAgQIECBAgAABAgKHGyBAgAABAgQIECBAgAABAgTyAgJHfoUGIECAAAECBAgQIECAAAECBAQON0CAAAECBAgQIECAAAECBAjkBQSO/AoNQIAAAQIECBAgQIAAAQIECAgcboAAAQIECBAgQIAAAQIECBDICwgc+RUagAABAgQIECBAgAABAgQIEBA43AABAgQIECBAgAABAgQIECCQFxA48is0AAECBAgQIECAAAECBAgQICBwuAECBAgQIECAAAECBAgQIEAgLyBw5FdoAAIECBAgQIAAAQIECBAgQEDgcAMECBAgQIAAAQIECBAgQIBAXkDgyK/QAAQIECBAgAABAgQIECBAgIDA4QYIECBAgAABAgQIECBAgACBvIDAkV+hAQgQIECAAAECBAgQIECAAAGBww0QIECAAAECBAgQIECAAAECeQGBI79CAxAgQIAAAQIECBAgQIAAAQIChxsgQIAAAQIECBAgQIAAAQIE8gICR36FBiBAgAABAgQIECBAgAABAgQEDjdAgAABAgQIECBAgAABAgQI5AUEjvwKDUCAAAECBAgQIECAAAECBAgIHG6AAAECBAgQIECAAAECBAgQyAsIHPkVGoAAAQIECBAgQIAAAQIECBAQONwAAQIECBAgQIAAAQIECBAgkBcQOPIrNAABAgQIECBAgAABAgQIECAgcLgBAgQIECBAgAABAgQIECBAIC8gcORXaAACBAgQIECAAAECBAgQIEBA4HADBAgQIECAAAECBAgQIECAQF5A4Miv0AAECBAgQIAAAQIECBAgQICAwOEGCBAgQIAAAQIECBAgQIAAgbyAwJFfoQEIECBAgAABAgQIECBAgAABgcMNECBAgAABAgQIECBAgAABAnkBgSO/QgMQIECAAAECBAgQIECAAAECAocbIECAAAECBAgQIECAAAECBPICAkd+hQYgQIAAAQIECBAgQIAAAQIEBA43QIAAAQIECBAgQIAAAQIECOQFBI78Cg1AgAABAgQIECBAgAABAgQICBxugAABAgQIECBAgAABAgQIEMgLCBz5FRqAAAECBAgQIECAAAECBAgQEDjcAAECBAgQIECAAAECBAgQIJAXEDjyKzQAAQIECBAgQIAAAQIECBAgIHC4AQIECBAgQIAAAQIECBAgQCAvIHDkV2gAAgQIECBAgAABAgQIECBAQOBwAwQIECBAgAABAgQIECBAgEBeQODIr9AABAgQIECAAAECBAgQIECAgMDhBggQIECAAAECBAgQIECAAIG8gMCRX6EBCBAgQIAAAQIECBAgQIAAAYHDDRAgQIAAAQIECBAgQIAAAQJ5AYEjv0IDECBAgAABAgQIECBAgAABAgKHGyBAgAABAgQIECBAgAABAgTyAgJHfoUGIECAAAECBAgQIECAAAECBAQON0CAAAECBAgQIECAAAECBAjkBQSO/AoNQIAAAQIECBAgQIAAAQIECAgcboAAAQIECBAgQIAAAQIECBDICwgc+RUagAABAgQIECBAgAABAgQIEBA43AABAgQIECBAgAABAgQIECCQFxA48is0AAECBAgQIECAAAECBAgQICBwuAECBAgQIECAAAECBAgQIEAgLyBw5FdoAAIECBAgQIAAAQIECBAgQEDgcAMECBAgQIAAAQIECBAgQIBAXkDgyK/QAAQIECBAgAABAgQIECBAgIDA4QYIECBAgAABAgQIECBAgACBvIDAkV+hAQgQIECAAAECBAgQIECAAAGBww0QIECAAAECBAgQIECAAAECeQGBI79CAxAgQIAAAQIECBAgQIAAAQIChxsgQIAAAQIECBAgQIAAAQIE8gICR36FBiBAgAABAgQIECBAgAABAgQEDjdAgAABAgQIECBAgAABAgQI5AUEjvwKDUCAAAECBAgQIECAAAECBAgIHG6AAAECBAgQIECAAAECBAgQyAsIHPkVGoAAAQIECBAgQIAAAQIECBAQONwAAQIECBAgQIAAAQIECBAgkBcQOPIrNAABAgQIECBAgAABAgQIECAgcLgBAgQIECBAgAABAgQIECBAIC8gcORXaAACBAgQIECAAAECBAgQIEBA4HADBAgQIECAAAECBAgQIECAQF5A4Miv0AAECBAgQIAAAQIECBAgQICAwOEGCBAgQIAAAQIECBAgQIAAgbyAwJFfoQEIECBAgAABAgQIECBAgAABgcMNECBAgAABAgQIECBAgAABAnkBgSO/QgMQIECAAAECBAgQIECAAAECAocbIECAAAECBAgQIECAAAECBPICAkd+hQYgQIAAAQIECBAgQIAAAQIEBA43QIAAAQIECBAgQIAAAQIECOQFBI78Cg1AgAABAgQIECBAgAABAgQICBxugAABAgQIECBAgAABAgQIEMgLCBz5FRqAAAECBAgQIECAAAECBAgQEDjcAAECBAgQIECAAAECBAgQIJAXEDjyKzQAAQIECBAgQIAAAQIECBAgIHC4AQIECBAgQIAAAQIECBAgQCAvIHDkV2gAAgQIECBAgAABAgQIECBAQOBwAwQIECBAgAABAgQIECBAgEBeQODIr9AABAgQIECAAAECBAgQIECAgMDhBggQIECAAAECBAgQIECAAIG8gMCRX6EBCBAgQIAAAQIECBAgQIAAAYHDDRAgQIAAAQIECBAgQIAAAQJ5AYEjv0IDECBAgAABAgQIECBAgAABAgKHGyBAgAABAgQIECBAgAABAgTyAgJHfoUGIECAAAECBAgQIECAAAECBAQON0CAAAECBAgQIECAAAECBAjkBQSO/AoNQIAAAQIECBAgQIAAAQIECAgcboAAAQIECBAgQIAAAQIECBDICwgc+RUagAABAgQIECBAgAABAgQIEBA43AABAgQIECBAgAABAgQIECCQFxA48is0AAECBAgQIECAAAECBAgQICBwuAECBAgQIECAAAECBAgQIEAgLyBw5FdoAAIECBAgQIAAAQIECBAgQEDgcAMECBAgQIAAAQIECBAgQIBAXkDgyK/QAAQIECBAgAABAgQIECBAgIDA4QYIECBAgAABAgQIECBAgACBvIDAkV+hAQgQIECAAAECBAgQIECAAAGBww0QIECAAAECBAgQIECAAAECeQGBI79CAxAgQIAAAQIECBAgQIAAAQIChxsgQIAAAQIECBAgQIAAAQIE8gICR36FBiBAgAABAgQIECBAgAABAgQEDjdAgAABAgQIECBAgAABAgQI5AUEjvwKDUCAAAECBAgQIECAAAECBAgIHG6AAAECBAgQIECAAAECBAgQyAsIHPkVGoAAAQIECBAgQIAAAQIECBAQONwAAQIECBAgQIAAAQIECBAgkBcQOPIrNAABAgQIECBAgAABAgQIECAgcLgBAgQIECBAgAABAgQIECBAIC8gcORXaAACBAgQIECAAAECBAgQIEBA4HADBAgQIECAAAECBAgQIECAQF5A4Miv0AAECBAgQIAAAQIECBAgQICAwOEGCBAgQIAAAQIECBAgQIAAgbyAwJFfoQEIECBAgAABAgQIECBAgAABgcMNECBAgAABAgQIECBAgAABAnkBgSO/QgMQIECAAAECBAgQIECAAAECAocbIECAAAECBAgQIECAAAECBPICAkd+hQYgQIAAAQIECBAgQIAAAQIEBA43QIAAAQIECBAgQIAAAQIECOQFBI78Cg1AgAABAgQIECBAgAABAgQICBxugAABAgQIECBAgAABAgQIEMgLCBz5FRqAAAECBAgQIECAAAECBAgQEDjcAAECBAgQIECAAAECBAgQIJAXEDjyKzQAAQIECBAgQIAAAQIECBAgIHC4AQIECBAgQIAAAQIECBAgQCAvIHDkV2gAAgQIECBAgAABAgQIECBAQOBwAwQIECBAgAABAgQIECBAgEBeQODIr9AABAgQIECAAAECBAgQIECAgMDhBggQIECAAAECBAgQIECAAIG8gMCRX6EBCBAgQIAAAQIECBAgQIAAAYHDDRAgQIAAAQIECBAgQIAAAQJ5AYEjv0IDECBAgAABAgQIECBAgAABAgKHGyBAgAABAgQIECBAgAABAgTyAgJHfoUGIECAAAECBAgQIECAAAECBAQON0CAAAECBAgQIECAAAECBAjkBQSO/AoNQIAAAQIECBAgQIAAAQIECAgcboAAAQIECBAgQIAAAQIECBDICwgc+RUagAABAgQIECBAgAABAgQIEBA43AABAgQIECBAgAABAgQIECCQFxA48is0AAECBAgQIECAAAECBAgQICBwuAECBAgQIECAAAECBAgQIEAgLyBw5FdoAAIECBAgQIAAAQIECBAgQEDgcAMECBAgQIAAAQIECBAgQIBAXkDgyK/QAAQIECBAgAABAgQIECBAgIDA4QYIECBAgAABAgQIECBAgACBvIDAkV+hAQgQIECAAAECBAgQIECAAAGBww0QIECAAAECBAgQIECAAAECeQGBI79CAxAgQIAAAQIECBAgQIAAAQIChxsgQIAAAQIECBAgQIAAAQIE8gICR36FBiBAgAABAgQIECBAgAABAgQEDjdAgAABAgQIECBAgAABAgQI5AUEjvwKDUCAAAECBAgQIECAAAECBAgIHG6AAAECBAgQIECAAAECBAgQyAsIHPkVGoAAAQIECBAgQIAAAQIECBAQONwAAQIECBAgQIAAAQIECBAgkBcQOPIrNAABAgQIECBAgAABAgQIECAgcLgBAgQIECBAgAABAgQIECBAIC8gcORXaAACBAgQIECAAAECBAgQIEBA4HADBAgQIECAAAECBAgQIECAQF5A4Miv0AAECBAgQIAAAQIECBAgQICAwOEGCBAgQIAAAQIECBAgQIAAgbyAwJFfoQEIECBAgAABAgQIECBAgAABgcMNECBAgAABAgQIECBAgAABAnkBgSO/QgMQIECAAAECBAgQIECAAAECAocbIECAAAECBAgQIECAAAECBPICAkd+hQYgQIAAAQIECBAgQIAAAQIEBA43QIAAAQIECBAgQIAAAQIECOQFBI78Cg1AgAABAgQIECBAgAABAgQICBxugAABAgQIECBAgAABAgQIEMgLCBz5FRqAAAECBAgQIECAAAECBAgQEDjcAAECBAgQIECAAAECBAgQIJAXEDjyKzQAAQIECBAgQIAAAQIECBAgIHC4AQIECBAgQIAAAQIECBAgQCAvIHDkV2gAAgQIECBAgAABAgQIECBAQOBwAwQIECBAgAABAgQIECBAgEBeQODIr9AABAgQIECAAAECBAgQIECAgMDhBggQIECAAAECBAgQIECAAIG8gMCRX6EBCBAgQIAAAQIECBAgQIAAAYHDDRAgQIAAAQIECBAgQIAAAQJ5AYEjv0IDECBAgAABAgQIECBAgAABAgKHGyBAgAABAgQIECBAgAABAgTyAgJHfoUGIECAAAECBAgQIECAAAECBAQON0CAAAECBAgQIECAAAECBAjkBQSO/AoNQIAAAQIECBAgQIAAAQIECAgcboAAAQIECBAgQIAAAQIECBDICwgc+RUagAABAgQIECBAgAABAgQIEBA43AABAgQIECBAgAABAgQIECCQFxA48is0AAECBAgQIECAAAECBAgQICBwuAECBAgQIECAAAECBAgQIEAgLyBw5FdoAAIECBAgQIAAAQIECBAgQEDgcAMECBAgQIAAAQIECBAgQIBAXkDgyK/QAAQIECBAgAABAgQIECBAgIDA4QYIECBAgAABAgQIECBAgACBvIDAkV+hAQgQIECAAAECBAgQIECAAAGBww0QIECAAAECBAgQIECAAAECeQGBI79CAxAgQIAAAQIECBAgQIAAAQIChxsgQIAAAQIECBAgQIAAAQIE8gICR36FBiBAgAABAgQIECBAgAABAgQEDjdAgAABAgQIECBAgAABAgQI5AUEjvwKDUCAAAECBAgQIECAAAECBAgIHG6AAAECBAgQIECAAAECBAgQyAsIHPkVGoAAAQIECBAgQIAAAQIECBAQONwAAQIECBAgQIAAAQIECBAgkBcQOPIrNAABAgQIECBAgAABAgQIECAgcLgBAgQIECBAgAABAgQIECBAIC8gcORXaAACBAgQIECAAAECBAgQIEBA4HADBAgQIECAAAECBAgQIECAQF5A4Miv0AAECBAgQIAAAQIECBAgQICAwOEGCBAgQIAAAQIECBAgQIAAgbyAwJFfoQEIECBAgAABAgQIECBAgAABgcMNECBAgAABAgQIECBAgAABAnkBgSO/QgMQIECAAAECBAgQIECAAAECAocbIECAAAECBAgQIECAAAECBPICAkd+hQYgQIAAAQIECBAgQIAAAQIEBA43QIAAAQIECBAgQIAAAQIECOQFBI78Cg1AgAABAgQIECBAgAABAgQICBxugAABAgQIECBAgAABAgQIEMgLCBz5FRqAAAECBAgQIECAAAECBAgQEDjcAAECBAgQIECAAAECBAgQIJAXEDjyKzQAAQIECBAgQIAAAQIECBAgIHC4AQIECBAgQIAAAQIECBAgQCAvIHDkV2gAAgQIECBAgAABAgQIECBAQOBwAwQIECBAgAABAgQIECBAgEBeQODIr9AABAgQIECAAAECBAgQIECAgMDhBggQIECAAAECBAgQIECAAIG8gMCRX6EBCBAgQIAAAQIECBAgQIAAAYHDDRAgQIAAAQIECBAgQIAAAQJ5AYEjv0IDECBAgAABAgQIECBAgAABAgKHGyBAgAABAgQIECBAgAABAgTyAgJHfoUGIECAAAECBAgQIECAAAECBAQON0CAAAECBAgQIECAAAECBAjkBQSO/AoNQIAAAQIECBAgQIAAAQIECAgcboAAAQIECBAgQIAAAQIECBDICwgc+RUagAABAgQIECBAgAABAgQIEBA43AABAgQIECBAgAABAgQIECCQFxA48is0AAECBAgQIECAAAECBAgQICBwuAECBAgQIECAAAECBAgQIEAgLyBw5FdoAAIECBAgQIAAAQIECBAgQEDgcAMECBAgQIAAAQIECBAgQIBAXkDgyK/QAAQIECBAgAABAgQIECBAgIDA4QYIECBAgAABAgQIECBAgACBvIDAkV+hAQgQIECAAAECBAgQIECAAAGBww0QIECAAAECBAgQIECAAAECeQGBI79CAxAgQIAAAQIECBAgQIAAAQIChxsgQIAAAQIECBAgQIAAAQIE8gICR36FBiBAgAABAgQIECBAgAABAgQEDjdAgAABAgQIECBAgAABAgQI5AUEjvwKDUCAAAECBAgQIECAAAECBAgIHG6AAAECBAgQIECAAAECBAgQyAsIHPkVGoAAAQIECBAgQIAAAQIECBAQONwAAQIECBAgQIAAAQIECBAgkBcQOPIrNAABAgQIECBAgAABAgQIECAgcLgBAgQIECBAgAABAgQIECBAIC8gcORXaAACBAgQIECAAAECBAgQIEBA4HADBAgQIECAAAECBAgQIECAQF5A4Miv0AAECBAgQIAAAQIECBAgQICAwOEGCBAgQIAAAQIECBAgQIAAgbyAwJFfoQEIECBAgAABAgQIECBAgAABgcMNECBAgAABAgQIECBAgAABAnkBgSO/QgMQIECAAAECBAgQIECAAAECAocbIECAAAECBAgQIECAAAECBPICAkd+hQYgQIAAAQIECBAgQIAAAQIEBA43QIAAAQIECBAgQIAAAQIECOQFBI78Cg1AgAABAgQIECBAgAABAgQICBxugAABAgQIECBAgAABAgQIEMgLCBz5FRqAAAECBAgQIECAAAECBAgQEDjcAAECBAgQIECAAAECBAgQIJAXEDjyKzQAAQIECBAgQIAAAQIECBAgIHC4AQIECBAgQIAAAQIECBAgQCAvIHDkV2gAAgQIECBAgAABAgQIECBAQOBwAwQIECBAgAABAgQIECBAgEBeQODIr9AABAgQIECAAAECBAgQIECAgMDhBggQIECAAAECBAgQIECAAIG8gMCRX6EBCBAgQIAAAQIECBAgQIAAAYHDDRAgQIAAAQIECBAgQIAAAQJ5AYEjv0IDECBAgAABAgQIECBAgAABAgKHGyBAgAABAgQIECBAgAABAgTyAgJHfoUGIECAAAECBAgQIECAAAECBAQON0CAAAECBAgQIECAAAECBAjkBQSO/AoNQIAAAQIECBAgQIAAAQIECAgcboAAAQIECBAgQIAAAQIECBDICwgc+RUagAABAgQIECBAgAABAgQIEBA43AABAgQIECBAgAABAgQIECCQFxA48is0AAECBAgQIECAAAECBAgQICBwuAECBAgQIECAAAECBAgQIEAgLyBw5FdoAAIECBAgQIAAAQIECBAgQEDgcAMECBAgQIAAAQIECBAgQIBAXkDgyK/QAAQIECBAgAABAgQIECBAgIDA4QYIECBAgAABAgQIECBAgACBvIDAkV+hAQgQIECAAAECBAgQIECAAAGBww0QIECAAAECBAgQIECAAAECeQGBI79CAxAgQIAAAQIECBAgQIAAAQIChxsgQIAAAQIECBAgQIAAAQIE8gICR36FBiBAgAABAgQIECBAgAABAgQEDjdAgAABAgQIECBAgAABAgQI5AUEjvwKDUCAAAECBAgQIECAAAECBAgIHG6AAAECBAgQIECAAAECBAgQyAsIHPkVGoAAAQIECBAgQIAAAQIECBAQONwAAQIECBAgQIAAAQIECBAgkBcQOPIrNAABAgQIECBAgAABAgQIECDwN4UTT/xKUkEiAAAAAElFTkSuQmCC","e":1},{"id":"image_3","w":1080,"h":1080,"u":"","p":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABDgAAAQ4CAYAAADsEGyPAAAgAElEQVR4Xuzd4Ynlh3nF4WtUgNVB1EFIASM2HagEpYN0kHQQd2B1kKQDwRX67FQQq4O4gGGDQIFgYpRd71j+vecR7CfN/d97nvOCxGFm9lcP/xAgQIAAAQIECBAgQIAAAQIE4gK/in9+H58AAQIECBAgQIAAAQIECBAg8DBwOAICBAgQIECAAAECBAgQIEAgL2DgyFcoAAECBAgQIECAAAECBAgQIGDgcAMECBAgQIAAAQIECBAgQIBAXsDAka9QAAIECBAgQIAAAQIECBAgQMDA4QYIECBAgAABAgQIECBAgACBvICBI1+hAAQIECBAgAABAgQIECBAgICBww0QIECAAAECBAgQIECAAAECeQEDR75CAQgQIECAAAECBAgQIECAAAEDhxsgQIAAAQIECBAgQIAAAQIE8gIGjnyFAhAgQIAAAQIECBAgQIAAAQIGDjdAgAABAgQIECBAgAABAgQI5AUMHPkKBSBAgAABAgQIECBAgAABAgQMHG6AAAECBAgQIECAAAECBAgQyAsYOPIVCkCAAAECBAgQIECAAAECBAgYONwAAQIECBAgQIAAAQIECBAgkBcwcOQrFIAAAQIECBAgQIAAAQIECBAwcLgBAgQIECBAgAABAgQIECBAIC9g4MhXKAABAgQIECBAgAABAgQIECBg4HADBAgQIECAAAECBAgQIECAQF7AwJGvUAACBAgQIECAAAECBAgQIEDAwOEGCBAgQIAAAQIECBAgQIAAgbyAgSNfoQAECBAgQIAAAQIECBAgQICAgcMNECBAgAABAgQIECBAgAABAnkBA0e+QgEIECBAgAABAgQIECBAgAABA4cbIECAAAECBAgQIECAAAECBPICBo58hQIQIECAAAECBAgQIECAAAECBg43QIAAAQIECBAgQIAAAQIECOQFDBz5CgUgQIAAAQIECBAgQIAAAQIEDBxugAABAgQIECBAgAABAgQIEMgLGDjyFQpAgAABAgQIECBAgAABAgQIGDjcAAECBAgQIECAAAECBAgQIJAXMHDkKxSAAAECBAgQIECAAAECBAgQMHC4AQIECBAgQIAAAQIECBAgQCAvYODIVygAAQIECBAgQIAAAQIECBAgYOBwAwQIECBAgAABAgQIECBAgEBewMCRr1AAAgQIECBAgAABAgQIECBAwMDhBggQIECAAAECBAgQIECAAIG8gIEjX6EABAgQIECAAAECBAgQIECAgIHDDRAgQIAAAQIECBAgQIAAAQJ5AQNHvkIBCBAgQIAAAQIECBAgQIAAAQOHGyBAgAABAgQIECBAgAABAgTyAgaOfIUCECBAgAABAgQIECBAgAABAgYON0CAAAECBAgQIECAAAECBAjkBQwc+QoFIECAAAECBAgQIECAAAECBAwcboAAAQIECBAgQIAAAQIECBDICxg48hUKQIAAAQIECBAgQIAAAQIECBg43AABAgQIECBAgAABAgQIECCQFzBw5CsUgAABAgQIECBAgAABAgQIEDBwuAECBAgQIECAAAECBAgQIEAgL2DgyFcoAAECBAgQIECAAAECBAgQIGDgcAMECBAgQIAAAQIECBAgQIBAXsDAka9QAAIECBAgQIAAAQIECBAgQMDA4QYIECBAgAABAgQIECBAgACBvICBI1+hAAQIECBAgAABAgQIECBAgICBww0QIECAAAECBAgQIECAAAECeQEDR75CAQgQIECAAAECBAgQIECAAAEDhxsgQIAAAQIECBAgQIAAAQIE8gIGjnyFAhAgQIAAAQIECBAgQIAAAQIGDjdAgAABAgQIECBAgAABAgQI5AUMHPkKBSBAgAABAgQIECBAgAABAgQMHG6AAAECBAgQIECAAAECBAgQyAsYOPIVCkCAAAECBAgQIECAAAECBAgYONwAAQIECBAgQIAAAQIECBAgkBcwcOQrFIAAAQIECBAgQIAAAQIECBAwcLgBAgQIECBAgAABAgQIECBAIC9g4MhXKAABAgQIECBAgAABAgQIECBg4HADBAgQIECAAAECBAgQIECAQF7AwJGvUAACBAgQIECAAAECBAgQIEDAwOEGCBAgQIAAAQIECBAgQIAAgbyAgSNfoQAECBAgQIAAAQIECBAgQICAgcMNECBAgAABAgQIECBAgAABAnkBA0e+QgEIECBAgAABAgQIECBAgAABA4cbIECAAAECBAgQIECAAAECBPICBo58hQIQIECAAAECBAgQIECAAAECBg43QIAAAQIECBAgQIAAAQIECOQFDBz5CgUgQIAAAQIECBAgQIAAAQIEDBxugAABAgQIECBAgAABAgQIEMgLGDjyFQpAgAABAgQIECBAgAABAgQIGDjcAAECBAgQIECAAAECBAgQIJAXMHDkKxSAAAECBAgQIECAAAECBAgQMHC4AQIECBAgQIAAAQIECBAgQCAvYODIVygAAQIECBAgQIAAAQIECBAgYOBwAwQIECBAgAABAgQIECBAgEBewMCRr1AAAgQIECBAgAABAgQIECBAwMDhBggQIECAAAECBAgQIECAAIG8gIEjX6EABAgQIECAAAECBAgQIECAgIHDDRAgQIAAAQIECBAgQIAAAQJ5AQNHvkIBCBAgQIAAAQIECBAgQIAAAQOHGyBAgAABAgQIECBAgAABAgTyAgaOfIUCECBAgAABAgQIECBAgAABAgYON0CAAAECBAgQIECAAAECBAjkBQwc+QoFIECAAAECBAgQIECAAAECBAwcboAAAQIECBAgQIAAAQIECBDICxg48hUKQIAAAQIECBAgQIAAAQIECBg43AABAgQIECBAgAABAgQIECCQFzBw5CsUgAABAgQIECBAgAABAgQIEDBwuAECBAgQIECAAAECBAgQIEAgL2DgyFcoAAECBAgQIECAAAECBAgQIGDgcAMECBAgQIAAAQIECBAgQIBAXsDAka9QAAIECBAgQIAAAQIECBAgQMDA4QYIECBAgAABAgQIECBAgACBvICBI1+hAAQIECBAgAABAgQIECBAgICBww0QIECAAAECBAgQIECAAAECeQEDR75CAQgQIECAAAECBAgQIECAAAEDhxsgQIAAAQIECBAgQIAAAQIE8gIGjnyFAhAgQIAAAQIECBAgQIAAAQIGDjdAgAABAgQIECBAgAABAgQI5AUMHPkKBSBAgAABAgQIECBAgAABAgQMHG6AAAECBAgQIECAAAECBAgQyAsYOPIVCkCAAAECBAgQIECAAAECBAgYONwAAQIECBAgQIAAAQIECBAgkBcwcOQrFIAAAQIECBAgQIAAAQIECBAwcLgBAgQIECBAgAABAgQIECBAIC9g4MhXKAABAgQIECBAgAABAgQIECBg4HADBAgQIECAAAECBAgQIECAQF7AwJGvUAACBAgQIECAAAECBAgQIEDAwOEGCBAgQIAAAQIECBAgQIAAgbyAgSNfoQAECBAgQIAAAQIECBAgQICAgcMNECBAgAABAgQIECBAgAABAnkBA0e+QgEIECBAgAABAgQIECBAgAABA4cbIECAAAECBAgQIECAAAECBPICBo58hQIQIECAAAECBAgQIECAAAECBg43QIAAAQIECBAgQIAAAQIECOQFDBz5CgUgQIAAAQIECBAgQIAAAQIEDBxugAABAgQIECBAgAABAgQIEMgLGDjyFQpAgAABAgQIECBAgAABAgQIGDjcAAECBAgQIECAAAECBAgQIJAXMHDkKxSAAAECBAgQIECAAAECBAgQMHC4AQIECBAgQIAAAQIECBAgQCAvYODIVygAAQIECBAgQIAAAQIECBAgYOBwAwQIECBAgAABAgQIECBAgEBewMCRr1AAAgQIECBAgAABAgQIECBAwMDhBggQIECAAAECBAgQIECAAIG8gIEjX6EABAgQIECAAAECBAgQIECAgIHDDRAgQIAAAQIECBAgQIAAAQJ5AQNHvkIBCBAgQIAAAQIECBAgQIAAAQOHGyBAgAABAgQIECBAgAABAgTyAgaOfIUCECBAgAABAgQIECBAgAABAgYON0CAAAECBAgQIECAAAECBAjkBQwc+QoFIECAAAECBAgQIECAAAECBAwcboAAAQIECBAgQIAAAQIECBDICxg48hUKQIAAAQIECBAgQIAAAQIECBg43AABAgQIECBAgAABAgQIECCQFzBw5CsUgAABAgQIECBAgAABAgQIEDBwuAECBAgQIECAAAECBAgQIEAgL2DgyFcoAAECBAgQIECAAAECBAgQIGDgcAMECBAgQIAAAQIECBAgQIBAXsDAka9QAAIECBAgQIAAAQIECBAgQMDA4QYIECBAgAABAgQIECBAgACBvICBI1+hAAQIECBAgAABAgQIECBAgICBww0QIECAAAECBAgQIECAAAECeQEDR75CAQgQIECAAAECBAgQIECAAAEDhxsgQIAAAQIECBAgQIAAAQIE8gIGjnyFAhAgQIAAAQIECBAgQIAAAQIGDjdAgAABAgQIECBAgAABAgQI5AUMHPkKBSBAgAABAgQIECBAgAABAgQMHG6AAAECBAgQIECAAAECBAgQyAsYOPIVCkCAAAECBAgQIECAAAECBAgYONwAAQIECBAgQIAAAQIECBAgkBcwcOQrFIAAAQIECBAgQIAAAQIECBAwcLgBAgQIECBAgAABAgQIECBAIC9g4MhXKAABAgQIECBAgAABAgQIECBg4HADBAgQIECAAAECBAgQIECAQF7AwJGvUAACBAgQIECAAAECBAgQIEDAwOEGCBAgQIAAAQIECBAgQIAAgbyAgSNfoQAECBAgQIAAAQIECBAgQICAgcMNECBAgAABAgQIECBAgAABAnkBA0e+QgEIECBAgAABAgQIECBAgAABA4cbIECAAAECBAgQIECAAAECBPICBo58hQIQIECAAAECBAgQIECAAAECBg43QIAAAQIECBAgQIAAAQIECOQFDBz5CgUgQIAAAQIECBAgQIAAAQIEDBxugAABAgQIECBAgAABAgQIEMgLGDjyFQpAgAABAgQIECBAgAABAgQIGDjcAAECBAgQIECAAAECBAgQIJAXMHDkKxSAAAECBAgQIECAAAECBAgQMHC4AQIECBAgQIAAAQIECBAgQCAvYODIVygAAQIECBAgQIAAAQIECBAgYOBwAwQIECBAgAABAgQIECBAgEBewMCRr1AAAgQIECBAgAABAgQIECBAwMDhBggQIECAAAECBAgQIECAAIG8gIEjX6EABAgQIECAAAECBAgQIECAgIHDDRAgQIAAAQIECBAgQIAAAQJ5AQNHvkIBCBAgQIAAAQIECBAgQIAAAQOHGyBAgAABAgQIECBAgAABAgTyAgaOfIUCECBAgAABAgQIECBAgAABAgYON0CAAAECBAgQIECAAAECBAjkBQwc+QoFIECAAAECBAgQIECAAAECBAwcboAAAQIECBAgQIAAAQIECBDICxg48hUKQIAAAQIECBAgQIAAAQIECBg43AABAgQIECBAgAABAgQIECCQFzBw5CsUgAABAgQIECBAgAABAgQIEDBwuAECBAgQIECAAAECBAgQIEAgL2DgyFcoAAECBAgQIECAAAECBAgQIGDgcAMECBAgQIAAAQIECBAgQIBAXsDAka9QAAIECBAgQIAAAQIECBAgQMDA4QYIECBAgAABAgQIECBAgACBvICBI1+hAAQIECBAgAABAgQIECBAgICBww0QIECAAAECBAgQIECAAAECeQEDR75CAQgQIECAAAECBAgQIECAAAEDhxsgQIAAAQIECBAgQIAAAQIE8gIGjnyFAhAgQIAAAQIECBAgQIAAAQIGDjdAgAABAgQIECBAgAABAgQI5AUMHPkKBSBAgAABAgQIECBAgAABAgQMHG6AAAECBAgQIECAAAECBAgQyAsYOPIVCkCAAAECBAgQIECAAAECBAgYONwAAQIECBAgQIAAAQIECBAgkBcwcOQrFIAAAQIECBAgQIAAAQIECBAwcLgBAgQIECBAgAABAgQIECBAIC9g4MhXKAABAgQIECBAgAABAgQIECBg4HADBAgQIECAAAECBAgQIECAQF7AwJGvUAACBAgQIECAAAECBAgQIEDAwOEGCBAgQIAAAQIECBAgQIAAgbyAgSNfoQAECBAgQIAAAQIECBAgQICAgcMNECBAgAABAgQIECBAgAABAnkBA0e+QgEIECBAgAABAgQIECBAgAABA4cbIECAAAECBAgQIECAAAECBPICBo58hQIQIECAAAECBAgQIECAAAECBg43QIAAAQIECBAgQIAAAQIECOQFDBz5CgUgQIAAAQIECBAgQIAAAQIEDBxugAABAgQIECBAgAABAgQIEMgLGDjyFQpAgAABAgQIECBAgAABAgQIGDjcAAECBAgQIECAAAECBAgQIJAXMHDkKxSAAAECBAgQIECAAAECBAgQMHC4AQIECBAgQIAAAQIECBAgQCAvYODIVygAAQIECBAgQIAAAQIECBAgYOBwAwQIECBAgAABAgQIECBAgEBewMCRr1AAAgQIECBAgAABAgQIECBAwMDhBggQIECAAAECBAgQIECAAIG8gIEjX6EABAgQIECAAAECBAgQIECAgIHDDRAgQIAAAQIECBAgQIAAAQJ5AQNHvkIBCBAgQIAAAQIECBAgQIAAAQOHGyBAgAABAgQIECBAgAABAgTyAgaOfIUCECBAgAABAgQIECBAgAABAgYON0CAAAECBAgQIECAAAECBAjkBQwc+QoFIECAAAECBAgQIECAAAECBAwcboAAAQIECBAgQIAAAQIECBDICxg48hUKQIAAAQIECBAgQIAAAQIECBg43AABAgQIECBAgAABAgQIECCQFzBw5CsUgAABAgQIECBAgAABAgQIEDBwuAECBAgQIECAAAECBAgQIEAgL2DgyFcoAAECBAgQIECAAAECBAgQIGDgcAMECBAgQIAAAQIECBAgQIBAXsDAka9QAAIECBAgQIAAAQIECBAgQMDA4QYIECBAgAABAgQIECBAgACBvICBI1+hAAQIECBAgAABAgQIECBAgICBww0QIECAAAECBAgQIECAAAECeQEDR75CAQgQIECAAAECBAgQIECAAAEDhxsgQIAAAQIECBAgQIAAAQIE8gIGjnyFAhAgQIAAAQIECBAgQIAAAQIGDjdAgAABAgQIECBAgAABAgQI5AUMHPkKBSBAgAABAgQIECBAgAABAgQMHG6AAAECBAgQIECAAAECBAgQyAsYOPIVCkCAAAECBAgQIECAAAECBAgYONwAAQIECBAgQIAAAQIECBAgkBcwcOQrFIAAAQIECBAgQIAAAQIECBAwcLgBAgQIECBAgAABAgQIECBAIC9g4MhXKAABAgQIECBAgAABAgQIECBg4HADBAgQIECAAAECBAgQIECAQF7AwJGvUAACBAgQIECAAAECBAgQIEDAwOEGCBAgQIAAAQIECBAgQIAAgbyAgSNfoQAECBAgQIAAAQIECBAgQICAgcMNECBAgAABAgQIECBAgAABAnkBA0e+QgEIECBAgAABAgQIECBAgAABA4cbIECAAAECBAgQIECAAAECBPICBo58hQIQIECAAAECBAgQIECAAAECBg43QIAAAQIECBAgQIAAAQIECOQFDBz5CgUgQIAAAQIECBAgQIAAAQIEDBxugAABAgQIECBAgAABAgQIEMgLGDjyFQpAgAABAgQIECBAgAABAgQIGDjcAAECBAgQIECAAAECBAgQIJAXMHDkKxSAAAECBAgQIECAAAECBAgQMHC4AQIECBAgQIAAAQIECBAgQCAvYODIVygAAQIECBAgQIAAAQIECBAgYOBwAwQIECBAgAABAgQIECBAgEBewMCRr1AAAgQIECBAgAABAgQIECBAwMDhBggQIECAAAECBAgQIECAAIG8gIEjX6EABAgQIECAAAECBAgQIECAgIHDDRAgQIAAAQIECBAgQIAAAQJ5AQNHvkIBCBAgQIAAAQIECBAgQIAAAQOHGyBAgAABAgQIECBAgAABAgTyAgaOfIUCECBAgAABAgQIECBAgAABAgYON0CAAAECBAgQIECAAAECBAjkBQwc+QoFIECAAAECBAgQIECAAAECBAwcboAAAQIECBAgQIAAAQIECBDICxg48hUKQIAAAQIECBAgQIAAAQIECBg43AABAgQIECBAgAABAgQIECCQFzBw5CsUgAABAgQIECBAgAABAgQIEDBwuAECBAgQIECAAAECBAgQIEAgL2DgyFcoAAECBAgQIECAAAECBAgQIGDgcAMECBAgQIAAAQIECBAgQIBAXsDAka9QAAIECBAgQIAAAQIECBAgQMDA4QYIECBAgAABAgQIECBAgACBvICBI1+hAAQIECBAgAABAgQIECBAgICBww0QIECAAAECBAgQIECAAAECeQEDR75CAQgQIECAAAECBAgQIECAAAEDhxsgQIAAAQIECBAgQIAAAQIE8gIGjnyFAhAgQIAAAQIECBAgQIAAAQIGDjdAgAABAgQIECBAgAABAgQI5AUMHPkKBSBAgAABAgQIECBAgAABAgQMHG6AAAECBAgQIECAAAECBAgQyAsYOPIVCkCAAAECBAgQIECAAAECBAgYONwAAQIECBAgQIAAAQIECBAgkBcwcOQrFIAAAQIECBAgQIAAAQIECBAwcLgBAgQIECBAgAABAgQIECBAIC9g4MhXKAABAgQIECBAgAABAgQIECBg4HADBAgQIECAAAECBAgQIECAQF7AwJGvUAACBAgQIECAAAECBAgQIEDAwOEGCBAgQIAAAQIECBAgQIAAgbyAgSNfoQAECBAgQIAAAQIECBAgQICAgcMNECBAgAABAgQIECBAgAABAnkBA0e+QgEIECBAgAABAgQIECBAgAABA4cbIECAAAECBAgQIECAAAECBPICBo58hQIQIECAAAECBAgQIECAAAECBg43QIAAAQIECBAgQIAAAQIECOQFDBz5CgUgQIAAAQIECBAgQIAAAQIEDBxugAABAgQIECBAgAABAgQIEMgLGDjyFQpAgAABAgQIECBAgAABAgQIGDjcAAECBAgQIECAAAECBAgQIJAXMHDkKxSAAAECBAgQIECAAAECBAgQMHC4AQIECBAgQIAAAQIECBAgQCAvYODIVygAAQIECBAgQIAAAQIECBAgYOBwAwQIECBAgAABAgQIECBAgEBewMCRr1AAAgQIECBAgAABAgQIECBAwMDhBggQIECAAAECBAgQIECAAIG8gIEjX6EABAgQIECAAAECBAgQIECAgIHDDRAgQIAAAQIECBAgQIAAAQJ5AQNHvkIBCBAgQIAAAQIECBAgQIAAAQOHGyBAgAABAgQIECBAgAABAgTyAgaOfIUCECBAgAABAgQIECBAgAABAgYON0CAAAECBAgQIECAAAECBAjkBQwc+QoFIECAAAECBAgQIECAAAECBAwcboAAAQIECBAgQIAAAQIECBDICxg48hUKQIAAAQIECBAgQIAAAQIECBg43AABAgQIECBAgAABAgQIECCQFzBw5CsUgAABAgQIECBAgAABAgQIEDBwuAECBAgQIECAAAECBAgQIEAgL2DgyFcoAAECBAgQIECAAAECBAgQIGDgcAMECBAgQIAAAQIECBAgQIBAXsDAka9QAAIECBAgQIAAAQIECBAgQMDA4QYIECBAgAABAgQIECBAgACBvICBI1+hAAQIECBAgAABAgQIECBAgICBww0QIECAAAECBAgQIECAAAECeQEDR75CAQgQIECAAAECBAgQIECAAAEDhxsgQIAAAQIECBAgQIAAAQIE8gIGjnyFAhAgQIAAAQIECBAgQIAAAQIGDjdAgAABAgQIECBAgAABAgQI5AUMHPkKBSBAgAABAgQIECBAgAABAgQMHG6AAAECBAgQIECAAAECBAgQyAsYOPIVCkCAAAECBAgQIECAAAECBAgYONwAAQIECBAgQIAAAQIECBAgkBcwcOQrFIAAAQIECBAgQIAAAQIECBAwcLgBAgQIECBAgAABAgQIECBAIC9g4MhXKAABAgQIECBAgAABAgQIECBg4HADBAgQIECAAAECBAgQIECAQF7AwJGvUAACBAgQIECAAAECBAgQIEDAwOEGCBAgQIAAAQIECBAgQIAAgbyAgSNfoQAECBAgQIAAAQIECBAgQICAgcMNECBAgAABAgQIECBAgAABAnkBA0e+QgEIECBAgAABAgQIECBAgAABA4cbIECAAAECBAgQIECAAAECBPICBo58hQIQIECAAAECBAgQIECAAAECBg43QIAAAQIECBAgQIAAAQIECOQFDBz5CgUgQIAAAQIECBAgQIAAAQIEDBxugAABAgQIECBAgAABAgQIEMgLGDjyFQpAgAABAgQIECBAgAABAgQIGDjcAAECBAgQIECAAAECBAgQIJAXMHDkKxSAAAECBAgQIECAAAECBAgQMHC4AQIECBAgQIAAAQIECBAgQCAvYODIVygAAQIECBAgQIAAAQIECBAgYOBwAwQIECBAgAABAgQIECBAgEBewMCRr1AAAgQIECBAgAABAgQIECBAwMDhBggQIECAAAECBAgQIECAAIG8gIEjX6EABAgQIECAAAECBAgQIECAgIHDDRAgQIAAAQIECBAgQIAAAQJ5AQNHvkIBCBAgQIAAAQIECBAgQIAAAQOHGyBAgAABAgQIECBAgAABAgTyAgaOfIUCECBAgAABAgQIECBAgAABAgYON0CAAAECBAgQIECAAAECBAjkBQwc+QoFIECAAAECBAgQIECAAAECBAwcboAAAQIECBAgQIAAAQIECBDICxg48hUKQIAAAQIECBAgQIAAAQIECBg43AABAgQIECBAgAABAgQIECCQFzBw5CsUgElK5UcAACAASURBVAABAgQIECBAgAABAgQIEDBwuAECBAgQIECAAAECBAgQIEAgL2DgyFcoAAECBAgQIECAAAECBAgQIGDgcAMECBAgQIAAAQIECBAgQIBAXsDAka9QAAIECBAgQIAAAQIECBAgQMDA4QYIECBAgAABAgQIECBAgACBvICBI1+hAAQIECBAgAABAgQIECBAgICBww0QIECAAAECBAgQIECAAAECeQEDR75CAQgQIECAAAECBAgQIECAAAEDhxsgQIAAAQIECBAgQIAAAQIE8gIGjnyFAhAgQIAAAQIECBAgQIAAAQIGDjdAgAABAgQIECBAgAABAgQI5AUMHPkKBSBAgAABAgQIECBAgAABAgQMHG6AAAECBAgQIECAAAECBAgQyAsYOPIVCkCAAAECBAgQIECAAAECBAgYONwAAQIECBAgQIAAAQIECBAgkBcwcOQrFIAAAQIECBAgQIAAAQIECBAwcLgBAgQIECBAgAABAgQIECBAIC9g4MhXKAABAgQIECBAgAABAgQIECBg4HADBAgQIECAAAECBAgQIECAQF7AwJGvUAACBAgQIECAAAECBAgQIEDAwOEGCBAgQIAAAQIECBAgQIAAgbyAgSNfoQAECBAgQIAAAQIECBAgQICAgcMNECBAgAABAgQIECBAgAABAnkBA0e+QgEIECBAgAABAgQIECBAgAABA4cbIECAAAECBAgQIECAAAECBPICBo58hQIQIECAAAECBAgQIECAAAECBg43QIAAAQIECBAgQIAAAQIECOQFDBz5CgUgQIAAAQIECBAgQIAAAQIEDBxugAABAgQIECBAgAABAgQIEMgLGDjyFQpAgAABAgQIECBAgAABAgQIGDjcAAECBAgQIECAAAECBAgQIJAXMHDkKxSAAAECBAgQIECAAAECBAgQMHC4AQIECBAgQIAAAQIECBAgQCAvYODIVygAAQIECBAgQIAAAQIECBAgYOBwAwQIECBAgAABAgQIECBAgEBewMCRr1AAAgQIECBAgAABAgQIECBAwMDhBggQIECAAAECBAgQIECAAIG8gIEjX6EABAgQIECAAAECBAgQIECAgIHDDRAgQIAAAQIECBAgQIAAAQJ5AQNHvkIBCBAgQIAAAQIECBAgQIAAAQOHGyBAgAABAgQIECBAgAABAgTyAgaOfIUCECBAgAABAgQIECBAgAABAgYON0CAAAECBAgQIECAAAECBAjkBQwc+QoFIECAAAECBAgQIECAAAECBAwcboAAAQIECBAgQIAAAQIECBDICxg48hUKQIAAAQIECBAgQIAAAQIECBg43AABAgQIECBAgAABAgQIECCQFzBw5CsUgAABAgQIECBAgAABAgQIEDBwuAECBAgQIECAAAECBAgQIEAgL2DgyFcoAAECBAgQIECAAAECBAgQIGDgcAMECBAgQIAAAQIECBAgQIBAXsDAka9QAAIECBAgQIAAAQIECBAgQMDA4QYIECBAgAABAgQIECBAgACBvICBI1+hAAQIECBAgAABAgQIECBAgICBww0QIECAAAECBAgQIECAAAECeQEDR75CAQgQIECAAAECBAgQIECAAAEDhxsgQIAAAQIECBAgQIAAAQIE8gIGjnyFAhAgQIAAAQIECBAgQIAAAQIGDjdAgAABAgQIECBAgAABAgQI5AUMHPkKBSBAgAABAgQIECBAgAABAgQMHG6AAAECBAgQIECAAAECBAgQyAsYOPIVCkCAAAECBAgQIECAAAECBAgYONwAAQIECBAgQIAAAQIECBAgkBcwcOQrFIAAAQIECBAgQIAAAQIECBAwcLgBAgQIECBAgAABAgQIECBAIC9g4MhXKAABAgQIECBAgAABAgQIECBg4HADBAgQIECAAAECBAgQIECAQF7AwJGvUAACBAgQIECAAAECBAgQIEDAwOEGCBAgQIAAAQIECBAgQIAAgbyAgSNfoQAECBAgQIAAAQIECBAgQICAgcMNECBAgAABAgQIECBAgAABAnkBA0e+QgEIECBAgAABAgQIECBAgAABA4cbIECAAAECBAgQIECAAAECBPICBo58hQIQIECAAAECBAgQIECAAAECBg43QIAAAQIECBAgQIAAAQIECOQFDBz5CgUgQIAAAQIECBAgQIAAAQIEDBxugAABAgQIECBAgAABAgQIEMgLGDjyFQpAgAABAgQIECBAgAABAgQIGDjcAAECBAgQIECAAAECBAgQIJAXMHDkKxSAAAECBAgQIECAAAECBAgQMHC4AQIECBAgQIAAAQIECBAgQCAvYODIVygAAQIECBAgQIAAAQIECBAgYOBwAwQIECBAgAABAgQIECBAgEBewMCRr1AAAgQIECBAgAABAgQIECBAwMDhBggQIECAAAECBAgQIECAAIG8gIEjX6EABAgQIECAAAECBAgQIECAgIHDDRAgQIAAAQIECBAgQIAAAQJ5AQNHvkIBCBAgQIAAAQIECBAgQIAAAQOHGyBAgAABAgQIECBAgAABAgTyAgaOfIUCECBAgAABAgQIECBAgAABAgYON0CAAAECBAgQIECAAAECBAjkBQwc+QoFIECAAAECBAgQIECAAAECBAwcboAAAQIECBAgQIAAAQIECBDICxg48hUKQIAAAQIECBAgQIAAAQIECBg43AABAgQIECBAgAABAgQIECCQFzBw5CsUgAABAgQIECBAgAABAgQIEDBwuAECBAgQIECAAAECBAgQIEAgL2DgyFcoAAECBAgQIECAAAECBAgQIGDgcAMECBAgQIAAAQIECBAgQIBAXsDAka9QAAIECBAgQIAAAQIECBAgQMDA4QYIECBAgAABAgQIECBAgACBvICBI1+hAAQIECBAgAABAgQIECBAgICBww0QIECAAAECBAgQIECAAAECeQEDR75CAQgQIECAAAECBAgQIECAAAEDhxsgQIAAAQIECBAgQIAAAQIE8gIGjnyFAhAgQIAAAQIECBAgQIAAAQIGDjdAgAABAgQIECBAgAABAgQI5AUMHPkKBSBAgAABAgQIECBAgAABAgQMHG6AAAECBAgQIECAAAECBAgQyAsYOPIVCkCAAAECBAgQIECAAAECBAgYONwAAQIECBAgQIAAAQIECBAgkBcwcOQrFIAAAQIECBAgQIAAAQIECBAwcLgBAgQIECBAgAABAgQIECBAIC9g4MhXKAABAgQIECBAgAABAgQIECBg4HADBAgQIECAAAECBAgQIECAQF7AwJGvUAACBAgQIECAAAECBAgQIEDAwOEGCBAgQIAAAQIECBAgQIAAgbyAgSNfoQAECBAgQIAAAQIECBAgQICAgcMNECBAgAABAgQIECBAgAABAnkBA0e+QgEIECBAgAABAgQIECBAgAABA4cbIECAAAECBAgQIECAAAECBPICBo58hQIQIECAAAECBAgQIECAAAECBg43QIAAAQIECBAgQIAAAQIECOQFDBz5CgUgQIAAAQIECBAgQIAAAQIEDBxugAABAgQIECBAgAABAgQIEMgLGDjyFQpAgAABAgQIECBAgAABAgQIGDjcAAECBAgQIECAAAECBAgQIJAXMHDkKxSAAAECBAgQIECAAAECBAgQMHC4AQIECBAgQIAAAQIECBAgQCAvYODIVygAAQIECBAgQIAAAQIECBAgYOBwAwQIECBAgAABAgQIECBAgEBewMCRr1AAAgQIECBAgAABAgQIECBAwMDhBggQIECAAAECBAgQIECAAIG8gIEjX6EABAgQIECAAAECBAgQIECAgIHDDRAgQIAAAQIECBAgQIAAAQJ5AQNHvkIBCBAgQIAAAQIECBAgQIAAAQOHGyBAgAABAgQIECBAgAABAgTyAgaOfIUCECBAgAABAgQIECBAgAABAgYON0CAAAECBAgQIECAAAECBAjkBQwc+QoFIECAAAECBAgQIECAAAECBAwcboAAAQIECBAgQIAAAQIECBDICxg48hUKQIAAAQIECBAgQIAAAQIECBg43AABAgQIECBAgAABAgQIECCQFzBw5CsUgAABAgQIECBAgAABAgQIEDBwuAECBAgQIECAAAECBAgQIEAgL2DgyFcoAAECBAgQIECAAAECBAgQIGDgcAMECBAgQIAAAQIECBAgQIBAXsDAka9QAAIECBAgQIAAAQIECBAgQMDA4QYIECBAgAABAgQIECBAgACBvICBI1+hAAQIECBAgAABAgQIECBAgICBww0QIECAAAECBAgQIECAAAECeQEDR75CAQgQIECAAAECBAgQIECAAAEDhxsgQIAAAQIECBAgQIAAAQIE8gIGjnyFAhAgQIAAAQIECBAgQIAAAQIGDjdAgAABAgQIECBAgAABAgQI5AUMHPkKBSBAgAABAgQIECBAgAABAgQMHG6AAAECBAgQIECAAAECBAgQyAsYOPIVCkCAAAECBAgQIECAAAECBAgYONwAAQIECBAgQIAAAQIECBAgkBcwcOQrFIAAAQIECBAgQIAAAQIECBAwcLgBAgQIECBAgAABAgQIECBAIC9g4MhXKAABAgQIECBAgAABAgQIECBg4HADBAgQIECAAAECBAgQIECAQF7AwJGvUAACBAgQIECAAAECBAgQIEDAwOEGCBAgQIAAAQIECBAgQIAAgbyAgSNfoQAECBAgQIAAAQIECBAgQICAgcMNECBAgAABAgQIECBAgAABAnkBA0e+QgEIECBAgAABAgQIECBAgAABA4cbIECAAAECBAgQIECAAAECBPICBo58hQIQIECAAAECBAgQIECAAAECBg43QIAAAQIECBAgQIAAAQIECOQFDBz5CgUgQIAAAQIECBAgQIAAAQIEDBxugAABAgQIECBAgAABAgQIEMgLGDjyFQpAgAABAgQIECBAgAABAgQIGDjcAAECBAgQIECAAAECBAgQIJAXMHDkKxSAAAECBAgQIECAAAECBAgQMHC4AQIECBAgQIAAAQIECBAgQCAvYODIVygAAQIECBAgQIAAAQIECBAgYOBwAwQIECBAgAABAgQIECBAgEBewMCRr1AAAgQIECBAgAABAgQIECBAwMDhBggQIECAAAECBAgQIECAAIG8gIEjX6EABAgQIECAAAECBAgQIECAgIHDDRAgQIAAAQIECBAgQIAAAQJ5AQNHvkIBCBAgQIAAAQIECBAgQIAAAQOHGyBAgAABAgQIECBAgAABAgTyAgaOfIUCECBAgAABAgQIECBAgAABAgYON0CAAAECBAgQIECAAAECBAjkBQwc+QoFIECAAAECBAgQIECAAAECBAwcboAAAQIECBAgQIAAAQIECBDICxg48hUKQIAAAQIECBAgQIAAAQIECBg43AABAgQIECBAgAABAgQIECCQFzBw5CsUgAABAgQIECBAgAABAgQIEDBwuAECBAgQIECAAAECBAgQIEAgL2DgyFcoAAECBAgQIECAAAECBAgQIGDgcAMECBAgQIAAAQIECBAgQIBAXsDAka9QAAIECBAgQIAAAQIECBAgQMDA4QYIECBAgAABAgQIECBAgACBvICBI1+hAAQIECBAgAABAgQIECBAgICBww0QIECAAAECBAgQIECAAAECeQEDR75CAQgQIECAAAECBAgQIECAAAEDhxsgQIAAAQIECBAgQIAAAQIE8gIGjnyFAhAgQIAAAQIECBAgQIAAAQIGDjdAgAABAgQIECBAgAABAgQI5AUMHPkKBSBAgAABAgQIECBAgAABAgQMHG6AAAECBAgQIECAAAECBAgQyAsYOPIVCkCAAAECBAgQIECAAAECBAgYONwAAQIECBAgQIAAAQIECBAgkBcwcOQrFIAAAQIECBAgQIAAAQIECBAwcLgBAgQIECBAgAABAgQIECBAIC9g4MhXKAABAgQIECBAgAABAgQIECBg4HADBAgQIECAAAECBAgQIECAQF7AwJGvUAACBAgQIECAAAECBAgQIEDAwOEGCBAgQIAAAQIECBAgQIAAgbyAgSNfoQAECBAgQIAAAQIECBAgQICAgcMNECBAgAABAgQIECBAgAABAnkBA0e+QgEIECBAgAABAgQIECBAgAABA4cbIECAAAECBAgQIECAAAECBPICBo58hQIQIECAAAECBAgQIECAAAECBg43QIAAAQIECBAgQIAAAQIECOQFDBz5CgUgQIAAAQIECBAgQIAAAQIEDBxugAABAgQIECBAgAABAgQIEMgLGDjyFQpAgAABAgQIECBAgAABAgQIGDjcAAECBAgQIECAAAECBAgQIJAXMHDkKxSAAAECBAgQIECAAAECBAgQMHC4AQIECBAgQIAAAQIECBAgQCAvYODIVygAAQIECBAgQIAAAQIECBAgYOBwAwQIECBAgAABAgQIECBAgEBewMCRr1AAAgQIECBAgAABAgQIECBAwMDhBggQIECAAAECBAgQIECAAIG8gIEjX6EABAgQIECAAAECBAgQIECAgIHDDRAgQIAAAQIECBAgQIAAAQJ5AQNHvkIBCBAgQIAAAQIECBAgQIAAAQOHGyBAgAABAgQIECBAgAABAgTyAgaOfIUCECBAgAABAgQIECBAgAABAgYON0CAAAECBAgQIECAAAECBAjkBQwc+QoFIECAAAECBAgQIECAAAECBAwcboAAAQIECBAgQIAAAQIECBDICxg48hUKQIAAAQIECBAgQIAAAQIECBg43AABAgQIECBAgAABAgQIECCQFzBw5CsUgAABAgQIECBAgAABAgQIEDBwuAECBAgQIECAAAECBAgQIEAgL2DgyFcoAAECBAgQIECAAAECBAgQIGDgcAMECBAgQIAAAQIECBAgQIBAXsDAka9QAAIECBAgQIAAAQIECBAgQMDA4QYIECBAgAABAgQIECBAgACBvICBI1+hAAQIECBAgAABAgQIECBAgICBww0QIECAAAECBAgQIECAAAECeQEDR75CAQgQIECAAAECBAgQIECAAAEDhxsgQIAAAQIECBAgQIAAAQIE8gIGjnyFAhAgQIAAAQIECBAgQIAAAQIGDjdAgAABAgQIECBAgAABAgQI5AUMHPkKBSBAgAABAgQIECBAgAABAgQMHG6AAAECBAgQIECAAAECBAgQyAsYOPIVCkCAAAECBAgQIECAAAECBAgYONwAAQIECBAgQIAAAQIECBAgkBcwcOQrFIAAAQIECBAgQIAAAQIECBAwcLgBAgQIECBAgAABAgQIECBAIC9g4MhXKAABAgQIECBAgAABAgQIECBg4HADBAgQIECAAAECBAgQIECAQF7AwJGvUAACBAgQIECAAAECBAgQIEDAwOEGCBAgQIAAAQIECBAgQIAAgbyAgSNfoQAECBAgQIAAAQIECBAgQICAgcMNECBAgAABAgQIECBAgAABAnkBA0e+QgEIECBAgAABAgQIECBAgAABA4cbIECAAAECBAgQIECAAAECBPICBo58hQIQIECAAAECBAgQIECAAAECBg43QIAAAQIECBAgQIAAAQIECOQFDBz5CgUgQIAAAQIECBAgQIAAAQIEDBxugAABAgQIECBAgAABAgQIEMgLGDjyFQpAgAABAgQIECBAgAABAgQIGDjcAAECBAgQIECAAAECBAgQIJAXMHDkKxSAAAECBAgQIECAAAECBAgQMHC4AQIECBAgQIAAAQIECBAgQCAvYODIVygAAQIECBAgQIAAAQIECBAgYOBwAwQIECBAgAABAgQIECBAgEBewMCRr1AAAgQIECBAgAABAgQIECBAwMDhBggQIECAAAECBAgQIECAAIG8gIEjX6EABAgQIECAAAECBAgQIECAgIHDDRAgQIAAAQIECBAgQIAAAQJ5AQNHvkIBCBAgQIAAAQIECBAgQIAAAQOHGyBAgAABAgQIvKHAu3fvPn99ff368Xj8+Odv/+it/uPxeHzz2WefffPtt9/+1xt+DI8mQIAAAQLnBQwc5ysWkAABAgQIEPilBF5eXn4cNX7zeDx+/TOf4Yf3799//d133337S31W70uAAAECBOoCBo56gz4/AQIECBAg8Fcp8OWXX/7z+/fv/+kDP9w/PJ/Pbz7wNb6cAAECBAgQeDweBg5nQIAAAQIECBD4xAI/fefGbz/mse/fv/9738nxMXJeQ4AAAQLrAgaO9QuQnwABAgQIEPikAj/9zo3f/z9+LOVPve8Pz+fzi0/6oTyMAAEC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gAABAuMCBo7xAxCfAAECBAgQIECAAAECBAhcEDBwXGhRBgIECBAgQIAAAQIECBAgMC5g4Bg/APEJECBAgAABAgQIECBAgMAFAQPHhRZlIECAAAECBAgQIECAAAEC4wIGjvEDEJ8AAQIECBAgQIAAAQIECFwQMHBcaFEGAgQIECBAgAABAgQIECAwLmDgGD8A8QkQIECAAAECBAgQIECAwAUBA8eFFmUgQIAAAQIECPx3O3ZQAAAAQECsf2s93CIYLwQIECBAgEBcwMERH4D4BAgQIECAAAECBAgQIEDgQcDB8dCiDAQIECBAgAABAgQIECBAIC7g4IgPQHwCBAgQIECAAAECBAgQIPAg4OB4aFEGAgQIECBAgAABAgQIECAQF3BwxAcgPgECBAgQIECAAAECBAgQeBBwcDy0KAMBAgQIECBAgAABAgQIEIgLODjiAxCfAAECBAgQIECAAAECBAg8CDg4HlqUgQABAgQIECBAgAABAgQIxAUcHPEBiE+AAAECBAgQIECAAAECBB4EHBwPLcpAgAABAgQIECBAgAABAgTiAg6O+ADEJ0CAAAECBAgQIECAAAECDwIOjocWZSBAgAABAgQIECBAgAABAnEBB0d8AOITIECAAAECBAgQIECAAIEHAQfHQ4syECBAgAABAgQIECBAgACBuICDIz4A8QkQIECAAAECBAgQIECAwIOAg+OhRRkIECBAgAABAgQIECBAgEBcwMERH4D4BAgQIECAAAECBAgQIEDgQcDB8dCiDAQIECBAgAABAgQIECBAIC7g4IgPQHwCBAgQIECAAAECBAgQIPAg4OB4aFEGAgQIECBAgAABAgQIECAQF3BwxAcgPgECBAgQIECAAAECBAgQeBBwcDy0KAMBAgQIECBAgAABAgQIEIgLODjiAxCfAAECBAgQIECAAAECBAg8CDg4HlqUgQABAgQIECBAgAABAgQIxAUcHPEBiE+AAAECBAgQIECAAAECBB4EHBwPLcpAgAABAgQIECBAgAABAgTiAg6O+ADEJ0CAAAECBAgQIECAAAECDwIOjocWZSBAgAABAgQIECBAgAABAnEBB0d8AOITIECAAAECBAgQIECAAIEHAQfHQ4syECBAgAABAgQIECBAgACBuICDIz4A8QkQIECAAAECBAgQIECAwIOAg+OhRRkIECBAgAABAgQIECBAgEBcwMERH4D4BAgQIECAAAECBAgQIEDgQcDB8dCiDAQIECBAgAABAgQIECBAIC7g4IgPQHwCBAgQIECAAAECBAgQIPAg4OB4aFEGAgQIECBAgAABAgQIECAQF3BwxAcgPgECBAgQIECAAAECBAgQeBBwcDy0KAMBAgQIECBAgAABAgQIEIgLODjiAxCfAAECBAgQIECAAAECBAg8CDg4HlqUgQABAgQIECBAgAABAgQIxAUcHPEBiE+AAAECBAgQIECAAAECBB4EHBwPLcpAgAABAgQIECBAgAABAgTiAg6O+ADEJ0CAAAECBAgQIECAAAECDwIOjocWZSBAgAABAgQIECBAgAABAnEBB0d8AOITIECAAAECBAgQIECAAIEHAQfHQ4syECBAgAABAgQIECBAgACBuICDIz4A8QkQIECAAAECBAgQIECAwIOAg+OhRRkIECBAgAABAgQIECBAgEBcwMERH4D4BAgQIECAAAECBAgQIEDgQcDB8dCiDAQIECBAgAABAgQIECBAIC7g4IgPQHwCBAgQIECAAAECBAgQIPAg4OB4aFEGAgQIECBAgAABAgQIECAQF3BwxAcgPgECBAgQIECAAAECBAgQeBBwcDy0KAMBAgQIECBAgAABAgQIEIgLODjiAxCfAAECBAgQIECAAAECBAg8CDg4HlqUgQABAgQIECBAgAABAgQIxAUcHPEBiE+AAAECBAgQIECAAAECBB4EHBwPLcpAgAABAgQIECBAgAABAgTiAg6O+ADEJ0CAAAECBAgQIECAAAECDwIOjocWZSBAgAABAgQIECBAgAABAnEBB0d8AOITIECAAAECBAgQIECAAIEHAQfHQ4syECBAgAABAgQIECBAgACBuICDIz4A8QkQIECAAAECBAgQIECAwIOAg+OhRRkIECBAgAABAgQIECBAgEBcwMERH4D4BAgQIECAAAECBAgQIEDgQcDB8dCiDAQIECBAgAABAgQIECBAIC7g4IgPQHwCBAgQIECAAAECBAgQIPAg4OB4aFEGAgQIECBAgAABAgQIECAQF3BwxAcgPgECBAgQIECAAAECBAgQeBBwcDy0KAMBAgQIECBAgAABAgQIEIgLODjiAxCfAAECBAgQIECAAAECBAg8CDg4HlqUgQABAgQIECBAgAABAgQIxAUcHPEBiE+AAAECBAgQIECAAAECBB4EHBwPLcpAgAABAgQIECBAgAABAgTiAg6O+ADEJ0CAAAECBAgQIECAAAECDwIOjocWZSBAgAABAgQIECBAgAABAnEBB0d8AOITIECAAAECBAgQIECAAIEHAQfHQ4syECBAgAABAgQIECBAgACBuICDIz4A8QkQIECAAAECBAgQIECAwIOAg+OhRRkIECBAgAABAgQIECBAgEBcwMERH4D4BAgQIECAAAECBAgQIEDgQcDB8dCiDAQIECBAgAABAgQIECBAIC7g4IgPQHwCBAgQIECAAAECBAgQIPAg4OB4aFEGAgQIECBAgAABAgQIECAQF3BwxAcgPgECBAgQIECAAAECBAgQeBBwcDy0KAMBAgQIECBAgAABAgQIEIgLODjiAxCfAAECBAgQIECAAAECBAg8CDg4HlqUgQABAgQIECBAgAABAgQIxAUcHPEBiE+AAAECBAgQIECAAAECBB4EHBwPLcpAgAABAgQIECBAgAABAgTiAg6O+ADEJ0CAAAECBAgQIECAAAECDwIOjocWZSBAgAABAgQIECBAgAABAnEBB0d8AOITIECAAAECBAgQIECAAIEHAQfHQ4syECBAgAABAgQIECBAgACBuICDIz4A8QkQIECAAAECBAgQIECAwIOAg+OhRRkIECBAgAABAgQIECBAgEBcwMERH4D4BAgQIECAAAECBAgQIEDgQcDB8dCiDAQIECBAgAABAgQIECBAIC7g4IgPQHwCBAgQIECAAAECBAgQIPAg4OB4aFEGAgQIECBAgAABAgQIECAQF3BwxAcgPgECBAgQIECAAAECBAgQeBBwcDy0KAMBAgQIECBAgAABAgQIEIgLODjiAxCfAAECBAgQIECAAAECBAg8CDg4HlqUgQABAgQIECBAgAABAgQIxAUcHPEBiE+AAAECBAgQIECAAAECBB4EHBwPLcpAgAABAgQIECBAgAABAgTiAg6O+ADEJ0CAAAECBAgQIECAAAECDwIOjocWZSBAgAABAgQIECBAgAABAnEBB0d8AOITIECAAAECBAgQIECAAIEHAQfHQ4syECBAgAABAgQIECBAgACBuICDIz4A8QkQIECAAAECBAgQIECAwIOAg+OhRRkIECBAgAABAgQIECBAgEBcwMERH4D4BAgQIECAAAECBAgQIEDgQcDB8dCiDAQIECBAgAABAgQIECBAIC7g4IgPQHwCBAgQIECAAAECBAgQIPAg4OB4aFEGAgQIECBAgAABAgQIECAQF3BwxAcgPgECBAgQIECAAAECBAgQeBBwcDy0KAMBAgQIECBAgAABAgQIEIgLODjiAxCfAAECBAgQIECAAAECBAg8CDg4HlqUgQABAgQIECBAgAABAgQIxAUcHPEBiE+AAAECBAgQIECAAAECBB4EHBwPLcpAgAABAgQIECBAgAABAgTiAg6O+ADEJ0CAAAECBAgQIECAAAECDwIOjocWZSBAgAABAgQIECBAgAABAnEBB0d8AOITIECAAAECBAgQIECAAIEHAQfHQ4syECBAgAABAgQIECBAgACBuICDIz4A8QkQIECAAAECBAgQIECAwIOAg+OhRRkIECBAgAABAgQIECBAgEBcwMERH4D4BAgQIECAAAECBAgQIEDgQWAbGIM51m4xaQAAAABJRU5ErkJggg==","e":1},{"id":"image_4","w":1080,"h":1080,"u":"","p":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABDgAAAQ4CAYAAADsEGyPAAAgAElEQVR4Xuzd0Ymu53WG4V9MAU4HTgfRsdCAOojdgV1COkkHUQnqIBt+nQd3YHdgFSAUNvZB8InJ7HkWc6NL4MN5v1nXWmBzs7f81cM/BAgQIECAAAECBAgQIECAAIG4wFfx39+vT4AAAQIECBAgQIAAAQIECBB4CByOgAABAgQIECBAgAABAgQIEMgLCBz5FRqAAAECBAgQIECAAAECBAgQEDjcAAECBAgQIECAAAECBAgQIJAXEDjyKzQAAQIECBAgQIAAAQIECBAgIHC4AQIECBAgQIAAAQIECBAgQCAvIHDkV2gAAgQIECBAgAABAgQIECBAQOBwAwQIECBAgAABAgQIECBAgEBeQODIr9AABAgQIECAAAECBAgQIECAgMDhBggQIECAAAECBAgQIECAAIG8gMCRX6EBCBAgQIAAAQIECBAgQIAAAYHDDRAgQIAAAQIECBAgQIAAAQJ5AYEjv0IDECBAgAABAgQIECBAgAABAgKHGyBAgAABAgQIECBAgAABAgTyAgJHfoUGIECAAAECBAgQIECAAAECBAQON0CAAAECBAgQIECAAAECBAjkBQSO/AoNQIAAAQIECBAgQIAAAQIECAgcboAAAQIECBAgQIAAAQIECBDICwgc+RUagAABAgQIECBAgAABAgQIEBA43AABAgQIECBAgAABAgQIECCQFxA48is0AAECBAgQIECAAAECBAgQICBwuAECBAgQIECAAAECBAgQIEAgLyBw5FdoAAIECBAgQIAAAQIECBAgQEDgcAMECBAgQIAAAQIECBAgQIBAXkDgyK/QAAQIECBAgAABAgQIECBAgIDA4QYIECBAgAABAgQIECBAgACBvIDAkV+hAQgQIECAAAECBAgQIECAAAGBww0QIECAAAECBAgQIECAAAECeQGBI79CAxAgQIAAAQIECBAgQIAAAQIChxsgQIAAAQIECBAgQIAAAQIE8gICR36FBiBAgAABAgQIECBAgAABAgQEDjdAgAABAgQIECBAgAABAgQI5AUEjvwKDUCAAAECBAgQIECAAAECBAgIHG6AAAECBAgQIECAAAECBAgQyAsIHPkVGoAAAQIECBAgQIAAAQIECBAQONwAAQIECBAgQIAAAQIECBAgkBcQOPIrNAABAgQIECBAgAABAgQIECAgcLgBAgQIECBAgAABAgQIECBAIC8gcORXaAACBAgQIECAAAECBAgQIEBA4HADBAgQIECAAAECBAgQIECAQF5A4Miv0AAECBAgQIAAAQIECBAgQICAwOEGCBAgQIAAAQIECBAgQIAAgbyAwJFfoQEIECBAgAABAgQIECBAgAABgcMNECBAgAABAgQIECBAgAABAnkBgSO/QgMQIECAAAECBAgQIECAAAECAocbIECAAAECBAgQIECAAAECBPICAkd+hQYgQIAAAQIECBAgQIAAAQIEBA43QIAAAQIECBAgQIAAAQIECOQFBI78Cg1AgAABAgQIECBAgAABAgQICBxugAABAgQIECBAgAABAgQIEMgLCBz5FRqAAAECBAgQIECAAAECBAgQEDjcAAECBAgQIECAAAECBAgQIJAXEDjyKzQAAQIECBAgQIAAAQIECBAgIHC4AQIECBAgQIAAAQIECBAgQCAvIHDkV2gAAgQIECBAgAABAgQIECBAQOBwAwQIECBAgAABAgQIECBAgEBeQODIr9AABAgQIECAAAECBAgQIECAgMDhBggQIECAAAECBAgQIECAAIG8gMCRX6EBCBAgQIAAAQIECBAgQIAAAYHDDRAgQIAAAQIECBAgQIAAAQJ5AYEjv0IDECBAgAABAgQIECBAgAABAgKHGyBAgAABAgQIECBAgAABAgTyAgJHfoUGIECAAAECBAgQIECAAAECBAQON0CAAAECBAgQIECAAAECBAjkBQSO/AoNQIAAAQIECBAgQIAAAQIECAgcboAAAQIECBAgQIAAAQIECBDICwgc+RUagAABAgQIECBAgAABAgQIEBA43AABAgQIECBAgAABAgQIECCQFxA48is0AAECBAgQIECAAAECBAgQICBwuAECBAgQIECAAAECBAgQIEAgLyBw5FdoAAIECBAgQIAAAQIECBAgQEDgcAMECBAgQIAAAQIECBAgQIBAXkDgyK/QAAQIECBAgAABAgQIECBAgIDA4QYIECBAgAABAgQIECBAgACBvIDAkV+hAQgQIECAAAECBAgQIECAAAGBww0QIECAAAECBAgQIECAAAECeQGBI79CAxAgQIAAAQIECBAgQIAAAQIChxsgQIAAAQIECBAgQIAAAQIE8gICR36FBiBAgAABAgQIECBAgAABAgQEDjdAgAABAgQIECBAgAABAgQI5AUEjvwKDUCAAAECBAgQIECAAAECBAgIHG6AAAECBAgQIECAAAECBAgQyAsIHPkVGoAAAQIECBAgQIAAAQIECBAQONwAAQIECBAgQIAAAQIECBAgkBcQOPIrNAABAgQIECBAgAABAgQIECAgcLgBAgQIECBAgAABAgQIECBAIC8gcORXaAACBAgQIECAAAECBAgQIEBA4HADBAgQIECAAAECBAgQIECAQF5A4Miv0AAECBAgQIAAAQIECBAgQICAwOEGCBAgQIAAAQIECBAgQIAAgbyAwJFfoQEIECBAgAABAgQIECBAgAABgcMNECBAgAABAgQIECBAgAABAnkBgSO/QgMQIECAAAECBAgQIECAAAECAocbIECAAAECBAgQIECAAAECBPICAkd+hQYgQIAAAQIECBAgQIAAAQIEBA43QIAAAQIECBAgQIAAAQIECOQFBI78Cg1AgAABAgQIECBAgAABAgQICBxugAABAgQIECBAgAABAgQIEMgLCBz5FRqAAAECBAgQIECAAAECBAgQEDjcAAECBAgQIECAAAECBAgQIJAXEDjyKzQAAQIECBAgQIAAAQIECBAgIHC4AQIECBAgQIAAAQIECBAgQCAvIHDkV2gAAgQIECBAgAABAgQIECBAQOBwAwQIECBAgAABAgQIECBAgEBeQODIr9AABAgQIECAAAECBAgQIECAgMDhBggQIECAAAECBAgQIECAAIG8gMCRX6EBCBAgQIAAAQIECBAgQIAAAYHDDRAgQIAAAQIECBAgQIAAAQJ5AYEjv0IDECBAgAABAgQIECBAgAABAgKHGyBAgAABAgQIECBAgAABAgTyAgJHfoUGIECAAAECBAgQIECAAAECBAQON0CAAAECBAgQIECAAAECBAjkBQSO/AoNQIAAAQIECBAgQIAAAQIECAgcboAAAQIECBAgQIAAAQIECBDICwgc+RUagAABAgQIECBAgAABAgQIEBA43AABAgQIECBAgAABAgQIECCQFxA48is0AAECBAgQIECAAAECBAgQICBwuAECBAgQIECAAAECBAgQIEAgLyBw5FdoAAIECBAgQIAAAQIECBAgQEDgcAMECBAgQIAAAQIECBAgQIBAXkDgyK/QAAQIECBAgAABAgQIECBAgIDA4QYIECBAgAABAgQIECBAgACBvIDAkV+hAQgQIECAAAECBAgQIECAAAGBww0QIECAAAECBAgQIECAAAECeQGBI79CAxAgQIAAAQIECBAgQIAAAQIChxsgQIAAAQIECBAgQIAAAQIE8gICR36FBiBAgAABAgQIECBAgAABAgQEDjdAgAABAgQIECBAgAABAgQI5AUEjvwKDUCAAAECBAgQIECAAAECBAgIHG6AAAECBAgQIECAAAECBAgQyAsIHPkVGoAAAQIECBAgQIAAAQIECBAQONwAAQIECBAgQIAAAQIECBAgkBcQOPIrNAABAgQIECBAgAABAgQIECAgcLgBAgQIECBAgAABAgQIECBAIC8gcORXaAACBAgQIECAAAECBAgQIEBA4HADBAgQIECAAAECBAgQIECAQF5A4Miv0AAECBAgQIAAAQIECBAgQICAwOEGCBAgQIAAAQIECBAgQIAAgbyAwJFfoQEIECBAgAABAgQIECBAgAABgcMNECBAgAABAgQIECBAgAABAnkBgSO/QgMQIECAAAECBAgQIECAAAECAocbIECAAAECBAgQIECAAAECBPICAkd+hQYgQIAAAQIECBAgQIAAAQIEBA43QIAAAQIECBAgQIAAAQIECOQFBI78Cg1AgAABAgQIECBAgAABAgQICBxugAABAgQIECBAgAABAgQIEMgLCBz5FRqAAAECBAgQIECAAAECBAgQEDjcAAECBAgQIECAAAECBAgQIJAXEDjyKzQAAQIECBAgQIAAAQIECBAgIHC4AQIECBAgQIAAAQIECBAgQCAvIHDkV2gAAgQIECBAgAABAgQIECBAQOBwAwQIECBAgAABAgQIECBAgEBeQODIr9AABAgQIECAAAECBAgQIECAgMDhBggQIECAAAECBAgQIECAAIG8gMCRX6EBCBAgQIAAAQIECBAgQIAAAYHDDRAgQIAAAQIECBAgQIAAAQJ5AYEjv0IDECBAgAABAgQIECBAgAABAgKHGyBAgAABAgQIECBAgAABAgTyAgJHfoUGIECAAAECBAgQIECAAAECBAQON0CAAAECBAgQIECAAAECBAjkBQSO/AoNQIAAAQIECBAgQIAAAQIECAgcboAAAQIECBAgQIAAAQIECBDICwgc+RUagAABAgQIECBAgAABAgQIEBA43AABAgQIECBAgAABAgQIECCQFxA48is0AAECBAgQIECAAAECBAgQICBwuAECBAgQIECAAAECBAgQIEAgLyBw5FdoAAIECBAgQIAAAQIECBAgQEDgcAMECBAgQIAAAQIECBAgQIBAXkDgyK/QAAQIECBAgAABAgQIECBAgIDA4QYIECBAgAABAgQIECBAgACBvIDAkV+hAQgQIECAAAECBAgQIECAAAGBww0QIECAAAECBAgQIECAAAECeQGBI79CAxAgQIAAAQIECBAgQIAAAQIChxsgQIAAAQIECBAgQIAAAQIE8gICR36FBiBAgAABAgQIECBAgAABAgQEDjdAgAABAgQIECBAgAABAgQI5AUEjvwKDUCAAAECBAgQIECAAAECBAgIHG6AAAECBAgQIECAAAECBAgQyAsIHPkVGoAAAQIECBAgQIAAAQIECBAQONwAAQIECBAgQIAAAQIECBAgkBcQOPIrNAABAgQIECBAgAABAgQIECAgcLgBAgQIECBAgAABAgQIECBAIC8gcORXaAACBAgQIECAAAECBAgQIEBA4HADBAgQIECAAAECBAgQIECAQF5A4Miv0AAECBAgQIAAAQIECBAgQICAwOEGCBAgQIAAAQIECBAgQIAAgbyAwJFfoQEIECBAgAABAgQIECBAgAABgcMNECBAgAABAgQIECBAgAABAnkBgSO/QgMQIECAAAECBAgQIECAAAECAocbIECAAAECBAgQIECAAAECBPICAkd+hQYgQIAAAQIECBAgQIAAAQIEBA43QIAAAQIECBAgQIAAAQIECOQFBI78Cg1AgAABAgQIECBAgAABAgQICBxugAABAgQIECBAgAABAgQIEMgLCBz5FRqAAAECBAgQIECAAAECBAgQEDjcAAECBAgQIECAAAECBAgQIJAXEDjyKzQAAQIECBAgQIAAAQIECBAgIHC4AQIECBAgQIAAAQIECBAgQCAvIHDkV2gAAgQIECBAgAABAgQIECBAQOBwAwQIECBAgAABAgQIECBAgEBeQODIr9AABAgQIECAAAECBAgQIECAgMDhBggQIECAAAECBAgQIECAAIG8gMCRX6EBCBAgQIAAAQIECBAgQIAAAYHDDRAgQIAAAQIECBAgQIAAAQJ5AYEjv0IDECBAgAABAgQIECBAgAABAgKHGyBAgAABAgQIECBAgAABAgTyAgJHfoUGIECAAAECBAgQIECAAAECBAQON0CAAAECBAgQIECAAAECBAjkBQSO/AoNQIAAAQIECBAgQIAAAQIECAgcboAAAQIECBAgQIAAAQIECBDICwgc+RUagAABAgQIECBAgAABAgQIEBA43AABAgQIECBAgAABAgQIECCQFxA48is0AAECBAgQIECAAAECBAgQICBwuAECBAgQIECAAAECBAgQIEAgLyBw5FdoAAIECBAgQIAAAQIECBAgQEDgcAMECBAgQIAAAQIECBAgQIBAXkDgyK/QAAQIECBAgAABAgQIECBAgIDA4QYIECBAgAABAgQIECBAgACBvIDAkV+hAQgQIECAAAECBAgQIECAAAGBww0QIECAAAECBAgQIECAAAECeQGBI79CAxAgQIAAAQIECBAgQIAAAQIChxsgQIAAAQIECBAgQIAAAQIE8gICR36FBiBAgAABAgQIECBAgAABAgQEDjdAgAABAgQIECBAgAABAgQI5AUEjvwKDUCAAAECBAgQIECAAAECBAgIHG6AAAECBAgQIECAAAECBAgQyAsIHPkVGoAAAQIECBAgQIAAAQIECBAQONwAAQIECBAgQIAAAQIECBAgkBcQOPIrNAABAgQIECBAgAABAgQIECAgcLgBAgQIECBAgAABAgQIECBAIC8gcORXaAACBAgQIECAAAECBAgQIEBA4HADBAgQIECAAAECBAgQIECAQF5A4Miv0AAECBAgQIAAAQIECBAgQICAwOEGCBAgQIAAAQIECBAgQIAAgbyAwJFfoQEIECBAgAABAgQIECBAgAABgcMNECBAgAABAgQIECBAgAABAnkBgSO/QgMQIECAAAECBAgQIECAAAECAocbIECAAAECBAgQIECAAAECBPICAkd+hQYgQIAAAQIECBAgQIAAAQIEBA43QIAAAQIECBAgQIAAAQIECOQFBI78Cg1AgAABAgQIECBAgAABAgQICBxugAABAgQIECBAgAABAgQIEMgLCBz5FRqAAAECBAgQIECAAAECBAgQEDjcAAECBAgQIECAAAECBAgQIJAXEDjyKzQAAQIECBAgQIAAAQIECBAgIHC4AQIECBAgQIAAAQIECBAgQCAvIHDkV2gAAgQIECBAgAABAgQIECBAQOBwAwQIECBAgAABAgQIECBAgEBeQODIr9AABAgQIECAAAECBAgQIECAgMDhBggQIECAAAECBAgQIECAAIG8gMCRX6EBCBAgQIAAAQIECBAgQIAAAYHDDRAgQIAAAQIECBAgQIAAAQJ5AYEjv0IDECBAgAABAgQIECBAgAABAgKHGyBAgAABAgQIECBAgAABAgTyAgJHfoUGIECAAAECBAgQIECAAAECBAQON0CAAAECBAgQIECAAAECBAjkBQSO/AoNQIAAAQIECBAgQIAAAQIECAgcboAAAQIECBAgQIAAAQIECBDICwgc+RUagAABAgQIECBAgAABAgQIEBA43AABAgQIECBAgAABAgQIECCQFxA48is0AAECBAgQIECAAAECBAgQICBwuAECBAgQIECAAAECBAgQIEAgLyBw5FdoAAIECBAgQIAAAQIECBAgQEDgcAMECBAgQIAAAQIECBAgQIBAXkDgyK/QAAQIECBAgAABAgQIECBAgIDA4QYIECBAgAABAgQIECBAgACBvIDAkV+hAQgQIECAAAECBAgQIECAAAGBww0QIECAAAECBAgQIECAAAECeQGBI79CAxAgQIAAAQIECBAgQIAAAQIChxsgQIAAAQIECBAgQIAAAQIE8gICR36FBiBAgAABAgQIECBAgAABAgQEDjdAgAABAgQIECBAgAABAgQI5AUEjvwKDUCAAAECBAgQIECAAAECBAgIHG6AAAECBAgQIECAAAECBAgQyAsIHPkVGoAAAQIECBAgQIAAAQIECBAQONwAAQIECBAgQIAAAQIECBAgkBcQOPIrNAABAgQIECBAgAABAgQIECAgcLgBAgQIECBAgAABAgQIECBAIC8gcORXaAACBAgQIECAAAECBAgQIEBA4HADBAgQIECAAAECBAgQIECAQF5A4Miv0AAECBAgQIAAAQIECBAgQICAwOEGCBAgQIAAAQIECBAgQIAAgbyAwJFfoQEIECBAgAABAgQIECBAgAABgcMNECBAgAABAgQIECBAgAABAnkBgSO/QgMQIECAAAECBAgQIECAAAECAocbIECAAAECBAgQIECAAAECBPICAkd+hQYgQIAAAQIECBAgQIAAAQIEBA43QIAAAQIECBAgQIAAAQIECOQFBI78Cg1AgAABAgQIECBAgAABAgQICBxugAABAgQIECBAgAABAgQIEMgLCBz5FRqAAAECBAgQIECAAAECBAgQEDjcAAECBAgQIECAAAECBAgQIJAXEDjyKzQAAQIECBAgQIAAAQIECBAgIHC4AQIECBAgQIAAAQIECBAgQCAvIHDkV2gAAgQIECBAgAABAgQIECBAQOBwAwQIECBAgAABAgQIECBAgEBeQODIr9AABAgQIECAAAECBAgQIECAgMDhBggQIECAAAECBAgQIECAAIG8gMCRX6EBCBAgQIAAAQIECBAgQIAAAYHDDRAgQIAAAQIECBAgQIAAAQJ5AYEjv0IDECBAgAABAgQIECBAgAABAgKHGyBAgAABAgQIECBAgAABAgTyAgJHfoUGIECAAAECBAgQIECAAAECBAQON0CAAAECBAgQIECAAAECBAjkBQSO/AoNQIAAAQIECBAgQIAAAQIECAgcboAAAQIECBAgQIAAAQIECBDICwgc+RUagAABAgQIECBAgAABAgQIEBA43AABAgQIECBAgAABAgQIECCQFxA48is0AAECBAgQIECAAAECBAgQICBwuAECBAgQIECAAAECBAgQIEAgLyBw5FdoAAIECBAgQIAAAQIECBAgQEDgcAMECBAgQIAAAQIECBAgQIBAXkDgyK/QAAQIECBAgAABAgQIECBAgIDA4QYIECBAgAABAgQIECBAgACBvIDAkV+hAQgQIECAAAECBAgQIECAAAGBww0QIECAAAECBAgQIECAAAECeQGBI79CAxAgQIAAAQIECBAgQIAAAQIChxsgQIAAAQIECBAgQIAAAQIE8gICR36FBiBAgAABAgQIECBAgAABAgQEDjdAgAABAgQIECBAgAABAgQI5AUEjvwKDUCAAAECBAgQIECAAAECBAgIHG6AAAECBAgQIECAAAECBAgQyAsIHPkVGoAAAQIECBAgQIAAAQIECBAQONwAAQIECBAgQIAAAQIECBAgkBcQOPIrNAABAgQIECBAgAABAgQIECAgcLgBAgQIECBAgAABAgQIECBAIC8gcORXaAACBAgQIECAAAECBAgQIEBA4HADBAgQIECAAAECBAgQIECAQF5A4Miv0AAECBAgQIAAAQIECBAgQICAwOEGCBAgQIAAAQIECBAgQIAAgbyAwJFfoQEIECBAgAABAgQIECBAgAABgcMNECBAgAABAgQIECBAgAABAnkBgSO/QgMQIECAAAECBAgQIECAAAECAocbIECAAAECBAgQIECAAAECBPICAkd+hQYgQIAAAQIECBAgQIAAAQIEBA43QIAAAQIECBAgQIAAAQIECOQFBI78Cg1AgAABAgQIECBAgAABAgQICBxugAABAgQIECBAgAABAgQIEMgLCBz5FRqAAAECBAgQIECAAAECBAgQEDjcAAECBAgQIECAAAECBAgQIJAXEDjyKzQAAQIECBAgQIAAAQIECBAgIHC4AQIECBAgQIAAAQIECBAgQCAvIHDkV2gAAgQIECBAgAABAgQIECBAQOBwAwQIECBAgAABAgQIECBAgEBeQODIr9AABAgQIECAAAECBAgQIECAgMDhBggQIECAAAECBAgQIECAAIG8gMCRX6EBCBAgQIAAAQIECBAgQIAAAYHDDRAgQIAAAQIECBAgQIAAAQJ5AYEjv0IDECBAgAABAgQIECBAgAABAgKHGyBAgAABAgQIECBAgAABAgTyAgJHfoUGIECAAAECBAgQIECAAAECBAQON0CAAAECBAgQIECAAAECBAjkBQSO/AoNQIAAAQIECBAgQIAAAQIECAgcboAAAQIECBAgQIAAAQIECBDICwgc+RUagAABAgQIECBAgAABAgQIEBA43AABAgQIECBAgAABAgQIECCQFxA48is0AAECBAgQIECAAAECBAgQICBwuAECBAgQIECAAAECBAgQIEAgLyBw5FdoAAIECBAgQIAAAQIECBAgQEDgcAMECBAgQIAAAQIECBAgQIBAXkDgyK/QAAQIECBAgAABAgQIECBAgIDA4QYIECBAgAABAgQIECBAgACBvIDAkV+hAQgQIECAAAECBAgQIECAAAGBww0QIECAAAECBAgQIECAAAECeQGBI79CAxAgQIAAAQIECBAgQIAAAQIChxsgQIAAAQIECBAgQIAAAQIE8gICR36FBiBAgAABAgQIECBAgAABAgQEDjdAgAABAgQIECBAgAABAgQI5AUEjvwKDUCAAAECBAgQIECAAAECBAgIHG6AAAECBAgQIECAAAECBAgQyAsIHPkVGoAAAQIECBAgQIAAAQIECBAQONwAAQIECBAgQIAAAQIECBAgkBcQOPIrNAABAgQIECBAgAABAgQIECAgcLgBAgQIECBAgAABAgQIECBAIC8gcORXaAACBAgQIECAAAECBAgQIEBA4HADBAgQIECAAAECBAgQIECAQF5A4Miv0AAECBAgQIAAAQIECBAgQICAwOEGCBAgQIAAAQIECBAgQIAAgbyAwJFfoQEIECBAgAABAgQIECBAgAABgcMNECBAgAABAgQIECBAgAABAnkBgSO/QgMQIECAAAECBAgQIECAAAECAocbIECAAAECBAgQIECAAAECBPICAkd+hQYgQIAAAQIECBAgQIAAAQIEBA43QIAAAQIECBAgQIAAAQIECOQFBI78Cg1AgAABAtAImrMAACAASURBVAQIECBAgAABAgQICBxugAABAgQIECBAgAABAgQIEMgLCBz5FRqAAAECBAgQIECAAAECBAgQEDjcAAECBAgQIECAAAECBAgQIJAXEDjyKzQAAQIECBAgQIAAAQIECBAgIHC4AQIECBAgQIAAAQIECBAgQCAvIHDkV2gAAgQIECBAgAABAgQIECBAQOBwAwQIECBAgAABAgQIECBAgEBeQODIr9AABAgQIECAAAECBAgQIECAgMDhBggQIECAAAECBAgQIECAAIG8gMCRX6EBCBAgQIAAAQIECBAgQIAAAYHDDRAgQIAAAQIECBAgQIAAAQJ5AYEjv0IDECBAgAABAgQIECBAgAABAgKHGyBAgAABAgQIECBAgAABAgTyAgJHfoUGIECAAAECBAgQIECAAAECBAQON0CAAAECBAgQIECAAAECBAjkBQSO/AoNQIAAAQIECBAgQIAAAQIECAgcboAAAQIECBAgQIAAAQIECBDICwgc+RUagAABAgQIECBAgAABAgQIEBA43AABAgQIECBAgAABAgQIECCQFxA48is0AAECBAgQIECAAAECBAgQICBwuAECBAgQIECAAAECBAgQIEAgLyBw5FdoAAIECBAgQIAAAQIECBAgQEDgcAMECBAgQIAAAQIECBAgQIBAXkDgyK/QAAQIECBAgAABAgQIECBAgIDA4QYIECBAgAABAgQIECBAgACBvIDAkV+hAQgQIECAAAECBAgQIECAAAGBww0QIECAAAECBAgQIECAAAECeQGBI79CAxAgQIAAAQIECBAgQIAAAQIChxsgQIAAAQIECBAgQIAAAQIE8gICR36FBiBAgAABAgQIECBAgAABAgQEDjdAgAABAgQIECBAgAABAgQI5AUEjvwKDUCAAAECBAgQIECAAAECBAgIHG6AAAECBAgQIECAAAECBAgQyAsIHPkVGoAAAQIECBAgQIAAAQIECBAQONwAAQIECBAgQIAAAQIECBAgkBcQOPIrNAABAgQIECBAgAABAgQIECAgcLgBAgQIECBAgAABAgQIECBAIC8gcORXaAACBAgQIECAAAECBAgQIEBA4HADBAgQIECAAAECBAgQIECAQF5A4Miv0AAECBAgQIAAAQIECBAgQICAwOEGCBAgQIAAAQIECBAgQIAAgbyAwJFfoQEIECBAgAABAgQIECBAgAABgcMNECBAgAABAgQIECBAgAABAnkBgSO/QgMQIECAAAECBAgQIECAAAECAocbIECAAAECBAgQIECAAAECBPICAkd+hQYgQIAAAQIECBAgQIAAAQIEBA43QIAAAQIECBAgQIAAAQIECOQFBI78Cg1AgAABAgQIECBAgAABAgQICBxugAABAgQIECBAgAABAgQIEMgLCBz5FRqAAAECBAgQIECAAAECBAgQEDjcAAECBAgQIECAAAECBAgQIJAXEDjyKzQAAQIECBAgQIAAAQIECBAgIHC4AQIECBAgQIAAAQIECBAgQCAvIHDkV2gAAgQIECBAgAABAgQIECBAQOBwAwQIECBAgAABAgQIECBAgEBeQODIr9AABAgQIECAAAECBAgQIECAgMDhBggQIECAAAECBAgQIECAAIG8gMCRX6EBCBAgQIAAAQIECBAgQIAAAYHDDRAgQIAAAQIECBAgQIAAAQJ5AYEjv0IDECBAgAABAgQIECBAgAABAgKHGyBAgAABAgQIECBAgAABAgTyAgJHfoUGIECAAAECBAgQIECAAAECBAQON0CAAAECBAgQIECAAAECBAjkBQSO/AoNQIAAAQIECBAgQIAAAQIECAgcboAAAQIECBAgQIAAAQIECBDICwgc+RUagAABAgQIECBAgAABAgQIEBA43AABAgQIECBAgAABAgQIECCQFxA48is0AAECBAgQIECAAAECBAgQICBwuAECBAgQIECAAAECBAgQIEAgLyBw5FdoAAIECBAgQIAAAQIECBAgQEDgcAMECBAgQIAAAQIECBAgQIBAXkDgyK/QAAQIECBAgAABAgQIECBAgIDA4QYIECBAgAABAgQIECBAgACBvIDAkV+hAQgQIECAAAECBAgQIECAAAGBww0QIECAAAECBAgQIECAAAECeQGBI79CAxAgQIAAAQIECBAgQIAAAQIChxsgQIAAAQIECBAgQIAAAQIE8gICR36FBiBAgAABAgQIECBAgAABAgQEDjdAgAABAgQIECBAgAABAgQI5AUEjvwKDUCAAAECBAgQIECAAAECBAgIHG6AAAECBAgQIECAAAECBAgQyAsIHPkVGoAAAQIECBAgQIAAAQIECBAQONwAAQIECBAgQIAAAQIECBAgkBcQOPIrNAABAgQIECBAgAABAgQIECAgcLgBAgQIECBAgAABAgQIECBAIC8gcORXaAACBAgQIECAAAECBAgQIEBA4HADBAgQIECAAAECBAgQIECAQF5A4Miv0AAECBAgQIAAAQIECBAgQICAwOEGCBAgQIAAAQIECBAgQIAAgbyAwJFfoQEIECBAgAABAgQIECBAgAABgcMNECBAgAABAgQIECBAgAABAnkBgSO/QgMQIECAAAECBAgQIECAAAECAocbIECAAAECBAgQIECAAAECBPICAkd+hQYgQIAAAQIECBAgQIAAAQIEBA43QIAAAQIECBAgQIAAAQIECOQFBI78Cg1AgAABAgQIECBAgAABAgQICBxugAABAgQIECBAgAABAgQIEMgLCBz5FRqAAAECBAgQIECAAAECBAgQEDjcAAECBAgQIECAAAECBAgQIJAXEDjyKzQAAQIECBAgQIAAAQIECBAgIHC4AQIECBAgQIAAAQIECBAgQCAvIHDkV2gAAgQIECBAgAABAgQIECBAQOBwAwQIECBAgAABAgQIECBAgEBeQODIr9AABAgQIECAAAECBAgQIECAgMDhBggQIECAAAECBAgQIECAAIG8gMCRX6EBCBAgQIAAAQIECBAgQIAAAYHDDRAgQIAAAQIECBAgQIAAAQJ5AYEjv0IDECBAgAABAgQIECBAgAABAgKHGyBAgAABAgQIECBAgAABAgTyAgJHfoUGIECAAAECBAgQIECAAAECBAQON0CAAAECBAgQIECAAAECBAjkBQSO/AoNQIAAAQIECBAgQIAAAQIECAgcboAAAQIECBAgQIAAAQIECBDICwgc+RUagAABAgQIECBAgAABAgQIEBA43AABAgQIECBAgAABAgQIECCQFxA48is0AAECBAgQIECAAAECBAgQICBwuAECBAgQIECAAAECBAgQIEAgLyBw5FdoAAIECBAgQIAAAQIECBAgQEDgcAMECBAgQIAAAQIECBAgQIBAXkDgyK/QAAQIECBAgAABAgQIECBAgIDA4QYIECBAgAABAgQIECBAgACBvIDAkV+hAQgQIECAAAECBAgQIECAAAGBww0QIECAAAECBAgQIECAAAECeQGBI79CAxAgQIAAAQIECBAgQIAAAQIChxsgQIAAAQIECBAgQIAAAQIE8gICR36FBiBAgAABAgQIECBAgAABAgQEDjdAgAABAgQIECBAgAABAgQI5AUEjvwKDUCAAAECBAgQIECAAAECBAgIHG6AAAECBAgQIECAAAECBAgQyAsIHPkVGoAAAQIECBAgQIAAAQIECBAQONwAAQIECBAgQIAAAQIECBAgkBcQOPIrNAABAgQIECBAgAABAgQIECAgcLgBAgQIECBAgAABAgQIECBAIC8gcORXaAACBAgQIECAAAECBAgQIEBA4HADBAgQIECAAAECBAgQIECAQF5A4Miv0AAECBAgQIAAAQIECBAgQICAwOEGCBAgQIAAAQIECBAgQIAAgbyAwJFfoQEIECBAgAABAgQIECBAgAABgcMNECBAgAABAgQIECBAgAABAnkBgSO/QgMQIECAAAECBAgQIECAAAECAocbIECAAAECBAgQIECAAAECBPICAkd+hQYgQIAAAQIECBAgQIAAAQIEBA43QIAAAQIECBAgQIAAAQIECOQFBI78Cg1AgAABAgQIECBAgAABAgQICBxugAABAgQIECBAgAABAgQIEMgLCBz5FRqAAAECBAgQIECAAAECBAgQEDjcAAECBAgQIECAAAECBAgQIJAXEDjyKzQAAQIECBAgQIAAAQIECBAgIHC4AQIECBAgQIAAAQIECBAgQCAvIHDkV2gAAgQIECBAgAABAgQIECBAQOBwAwQIECBAgAABAgQIECBAgEBeQODIr9AABAgQIECAAAECBAgQIECAgMDhBggQIECAAAECBAgQIECAAIG8gMCRX6EBCBAgQIAAAQIECBAgQIAAAYHDDRAgQIAAAQIECBAgQIAAAQJ5AYEjv0IDECBAgAABAgQIECBAgAABAgKHGyBAgAABAgQIECBAgAABAgTyAgJHfoUGIECAAAECBAgQIECAAAECBAQON0CAAAECBAgQIECAAAECBAjkBQSO/AoNQIAAAQIECBAgQIAAAQIECAgcboAAAQIECBAgQIAAAQIECBDICwgc+RUagAABAgQIECBAgAABAgQIEBA43AABAgQIECBAgAABAgQIECCQFxA48is0AAECBAgQIECAAAECBAgQICBwuAECBAgQIECAAAECBAgQIEAgLyBw5FdoAAIECBAgQIAAAQIECBAgQEDgcAMECBAgQIAAAQIECBAgQIBAXkDgyK/QAAQIECBAgAABAgQIECBAgIDA4QYIECBAgAABAgQIECBAgACBvIDAkV+hAQgQIECAAAECBAgQIECAAAGBww0QIECAAAECBAgQIECAAAECeQGBI79CAxAgQIAAAQIECBAgQIAAAQIChxsgQIAAAQIECBAgQIAAAQIE8gICR36FBiBAgAABAgQIECBAgAABAgQEDjdAgAABAgQIECBAgAABAgQI5AUEjvwKDUCAAAECBAgQIECAAAECBAgIHG6AAAECBAgQIECAAAECBAgQyAsIHPkVGoAAAQIECBAgQIAAAQIECBAQONwAAQIECBAgQIAAAQIECBAgkBcQOPIrNAABAgQIECBAgAABAgQIECAgcLgBAgQIECBAgAABAgQIECBAIC8gcORXaAACBAgQIECAAAECBAgQIEBA4HADBAgQIECAAAECBAgQIECAQF5A4Miv0AAECBAgQIAAAQIECBAgQICAwOEGCBAgQIAAAQIECBAgQIAAgbyAwJFfoQEIECBAgAABAgQIECBAgAABgcMNECBAgAABAgQIECBAgAABAnkBgSO/QgMQIECAAAECBAgQIECAAAECAocbIECAAAECBAgQIECAAAECBPICAkd+hQYgQIAAAQIECBAgQIAAAQIEBA43QIAAAQIECBAgQIAAAQIECOQFBI78Cg1AgAABAgQIECBAgAABAgQICBxugAABAgQIECBAgAABAgQIEMgLCBz5FRqAAAECBAgQIECAAAECBAgQEDjcAAECBAgQIECAAAECBAgQIJAXEDjyKzQAAQIECBAgQIAAAQIECBAgIHC4AQIECBAgQIAAAQIECBAgQCAvIHDkV2gAAgQIECBAgAABAgQIECBAQOBwAwQIECBAgAABAgQIECBAgEBeQODIr9AABAgQIECAAAECBAgQIECAgMDhBggQIECAAAECBAgQIECAAIG8gMCRX6EBCBAgQIAAAQIECBAgQIAAAYHDDRAgQIAAAQIECBAgQIAAAQJ5AYEjv0IDECBAgAABAgQIECBAgAABAgKHGyBAgAABAgQIECBAgAABAgTyAgJHfoUGIECAAAECBAgQIECAAAECBAQON0CAAAECBAgQIECAAAECBAjkBQSO/AoNQIAAAQIECBAgQIAAAQIECAgcboAAAQIECBAgQIAAAQIECBDICwgc+RUagAABAgQIECBAgAABAgQIEBA43AABAgQIECBAgAABAgQIECCQFxA48is0AAECBAgQIECAAAECBAgQICBwuAECBAgQIECAAAECBAgQIEAgLyBw5FdoAAIECBAgQIAAAQIECBAgQEDgcAMECBAgQIAAAQIECBAgQIBAXkDgyK/QAAQIECBAgAABAgQIECBAgIDA4QYIECBAgAABAgQIECBAgACBvIDAkV+hAQgQIECAAAECBAgQIECAAAGBww0QIECAAAECBAgQIECAAAECeQGBI79CAxAgQIAAAQIECBAgQIAAAQIChxsgQIAAAQIECBAgQIAAAQIE8gICR36FBiBAgAABAgQIECBAgAABAgQEDjdAgAABAgQIECBAgAABAgQI5AUEjvwKDUCAAAECBAgQIECAAAECBAgIHG6AAAECBAgQIECAAAECBAgQyAsIHPkVGoAAAQIECBAgQIAAAQIECBAQONwAAQIECBAgQIAAAQIECBAgkBcQOPIrNAABAgQIECBAgAABAgQIECAgcLgBAgQIECBAgAABAgQIECBAIC8gcORXaAACBAgQIECAAAECBAgQIEBA4HADBAgQIECAAAECBAgQIECAQF5A4Miv0AAECBAgQIAAAQIECBAgQICAwOEGCBAgQIAAAQIECBAgQIAAgbyAwJFfoQEIECBAgAABAgQIECBAgAABgcMNECBAgAABAgQIECBAgAABAnkBgSO/QgMQIECAAAECBAgQIECAAAECAocbIECAAAECBAgQIECAAAECBPICAkd+hQYgQIAAAQIECBAgQIAAAQIEBA43QIAAAQIECBAgQIAAAQIECOQFBI78Cg1AgAABAgQIECBAgAABAgQICBxugAABAgQIECBAgAABAgQIEMgLCBz5FRqAAAECBAgQIECAAAECBAgQEDjcAAECBAgQIECAAAECBAgQIJAXEDjyKzQAAQIECBAgQIAAAQIECBAgIHC4AQIECBAgQIAAAQIECBAgQCAvIHDkV2gAAgQIECBAgAABAgQIECBAQOBwAwQIECBAgAABAgQIECBAgEBeQODIr9AABAgQIECAAAECBAgQIECAgMDhBggQIECAAAECBAgQIECAAIG8gMCRX6EBCBAgQIAAAQIECBAgQIAAAYHDDRAgQIAAAQIECBAgQIAAAQJ5AYEjv0IDECBAgAABAgQIECBAgAABAgKHGyBAgAABAgQIECBAgAABAgTyAgJHfoUGIECAAAECBAgQIECAAAECBAQON0CAAAECBAgQIECAAAECBAjkBQSO/AoNQIAAAQIECBAgQIAAAQIECAgcboAAAQIECBAgQIAAAQIECBDICwgc+RUagAABAgQIECBAgAABAgQIEBA43AABAgQIECBAgAABAgQIECCQFxA48is0AAECBAgQIECAAAECBAgQICBwuAECBAgQIECAAAECBAgQIEAgLyBw5FdoAAIECBAgQIAAAQIECBAgQEDgcAMECBAgQIAAAQIECBAgQIBAXkDgyK/QAAQIECBAgAABAgQIECBAgIDA4QYIECBAgAABAgQIECBAgACBvIDAkV+hAQgQIECAAAECBAgQIECAAAGBww0QIECAAAECBAgQIECAAAECeQGBI79CAxAgQIAAAQIECBAgQIAAAQIChxsgQIAAAQIECBAgQIAAAQIE8gICR36FBiBAgAABAgQIECBAgAABAgQEDjdAgAABAgQIECBAgAABAgQI5AUEjvwKDUCAAAECBAgQIECAAAECBAgIHG6AAAECBAgQIECAAAECBAgQyAsIHPkVGoAAAQIECBAgQIAAAQIECBAQONwAAQIECBAgQIAAAQIECBAgkBcQOPIrNAABAgQIECBAgAABAgQIECAgcLgBAgQIECBAgAABAgQIECBAIC8gcORXaAACBAgQIECAAAECBAgQIEBA4HADBAgQIECAAAECBAgQIECAQF5A4Miv0AAECBAgQIAAAQIECBAgQICAwOEGCBAgQIAAAQK/UoHvvvvuX37++ec/PB6Pz//5t39g+NPj8fj+5eXl+0+fPv31V0pkbAIECBAICQgcoWX5VQkQIECAAAEC7yXw+vr6u88B4/F4/OafvPnT5wDyfD5/eK9ve4cAAQIECCwEBI6FqjcJECBAgAABAh9Y4PX19fOf2Piv/+ev+Mfn8/k5iPiHAAECBAh8SAGB40OuxS9FgAABAgQIENgIfPvtt9999dVX//3G13/vT3K8Uc6PESBAgMBcQOCYE/sAAQIECBAgQODjCLy+vv758Xj89o2/0U8vLy//6t/J8UY9P0aAAAECUwGBY8rrcQIECBAgQIDAxxF4419N+ccB/uP5fP7nx5nKb0KAAAECBP4mIHC4BAIECBAgQIDAr0Tg9fX1878o9N+/cNw/PZ/Pr7/wDT9OgAABAgTeXUDgeHdSDxIgQIAAAQIEPqbA6+vrL+/xmz2fT/8b8j0gvUGAAAEC7yrgv5zeldNjBAgQIECAAIGPKyBwfNzd+M0IECBA4MsFBI4vN/QCAQIECBAgQCAhIHAk1uSXJECAAIE3Cggcb4TzYwQIECBAgACBmsDr6+tfH4/Hb7709/ZXVL5U0M8TIECAwEJA4FioepMAAQIECBAg8AEF/EtGP+BS/EoECBAg8G4CAse7UXqIAAECBAgQIPCxBfzfxH7s/fjtCBAgQODLBASOL/Pz0wQIECBAgACBlMDr6+ufH4/Hb9/4S//l5eXl60+fPn3+qy7+IUCAAAECH0pA4PhQ6/DLECBAgAABAgS2At98883XLy8v//PGr/z++Xz+8Maf9WMECBAgQGAqIHBMeT1OgAABAgQIEPh4Am/8qyp/fD6f33+8afxGBAgQIEDgbwICh0sgQIAAAQIECPwKBf7+Jzk+/2mMf/bXVf7yyy+//OHHH3/89CtkMjIBAgQIhAQEjtCy/KoECBAgQIAAgfcW+Puf5vjd4/H47v/8X8j+9Hg8PgeNH/ypjfcW9x4BAgQIrAQEjpWsdwkQIECAAAECBAgQIECAAIEzAYHjjNqHCBAgQIAAAQIECBAgQIAAgZWAwLGS9S4BAgQIECBAgAABAgQIECBwJiBwnFH7EAECBAgQIECAAAECBAgQILASEDhWst4lQIAAAQIECBAgQIAAAQIEzgQEjjNqHyJAgAABAgQIECBAgAABAgRWAgLHSta7BAgQIECAAAECBAgQIECAwJmAwHFG7UMECBAgQIAAAQIECBAgQIDASkDgWMl6lwABAgQIECBAgAABAgQIEDgTEDjOqH2IAAECBAgQIECAAAECBAgQWAkIHCtZ7xIgQIAAAQIECBAgQIAAAQJnAgLHGbUPESBAgAABAgQIECBAgAABAisBgWMl610CBAgQIECAAAECBAgQIEDgTEDgOKP2IQIECBAgQIAAAQIECBAgQGAlIHCsZL1LgAABAgQIECBAgAABAgQInAkIHGfUPkSAAAECBAgQIECAAAECBAisBASOlax3CRAgQIAAAQIECBAgQIAAgTMBgeOM2ocIECBAgAABAgQIECBAgACBlYDAsZL1LgECBAgQIECAAAECBAgQIHAmIHCcUfsQAQIECBAgQIAAAQIECBAgsBIQOFay3iVAgAABAgQIECBAgAABAgTOBASOM2ofIkCAAAECBAgQIECAAAECBFYCAsdK1rsECBAgQIAAAQIECBAgQIDAmYDAcUbtQwQIECBAgAABAgQIECBAgMBKQOBYyXqXAAECBAgQIECAAAECBAgQOBMQOM6ofYgAAQIECBAgQIAAAQIECBBYCQgcK1nvEiBAgAABAgQIECBAgAABAmcCAscZtQ8RIECAAAECBAgQIECAAAECKwGBYyXrXQIECBAgQIAAAQIECBAgQOBMQOA4o/YhAgQIECBAgAABAgQIECBAYCUgcKxkvUuAAAECBAgQIECAAAECBAicCQgcZ9Q+RIAAAQIECBAgQIAAAQIECKwEBI6VrHcJECBAgAABAgQIECBAgACBMwGB44zahwgQIECAAAECBAgQIECAAIGVgMCxkvUuAQIECBAgQIAAAQIECBAgcCYgcJxR+xABAgQIECBAgAABAgQIECCwEhA4VrLeJUCAAAECBAgQIECAAAECBM4EBI4zah8iQIAAAQIECBAgQIAAAQIEVgICx0rWuwQIECBAgAABAgQIECBAgMCZgMBxRu1DBAgQIECAAAECBAgQIECAwEpA4FjJepcAAQIECBAgQIAAAQIECBA4ExA4zqh9iAABAgQIECBAgAABAgQIEFgJCBwrWe8SIECAAAECBAgQIECAAAECZwICxxm1DxEgQIAAAQIECBAgQIAAAQIrAYFjJetdAgQIECBAgAABAgQIECBA4ExA4Dij9iECBAgQIECAAAECBAgQIEBgJSBwrGS9S4AAAQIECBAgQIAAAQIECJwJCBxn1D5EgAABAgQIECBAgAABAgQIrAQEjpWsdwkQIECAAAECBAgQIECAAIEzAYHjjNqHCBAgQIAAAQIECBAgQIAAgZWAwLGS9S4BAgQIECBAgAABAgQIECBwJiBwnFH7EAECBAgQIECAAAECBAgQILASEDhWst4lQIAAAQIECBAgQIAAAQIEzgQEjjNqHyJAgAABAgQIECBAgAABAgRWAgLHSta7BAgQIECAAAECBAgQIECAwJmAwHFG7UMECBAgQIAAAQIECBAgQIDASkDgWMl6lwABAgQIECBAgAABAgQIEDgTEDjOqH2IAAECBAgQIECAAAECBAgQWAkIHCtZ7xIgQIAAAQIECBAgQIAAAQJnAgLHGbUPESBAgAABAgQIECBAgAABAisBgWMl610CBAgQIECAAAECBAgQIEDgTEDgOKP2IQIECBAgQIAAAQIECBAgQGAlIHCsZL1LgAABAgQIECBAgAABAgQInAkIHGfUPkSAAAECBAgQIECAAAECBAisBASOlax3CRAgQIAAAQIECBAgQIAAgTMBgeOM2ocIECBAgAABAgQIECBAgACBlYDAsZL1LgECBAgQIECAAAECBAgQU93xWAAAHR1JREFUIHAmIHCcUfsQAQIECBAgQIAAAQIECBAgsBIQOFay3iVAgAABAgQIECBAgAABAgTOBASOM2ofIkCAAAECBAgQIECAAAECBFYCAsdK1rsECBAgQIAAAQIECBAgQIDAmYDAcUbtQwQIECBAgAABAgQIECBAgMBKQOBYyXqXAAECBAgQIECAAAECBAgQOBMQOM6ofYgAAQIECBAgQIAAAQIECBBYCQgcK1nvEiBAgAABAgQIECBAgAABAmcCAscZtQ8RIECAAAECBAgQIECAAAECKwGBYyXrXQIECBAgQIAAAQIECBAgQOBMQOA4o/YhAgQIECBAgAABAgQIECBAYCUgcKxkvUuAAAECBAgQIECAAAECBAicCQgcZ9Q+RIAAAQIECBAgQIAAAQIECKwEBI6VrHcJECBAgAABAgQIECBAgACBMwGB44zahwgQIECAAAECBAgQIECAAIGVgMCxkvUuAQIECBAgQIAAAQIECBAgcCYgcJxR+xABAgQIECBAgAABAgQIECCwEhA4VrLeJUCAAAECBAgQIECAAAECBM4EBI4zah8iQIAAAQIECBAgQIAAAQIEVgICx0rWuwQIECBAgAABAgQIECBAgMCZgMBxRu1DBAgQIECAAAECBAgQIECAwEpA4FjJepcAAQIECBAgQIAAAQIECBA4ExA4zqh9iAABAgQIECBAgAABAgQIEFgJCBwrWe8SIECAAAECBAgQIECAAAECZwICxxm1DxEgQIAAAQIECBAgQIAAAQIrAYFjJetdAgQIECBAgAABAgQIECBA4ExA4Dij9iECBAgQIECAAAECBAgQIEBgJSBwrGS9S4AAAQIECBAgQIAAAQIECJwJCBxn1D5EgAABAgQIECBAgAABAgQIrAQEjpWsdwkQIECAAAECBAgQIECAAIEzAYHjjNqHCBAgQIAAAQIECBAgQIAAgZWAwLGS9S4BAgQIECBAgAABAgQIECBwJiBwnFH7EAECBAgQIECAAAECBAgQILASEDhWst4lQIAAAQIECBAgQIAAAQIEzgQEjjNqHyJAgAABAgQIECBAgAABAgRWAgLHSta7BAgQIECAAAECBAgQIECAwJmAwHFG7UMECBAgQIAAAQIECBAgQIDASkDgWMl6lwABAgQIECBAgAABAgQIEDgTEDjOqH2IAAECBAgQIECAAAECBAgQWAkIHCtZ7xIgQIAAAQIECBAgQIAAAQJnAgLHGbUPESBAgAABAgQIECBAgAABAisBgWMl610CBAgQIECAAAECBAgQIEDgTEDgOKP2IQIECBAgQIAAAQIECBAgQGAlIHCsZL1LgAABAgQIECBAgAABAgQInAkIHGfUPkSAAAECBAgQIECAAAECBAisBASOlax3CRAgQIAAAQIECBAgQIAAgTMBgeOM2ocIECBAgAABAgQIECBAgACBlYDAsZL1LgECBAgQIECAAAECBAgQIHAmIHCcUfsQAQIECBAgQIAAAQIECBAgsBIQOFay3iVAgAABAgQIECBAgAABAgTOBASOM2ofIkCAAAECBAgQIECAAAECBFYCAsdK1rsECBAgQIAAAQIECBAgQIDAmYDAcUbtQwQIECBAgAABAgQIECBAgMBKQOBYyXqXAAECBAgQIECAAAECBAgQOBMQOM6ofYgAAQIECBAgQIAAAQIECBBYCQgcK1nvEiBAgAABAgQIECBAgAABAmcCAscZtQ8RIECAAAECBAgQIECAAAECKwGBYyXrXQIECBAgQIAAAQIECBAgQOBMQOA4o/YhAgQIECBAgAABAgQIECBAYCUgcKxkvUuAAAECBAgQIECAAAECBAicCQgcZ9Q+RIAAAQIECBAgQIAAAQIECKwEBI6VrHcJECBAgAABAgQIECBAgACBMwGB44zahwgQIECAAAECBAgQIECAAIGVgMCxkvUuAQIECBAgQIAAAQIECBAgcCYgcJxR+xABAgQIECBAgAABAgQIECCwEhA4VrLeJUCAAAECBAgQIECAAAECBM4EBI4zah8iQIAAAQIECBAgQIAAAQIEVgICx0rWuwQIECBAgAABAgQIECBAgMCZgMBxRu1DBAgQIECAAAECBAgQIECAwEpA4FjJepcAAQIECBAgQIAAAQIECBA4ExA4zqh9iAABAgQIECBAgAABAgQIEFgJCBwrWe8SIECAAAECBAgQIECAAAECZwICxxm1DxEgQIAAAQIECBAgQIAAAQIrAYFjJetdAgQIECBAgAABAgQIECBA4ExA4Dij9iECBAgQIECAAAECBAgQIEBgJSBwrGS9S4AAAQIECBAgQIAAAQIECJwJCBxn1D5EgAABAgQIECBAgAABAgQIrAQEjpWsdwkQIECAAAECBAgQIECAAIEzAYHjjNqHCBAgQIAAAQIECBAgQIAAgZWAwLGS9S4BAgQIECBAgAABAgQIECBwJiBwnFH7EAECBAgQIECAAAECBAgQILASEDhWst4lQIAAAQIECBAgQIAAAQIEzgQEjjNqHyJAgAABAgQIECBAgAABAgRWAgLHSta7BAgQIECAAAECBAgQIECAwJmAwHFG7UMECBAgQIAAAQIECBAgQIDASkDgWMl6lwABAgQIECBAgAABAgQIEDgTEDjOqH2IAAECBAgQIECAAAECBAgQWAkIHCtZ7xIgQIAAAQIECBAgQIAAAQJnAgLHGbUPESBAgAABAgQIECBAgAABAisBgWMl610CBAgQIECAAAECBAgQIEDgTEDgOKP2IQIECBAgQIAAAQIECBAgQGAlIHCsZL1LgAABAgQIECBAgAABAgQInAkIHGfUPkSAAAECBAgQIECAAAECBAisBASOlax3CRAgQIAAAQIECBAgQIAAgTMBgeOM2ocIECBAgAABAgQIECBAgACBlYDAsZL1LgECBAgQIECAAAECBAgQIHAmIHCcUfsQAQIECBAgQIAAAQIECBAgsBIQOFay3iVAgAABAgQIECBAgAABAgTOBASOM2ofIkCAAAECBAgQIECAAAECBFYCAsdK1rsECBAgQIAAAQIECBAgQIDAmYDAcUbtQwQIECBAgAABAgQIECBAgMBKQOBYyXqXAAECBAgQIECAAAECBAgQOBMQOM6ofYgAAQIECBAgQIAAAQIECBBYCQgcK1nvEiBAgAABAgQIECBAgAABAmcCAscZtQ8RIECAAAECBAgQIECAAAECKwGBYyXrXQIECBAgQIAAAQIECBAgQOBMQOA4o/YhAgQIECBAgAABAgQIECBAYCUgcKxkvUuAAAECBAgQIECAAAECBAicCQgcZ9Q+RIAAAQIECBAgQIAAAQIECKwEBI6VrHcJECBAgAABAgQIECBAgACBMwGB44zahwgQIECAAAECBAgQIECAAIGVgMCxkvUuAQIECBAgQIAAAQIECBAgcCYgcJxR+xABAgQIECBAgAABAgQIECCwEhA4VrLeJUCAAAECBAgQIECAAAECBM4EBI4zah8iQIAAAQIECBAgQIAAAQIEVgICx0rWuwQIECBAgAABAgQIECBAgMCZgMBxRu1DBAgQIECAAAECBAgQIECAwEpA4FjJepcAAQIECBAgQIAAAQIECBA4ExA4zqh9iAABAgQIECBAgAABAgQIEFgJCBwrWe8SIECAAAECBAgQIECAAAECZwICxxm1DxEgQIAAAQIECBAgQIAAAQIrAYFjJetdAgQIECBAgAABAgQIECBA4ExA4Dij9iECBAgQIECAAAECBAgQIEBgJSBwrGS9S4AAAQIECBAgQIAAAQIECJwJCBxn1D5EgAABAgQIECBAgAABAgQIrAQEjpWsdwkQIECAAAECBAgQIECAAIEzAYHjjNqHCBAgQIAAAQIECBAgQIAAgZWAwLGS9S4BAgQIECBAgAABAgQIECBwJiBwnFH7EAECBAgQIECAAAECBAgQILASEDhWst4lQIAAAQIECBAgQIAAAQIEzgQEjjNqHyJAgAABAgQIECBAgAABAgRWAgLHSta7BAgQIECAAAECBAgQIECAwJmAwHFG7UMECBAgQIAAAQIECBAgQIDASkDgWMl6lwABAgQIECBAgAABAgQIEDgTEDjOqH2IAAECBAgQIECAAAECBAgQWAkIHCtZ7xIgQIAAAQIECBAgQIAAAQJnAgLHGbUPESBAgAABAgQIECBAgAABAisBgWMl610CBAgQIECAAAECBAgQIEDgTEDgOKP2IQIECBAgQIAAAQIECBAgQGAlIHCsZL1LgAABAgQIECBAgAABAgQInAkIHGfUPkSAAAECBAgQIECAAAECBAisBASOlax3CRAgQIAAAQIECBAgQIAAgTMBgeOM2ocIECBAgAABAgQIECBAgACBlYDAsZL1LgECBAgQIECAAAECBAgQIHAmIHCcUfsQAQIECBAgQIAAAQIECBAgsBIQOFay3iVAgAABAgQIECBAgAABAgTOBASOM2ofIkCAAAECBAgQIECAAAECBFYCAsdK1rsECBAgQIAAAQIECBAgQIDAmYDAcUbtQwQIECBAgAABAgQIECBAgMBKQOBYyXqXAAECBAgQIECAAAECBAgQOBMQOM6ofYgAAQIECBAgQIAAAQIECBBYCQgcK1nvEiBAgAABAgQIECBAgAABAmcCAscZtQ8RIECAAAECBAgQIECAAAECKwGBYyXrXQIECBAgQIAAAQIECBAgQOBMQOA4o/YhAgQIECBAgAABAgQIECBAYCUgcKxkvUuAAAECBAgQIECAAAECBAicCQgcZ9Q+RIAAAQIECBAgQIAAAQIECKwEBI6VrHcJECBAgAABAgQIECBAgACBMwGB44zahwgQIECAAAECBAgQIECAAIGVgMCxkvUuAQIECBAgQIAAAQIECBAgcCYgcJxR+xABAgQIECBAgAABAgQIECCwEhA4VrLeJUCAAAECBAgQIECAAAECBM4EBI4zah8iQIAAAQIECBAgQIAAAQIEVgICx0rWuwQIECBAgAABAgQIECBAgMCZgMBxRu1DBAgQIECAAAECBAgQIECAwEpA4FjJepcAAQIECBAgQIAAAQIECBA4ExA4zqh9iAABAgQIECBAgAABAgQIEFgJCBwrWe8SIECAAAECBAgQIECAAAECZwICxxm1DxEgQIAAAQIECBAgQIAAAQIrAYFjJetdAgQIECBAgAABAgQIECBA4ExA4Dij9iECBAgQIECAAAECBAgQIEBgJSBwrGS9S4AAAQIECBAgQIAAAQIECJwJCBxn1D5EgAABAgQIECBAgAABAgQIrAQEjpWsdwkQIECAAAECBAgQIECAAIEzAYHjjNqHCBAgQIAAAQIECBAgQIAAgZWAwLGS9S4BAgQIECBAgAABAgQIECBwJiBwnFH7EAECBAgQIECAAAECBAgQILASEDhWst4lQIAAAQIECBAgQIAAAQIEzgQEjjNqHyJAgAABAgQIECBAgAABAgRWAgLHSta7BAgQIECAAAECBAgQIECAwJmAwHFG7UMECBAgQIAAAQIECBAgQIDASkDgWMl6lwABAgQIECBAgAABAgQIEDgTEDjOqH2IAAECBAgQIECAAAECBAgQWAkIHCtZ7xIgQIAAAQIECBAgQIAAAQJnAgLHGbUPESBAgAABAgQIECBAgAABAisBgWMl610CBAgQIECAwP+2Y8c0AAAACMP8u0YFu2oAkp4jQIAAAQIECGQCAkdG7YgAAQIECBAgQIAAAQIECBB4CQgcL1m7BAgQIECAAAECBAgQIECAQCYgcGTUjggQIECAAAECBAgQIECAAIGXgMDxkrVLgAABAgQIECBAgAABAgQIZAICR0btiAABAgQIECBAgAABAgQIEHgJCBwvWbsECBAgQIAAAQIECBAgQIBAJiBwZNSOCBAgQIAAAQIECBAgQIAAgZeAwPGStUuAAAECBAgQIECAAAECBAhkAgJHRu2IAAECBAgQIECAAAECBAgQeAkIHC9ZuwQIECBAgAABAgQIECBAgEAmIHBk1I4IECBAgAABAgQIECBAgACBl4DA8ZK1S4AAAQIECBAgQIAAAQIECGQCAkdG7YgAAQIECBAgQIAAAQIECBB4CQgcL1m7BAgQIECAAAECBAgQIECAQCYgcGTUjggQIECAAAECBAgQIECAAIGXgMDxkrVLgAABAgQIECBAgAABAgQIZAICR0btiAABAgQIECBAgAABAgQIEHgJCBwvWbsECBAgQIAAAQIECBAgQIBAJiBwZNSOCBAgQIAAAQIECBAgQIAAgZeAwPGStUuAAAECBAgQIECAAAECBAhkAgJHRu2IAAECBAgQIECAAAECBAgQeAkIHC9ZuwQIECBAgAABAgQIECBAgEAmIHBk1I4IECBAgAABAgQIECBAgACBl4DA8ZK1S4AAAQIECBAgQIAAAQIECGQCAkdG7YgAAQIECBAgQIAAAQIECBB4CQgcL1m7BAgQIECAAAECBAgQIECAQCYgcGTUjggQIECAAAECBAgQIECAAIGXgMDxkrVLgAABAgQIECBAgAABAgQIZAICR0btiAABAgQIECBAgAABAgQIEHgJCBwvWbsECBAgQIAAAQIECBAgQIBAJiBwZNSOCBAgQIAAAQIECBAgQIAAgZeAwPGStUuAAAECBAgQIECAAAECBAhkAgJHRu2IAAECBAgQIECAAAECBAgQeAkIHC9ZuwQIECBAgAABAgQIECBAgEAmIHBk1I4IECBAgAABAgQIECBAgACBl4DA8ZK1S4AAAQIECBAgQIAAAQIECGQCAkdG7YgAAQIECBAgQIAAAQIECBB4CQgcL1m7BAgQIECAAAECBAgQIECAQCYgcGTUjggQIECAAAECBAgQIECAAIGXgMDxkrVLgAABAgQIECBAgAABAgQIZAICR0btiAABAgQIECBAgAABAgQIEHgJCBwvWbsECBAgQIAAAQIECBAgQIBAJiBwZNSOCBAgQIAAAQIECBAgQIAAgZeAwPGStUuAAAECBAgQIECAAAECBAhkAgJHRu2IAAECBAgQIECAAAECBAgQeAkIHC9ZuwQIECBAgAABAgQIECBAgEAmIHBk1I4IECBAgAABAgQIECBAgACBl4DA8ZK1S4AAAQIECBAgQIAAAQIECGQCAkdG7YgAAQIECBAgQIAAAQIECBB4CQgcL1m7BAgQIECAAAECBAgQIECAQCYgcGTUjggQIECAAAECBAgQIECAAIGXgMDxkrVLgAABAgQIECBAgAABAgQIZAICR0btiAABAgQIECBAgAABAgQIEHgJCBwvWbsECBAgQIAAAQIECBAgQIBAJiBwZNSOCBAgQIAAAQIECBAgQIAAgZeAwPGStUuAAAECBAgQIECAAAECBAhkAgJHRu2IAAECBAgQIECAAAECBAgQeAkIHC9ZuwQIECBAgAABAgQIECBAgEAmIHBk1I4IECBAgAABAgQIECBAgACBl4DA8ZK1S4AAAQIECBAgQIAAAQIECGQCAkdG7YgAAQIECBAgQIAAAQIECBB4CQgcL1m7BAgQIECAAAECBAgQIECAQCYgcGTUjggQIECAAAECBAgQIECAAIGXgMDxkrVLgAABAgQIECBAgAABAgQIZAICR0btiAABAgQIECBAgAABAgQIEHgJCBwvWbsECBAgQIAAAQIECBAgQIBAJiBwZNSOCBAgQIAAAQIECBAgQIAAgZeAwPGStUuAAAECBAgQIECAAAECBAhkAgJHRu2IAAECBAgQIECAAAECBAgQeAkIHC9ZuwQIECBAgAABAgQIECBAgEAmIHBk1I4IECBAgAABAgQIECBAgACBl4DA8ZK1S4AAAQIECBAgQIAAAQIECGQCAkdG7YgAAQIECBAgQIAAAQIECBB4CQgcL1m7BAgQIECAAAECBAgQIECAQCYgcGTUjggQIECAAAECBAgQIECAAIGXgMDxkrVLgAABAgQIECBAgAABAgQIZAICR0btiAABAgQIECBAgAABAgQIEHgJCBwvWbsECBAgQIAAAQIECBAgQIBAJiBwZNSOCBAgQIAAAQIECBAgQIAAgZeAwPGStUuAAAECBAgQIECAAAECBAhkAgJHRu2IAAECBAgQIECAAAECBAgQeAkIHC9ZuwQIECBAgAABAgQIECBAgEAmIHBk1I4IECBAgAABAgQIECBAgACBl4DA8ZK1S4AAAQIECBAgQIAAAQIECGQCAkdG7YgAAQIECBAgQIAAAQIECBB4CQgcL1m7BAgQIECAAAECBAgQIECAQCYgcGTUjggQIECAAAECBAgQIECAAIGXgMDxkrVLgAABAgQIECBAgAABAgQIZAICR0btiAABAgQIECBAgAABAgQIEHgJCBwvWbsECBAgQIAAAQIECBAgQIBAJiBwZNSOCBAgQIAAAQIECBAgQIAAgZeAwPGStUuAAAECBAgQIECAAAECBAhkAgJHRu2IAAECBAgQIECAAAECBAgQeAkIHC9ZuwQIECBAgAABAgQIECBAgEAmIHBk1I4IECBAgAABAgQIECBAgACBl4DA8ZK1S4AAAQIECBAgQIAAAQIECGQCAkdG7YgAAQIECBAgQIAAAQIECBB4CQgcL1m7BAgQIECAAAECBAgQIECAQCYgcGTUjggQIECAAAECBAgQIECAAIGXgMDxkrVLgAABAgQIECBAgAABAgQIZAICR0btiAABAgQIECBAgAABAgQIEHgJCBwvWbsECBAgQIAAAQIECBAgQIBAJiBwZNSOCBAgQIAAAQIECBAgQIAAgZeAwPGStUuAAAECBAgQIECAAAECBAhkAgJHRu2IAAECBAgQIECAAAECBAgQeAkIHC9ZuwQIECBAgAABAgQIECBAgEAmIHBk1I4IECBAgAABAgQIECBAgACBl4DA8ZK1S4AAAQIECBAgQIAAAQIECGQCAkdG7YgAAQIECBAgQIAAAQIECBB4CQgcL1m7BAgQIECAAAECBAgQIECAQCYgcGTUjggQIECAAAECBAgQIECAAIGXgMDxkrVLgAABAgQIECBAgAABAgQIZAICR0btiAABAgQIECBAgAABAgQIEHgJCBwvWbsECBAgQIAAAQIECBAgQIBAJiBwZNSOCBAgQIAAAQIECBAgQIAAgZeAwPGStUuAAAECBAgQIECAAAECBAhkAgJHRu2IAAECBAgQIECAAAECBAgQeAkIHC9ZuwQIECBAgAABAgQIECBAgEAmIHBk1I4IECBAgAABAgQIECBAgACBl4DA8ZK1S4AAAQIECBAgQIAAAQIECGQCAkdG7YgAAQIECBAgQIAAAQIECBB4CQgcL1m7BAgQIECAAAECBAgQIECAQCYgcGTUjggQIECAAAECBAgQIECAAIGXgMDxkrVLgAABAgQIECBAgAABAgQIZAICR0btiAABAgQIECBAgAABAgQIEHgJCBwvWbsECBAgQIAAAQIECBAgQIBAJiBwZNSOCBAgQIAAAQIECBAgQIAAgZeAwPGStUuAAAECBAgQIECAAAECBAhkAgJHRu2IAAECBAgQIECAAAECBAgQeAkIHC9ZuwQIECBAgAABAgQIECBAgEAmIHBk1I4IECBAgAABAgQIECBAgACBl4DA8ZK1S4AAAQIECBAgQIAAAQIECGQCAkdG7YgAAQIECBAgQIAAAQIECBB4CQgcL1m7BAgQIECAAAECBAgQIECAQCYgcGTUjggQIECAAAECBAgQIECAAIGXgMDxkrVLgAABAgQIECBAgAABAgQIZAICR0btiAABAgQIECBAgAABAgQIEHgJCBwvWbsECBAgQIAAAQIECBAgQIBAJiBwZNSOCBAgQIAAAQIECBAgQIAAgZeAwPGStUuAAAECBAgQIECAAAECBAhkAgJHRu2IAAECBAgQIECAAAECBAgQeAkIHC9ZuwQIECBAgAABAgQIECBAgEAmIHBk1I4IECBAgAABAgQIECBAgACBl8AADOloOb9ayHQAAAAASUVORK5CYII=","e":1},{"id":"image_5","w":1080,"h":1080,"u":"","p":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABDgAAAQ4CAYAAADsEGyPAAAgAElEQVR4Xuzd0Y3n91WH4f96CkBUQDqIr1ceJXSQDggdQAehg1ABpgLcAYvH90AH0AGuYNFKFhcRIocdYz7v+LHku9+Mv+c55+rV2n738BcBAgQIECBAgAABAgQIECBAIC7wLv5+zydAgAABAgQIECBAgAABAgQIPAQOR0CAAAECBAgQIECAAAECBAjkBQSO/AoNQIAAAQIECBAgQIAAAQIECAgcboAAAQIECBAgQIAAAQIECBDICwgc+RUagAABAgQIECBAgAABAgQIEBA43AABAgQIECBAgAABAgQIECCQFxA48is0AAECBAgQIECAAAECBAgQICBwuAECBAgQIECAAAECBAgQIEAgLyBw5FdoAAIECBAgQIAAAQIECBAgQEDgcAMECBAgQIAAAQIECBAgQIBAXkDgyK/QAAQIECBAgAABAgQIECBAgIDA4QYIECBAgAABAgQIECBAgACBvIDAkV+hAQgQIECAAAECBAgQIECAAAGBww0QIECAAAECBAgQIECAAAECeQGBI79CAxAgQIAAAQIECBAgQIAAAQIChxsgQIAAAQIECBAgQIAAAQIE8gICR36FBiBAgAABAgQIECBAgAABAgQEDjdAgAABAgQIECBAgAABAgQI5AUEjvwKDUCAAAECBAgQIECAAAECBAgIHG6AAAECBAgQIECAAAECBAgQyAsIHPkVGoAAAQIECBAgQIAAAQIECBAQONwAAQIECBAgQIAAAQIECBAgkBcQOPIrNAABAgQIECBAgAABAgQIECAgcLgBAgQIECBAgAABAgQIECBAIC8gcORXaAACBAgQIECAAAECBAgQIEBA4HADBAgQIECAAAECBAgQIECAQF5A4Miv0AAECBAgQIAAAQIECBAgQICAwOEGCBAgQIAAAQIECBAgQIAAgbyAwJFfoQEIECBAgAABAgQIECBAgAABgcMNECBAgAABAgQIECBAgAABAnkBgSO/QgMQIECAAAECBAgQIECAAAECAocbIECAAAECBAgQIECAAAECBPICAkd+hQYgQIAAAQIECBAgQIAAAQIEBA43QIAAAQIECBAgQIAAAQIECOQFBI78Cg1AgAABAgQIECBAgAABAgQICBxugAABAgQIECBAgAABAgQIEMgLCBz5FRqAAAECBAgQIECAAAECBAgQEDjcAAECBAgQIECAAAECBAgQIJAXEDjyKzQAAQIECBAgQIAAAQIECBAgIHC4AQIECBAgQIAAAQIECBAgQCAvIHDkV2gAAgQIECBAgAABAgQIECBAQOBwAwQIECBAgAABAgQIECBAgEBeQODIr9AABAgQIECAAAECBAgQIECAgMDhBggQIECAAAECBAgQIECAAIG8gMCRX6EBCBAgQIAAAQIECBAgQIAAAYHDDRAgQIAAAQIECBAgQIAAAQJ5AYEjv0IDECBAgAABAgQIECBAgAABAgKHGyBAgAABAgQIECBAgAABAgTyAgJHfoUGIECAAAECBAgQIECAAAECBAQON0CAAAECBAgQIECAAAECBAjkBQSO/AoNQIAAAQIECBAgQIAAAQIECAgcboAAAQIECBAgQIAAAQIECBDICwgc+RUagAABAgQIECBAgAABAgQIEBA43AABAgQIECBAgAABAgQIECCQFxA48is0AAECBAgQIECAAAECBAgQICBwuAECBAgQIECAAAECBAgQIEAgLyBw5FdoAAIECBAgQIAAAQIECBAgQEDgcAMECBAgQIAAAQIECBAgQIBAXkDgyK/QAAQIECBAgAABAgQIECBAgIDA4QYIECBAgAABAgQIECBAgACBvIDAkV+hAQgQIECAAAECBAgQIECAAAGBww0QIECAAAECBAgQIECAAAECeQGBI79CAxAgQIAAAQIECBAgQIAAAQIChxsgQIAAAQIECBAgQIAAAQIE8gICR36FBiBAgAABAgQIECBAgAABAgQEDjdAgAABAgQIECBAgAABAgQI5AUEjvwKDUCAAAECBAgQIECAAAECBAgIHG6AAAECBAgQIECAAAECBAgQyAsIHPkVGoAAAQIECBAgQIAAAQIECBAQONwAAQIECBAgQIAAAQIECBAgkBcQOPIrNAABAgQIECBAgAABAgQIECAgcLgBAgQIECBAgAABAgQIECBAIC8gcORXaAACBAgQIECAAAECBAgQIEBA4HADBAgQIECAAAECBAgQIECAQF5A4Miv0AAECBAgQIAAAQIECBAgQICAwOEGCBAgQIAAAQIECBAgQIAAgbyAwJFfoQEIECBAgAABAgQIECBAgAABgcMNECBAgAABAgQIECBAgAABAnkBgSO/QgMQIECAAAECBAgQIECAAAECAocbIECAAAECBAgQIECAAAECBPICAkd+hQYgQIAAAQIECBAgQIAAAQIEBA43QIAAAQIECBAgQIAAAQIECOQFBI78Cg1AgAABAgQIECBAgAABAgQICBxugAABAgQIECBAgAABAgQIEMgLCBz5FRqAAAECBAgQIECAAAECBAgQEDjcAAECBAgQIECAAAECBAgQIJAXEDjyKzQAAQIECBAgQIAAAQIECBAgIHC4AQIECBAgQIAAAQIECBAgQCAvIHDkV2gAAgQIECBAgAABAgQIECBAQOBwAwQIECBAgAABAgQIECBAgEBeQODIr9AABAgQIECAAAECBAgQIECAgMDhBggQIECAAAECBAgQIECAAIG8gMCRX6EBCBAgQIAAAQIECBAgQIAAAYHDDRAgQIAAAQIECBAgQIAAAQJ5AYEjv0IDECBAgAABAgQIECBAgAABAgKHGyBAgAABAgQIECBAgAABAgTyAgJHfoUGIECAAAECBAgQIECAAAECBAQON0CAAAECBAgQIECAAAECBAjkBQSO/AoNQIAAAQIECBAgQIAAAQIECAgcboAAAQIECBAgQIAAAQIECBDICwgc+RUagAABAgQIECBAgAABAgQIEBA43AABAgQIECBAgAABAgQIECCQFxA48is0AAECBAgQIECAAAECBAgQICBwuAECBAgQIECAAAECBAgQIEAgLyBw5FdoAAIECBAgQIAAAQIECBAgQEDgcAMECBAgQIAAAQIECBAgQIBAXkDgyK/QAAQIECBAgAABAgQIECBAgIDA4QYIECBAgAABAgQIECBAgACBvIDAkV+hAQgQIECAAAECBAgQIECAAAGBww0QIECAAAECBAgQIECAAAECeQGBI79CAxAgQIAAAQIECBAgQIAAAQIChxsgQIAAAQIECBAgQIAAAQIE8gICR36FBiBAgAABAgQIECBAgAABAgQEDjdAgAABAgQIECBAgAABAgQI5AUEjvwKDUCAAAECBAgQIECAAAECBAgIHG6AAAECBAgQIECAAAECBAgQyAsIHPkVGoAAAQIECBAgQIAAAQIECBAQONwAAQIECBAgQIAAAQIECBAgkBcQOPIrNAABAgQIECBAgAABAgQIECAgcLgBAgQIECBAgAABAgQIECBAIC8gcORXaAACBAgQIECAAAECBAgQIEBA4HADBAgQIECAAAECBAgQIECAQF5A4Miv0AAECBAgQIAAAQIECBAgQICAwOEGCBAgQIAAAQIECBAgQIAAgbyAwJFfoQEIECBAgAABAgQIECBAgAABgcMNECBAgAABAgQIECBAgAABAnkBgSO/QgMQIECAAAECBAgQIECAAAECAocbIECAAAECBAgQIECAAAECBPICAkd+hQYgQIAAAQIECBAgQIAAAQIEBA43QIAAAQIECBAgQIAAAQIECOQFBI78Cg1AgAABAgQIECBAgAABAgQICBxugAABAgQIECBAgAABAgQIEMgLCBz5FRqAAAECBAgQIECAAAECBAgQEDjcAAECBAgQIECAAAECBAgQIJAXEDjyKzQAAQIECBAgQIAAAQIECBAgIHC4AQIECBAgQIAAAQIECBAgQCAvIHDkV2gAAgQIECBAgAABAgQIECBAQOBwAwQIECBAgAABAgQIECBAgEBeQODIr9AABAgQIECAAAECBAgQIECAgMDhBggQIECAAAECBAgQIECAAIG8gMCRX6EBCBAgQIAAAQIECBAgQIAAAYHDDRAgQIAAAQIECBAgQIAAAQJ5AYEjv0IDECBAgAABAgQIECBAgAABAgKHGyBAgAABAgQIECBAgAABAgTyAgJHfoUGIECAAAECBAgQIECAAAECBAQON0CAAAECBAgQIECAAAECBAjkBQSO/AoNQIAAAQIECBAgQIAAAQIECAgcboAAAQIECBAgQIAAAQIECBDICwgc+RUagAABAgQIECBAgAABAgQIEBA43AABAgQIECBAgAABAgQIECCQFxA48is0AAECBAgQIECAAAECBAgQICBwuAECBAgQIECAAAECBAgQIEAgLyBw5FdoAAIECBAgQIAAAQIECBAgQEDgcAMECBAgQIAAAQIECBAgQIBAXkDgyK/QAAQIECBAgAABAgQIECBAgIDA4QYIECBAgAABAgQIECBAgACBvIDAkV+hAQgQIECAAAECBAgQIECAAAGBww0QIECAAAECBAgQIECAAAECeQGBI79CAxAgQIAAAQIECBAgQIAAAQIChxsgQIAAAQIECBAgQIAAAQIE8gICR36FBiBAgAABAgQIECBAgAABAgQEDjdAgAABAgQIECBAgAABAgQI5AUEjvwKDUCAAAECBAgQIECAAAECBAgIHG6AAAECBAgQIECAAAECBAgQyAsIHPkVGoAAAQIECBAgQIAAAQIECBAQONwAAQIECBAgQIAAAQIECBAgkBcQOPIrNAABAgQIECBAgAABAgQIECAgcLgBAgQIECBAgAABAgQIECBAIC8gcORXaAACBAgQIECAAAECBAgQIEBA4HADBAgQIECAAAECBAgQIECAQF5A4Miv0AAECBAgQIAAAQIECBAgQICAwOEGCBAgQIAAAQIECBAgQIAAgbyAwJFfoQEIECBAgAABAgQIECBAgAABgcMNECBAgAABAgQIECBAgAABAnkBgSO/QgMQIECAAAECBAgQIECAAAECAocbIECAAAECBAgQIECAAAECBPICAkd+hQYgQIAAAQIECBAgQIAAAQIEBA43QIAAAQIECBAgQIAAAQIECOQFBI78Cg1AgAABAgQIECBAgAABAgQICBxugAABAgQIECBAgAABAgQIEMgLCBz5FRqAAAECBAgQIECAAAECBAgQEDjcAAECBAgQIECAAAECBAgQIJAXEDjyKzQAAQIECBAgQIAAAQIECBAgIHC4AQIECBAgQIAAAQIECBAgQCAvIHDkV2gAAgQIECBAgAABAgQIECBAQOBwAwQIECBAgAABAgQIECBAgEBeQODIr9AABAgQIECAAAECBAgQIECAgMDhBggQIECAAAECBAgQIECAAIG8gMCRX6EBCBAgQIAAAQIECBAgQIAAAYHDDRAgQIAAAQIECBAgQIAAAQJ5AYEjv0IDECBAgAABAgQIECBAgAABAgKHGyBAgAABAgQIECBAgAABAgTyAgJHfoUGIECAAAECBAgQIECAAAECBAQON0CAAAECBAgQIECAAAECBAjkBQSO/AoNQIAAAQIECBAgQIAAAQIECAgcboAAAQIECBAgQIAAAQIECBDICwgc+RUagAABAgQIECBAgAABAgQIEBA43AABAgQIECBAgAABAgQIECCQFxA48is0AAECBAgQIECAAAECBAgQICBwuAECBAgQIECAAAECBAgQIEAgLyBw5FdoAAIECBAgQIAAAQIECBAgQEDgcAMECBAgQIAAAQIECBAgQIBAXkDgyK/QAAQIECBAgAABAgQIECBAgIDA4QYIECBAgAABAgQIECBAgACBvIDAkV+hAQgQIECAAAECBAgQIECAAAGBww0QIECAAAECBAgQIECAAAECeQGBI79CAxAgQIAAAQIECBAgQIAAAQIChxsgQIAAAQIECBAgQIAAAQIE8gICR36FBiBAgAABAgQIECBAgAABAgQEDjdAgAABAgQIECBAgAABAgQI5AUEjvwKDUCAAAECBAgQIECAAAECBAgIHG6AAAECBAgQIECAAAECBAgQyAsIHPkVGoAAAQIECBAgQIAAAQIECBAQONwAAQIECBAgQIAAAQIECBAgkBcQOPIrNAABAgQIECBAgAABAgQIECAgcLgBAgQIECBAgAABAgQIECBAIC8gcORXaAACBAgQIECAAAECBAgQIEBA4HADBAgQIECAAAECBAgQIECAQF5A4Miv0AAECBAgQIAAAQIECBAgQICAwOEGCBAgQIAAAQIECBAgQIAAgbyAwJFfoQEIECBAgAABAgQIECBAgAABgcMNECBAgAABAgQIECBAgAABAnkBgSO/QgMQIECAAAECBAgQIECAAAECAocbIECAAAECBAgQIECAAAECBPICAkd+hQYgQIAAAQIECBAgQIAAAQIEBA43QIAAAQIECBAgQIAAAQIECOQFBI78Cg1AgAABAgQIECBAgAABAgQICBxugAABAgQIECBAgAABAgQIEMgLCBz5FRqAAAECBAgQIECAAAECBAgQEDjcAAECBAgQIECAAAECBAgQIJAXEDjyKzQAAQIECBAgQIAAAQIECBAgIHC4AQIECBAgQIAAAQIECBAgQCAvIHDkV2gAAgQIECBAgAABAgQIECBAQOBwAwQIECBAgAABAgQIECBAgEBeQODIr9AABAgQIECAAAECBAgQIECAgMDhBggQIECAAAECBAgQIECAAIG8gMCRX6EBCBAgQIAAAQIECBAgQIAAAYHDDRAgQIAAAQIECBAgQIAAAQJ5AYEjv0IDECBAgAABAgQIECBAgAABAgKHGyBAgAABAgQIECBAgAABAgTyAgJHfoUGIECAAAECBAgQIECAAAECBAQON0CAAAECBAgQIECAAAECBAjkBQSO/AoNQIAAAQIECBAgQIAAAQIECAgcboAAAQIECBAgQIAAAQIECBDICwgc+RUagAABAgQIECBAgAABAgQIEBA43AABAgQIECBAgAABAgQIECCQFxA48is0AAECBAgQIECAAAECBAgQICBwuAECBAgQIECAAAECBAgQIEAgLyBw5FdoAAIECBAgQIAAAQIECBAgQEDgcAMECBAgQIAAAQIECBAgQIBAXkDgyK/QAAQIECBAgAABAgQIECBAgIDA4QYIECBAgAABAgQIECBAgACBvIDAkV+hAQgQIECAAAECBAgQIECAAAGBww0QIECAAAECBAgQIECAAAECeQGBI79CAxAgQIAAAQIECBAgQIAAAQIChxsgQIAAAQIECBAgQIAAAQIE8gICR36FBiBAgAABAgQIECBAgAABAgQEDjdAgAABAgQIECBAgAABAgQI5AUEjvwKDUCAAAECBAgQIECAAAECBAgIHG6AAAECBAgQIECAAAECBAgQyAsIHPkVGoAAAQIECBAgQIAAAQIECBAQONwAAQIECBAgQIAAAQIECBAgkBcQOPIrNAABAgQIECBAgAABAgQIECAgcLgBAgQIECBAgAABAgQIECBAIC8gcORXaAACBAgQIECAAAECBAgQIEBA4HADBAgQIECAAAECBAgQIECAQF5A4Miv0AAECBAgQIAAAQIECBAgQICAwOEGCBAgQIAAAQIECBAgQIAAgbyAwJFfoQEIECBAgAABAgQIECBAgAABgcMNECBAgAABAgQIECBAgAABAnkBgSO/QgMQIECAAAECBAgQIECAAAECAocbIECAAAECBAgQIECAAAECBPICAkd+hQYgQIAAAQIECBAgQIAAAQIEBA43QIAAAQIECBAgQIAAAQIECOQFBI78Cg1AgAABAgQIECBAgAABAgQICBxugAABAgQIECBAgAABAgQIEMgLCBz5FRqAAAECBAgQIECAAAECBAgQEDjcAAECBAgQIECAAAECBAgQIJAXEDjyKzQAAQIECBAgQIAAAQIECBAgIHC4AQIECBAgQIAAAQIECBAgQCAvIHDkV2gAAgQIECBAgAABAgQIECBAQOBwAwQIECBAgAABAgQIECBAgEBeQODIr9AABAgQIECAAAECBAgQIECAgMDhBggQIECAAAECBAgQIECAAIG8gMCRX6EBCBAgQIAAAQIECBAgQIAAAYHDDRAgQIAAAQIECBAgQIAAAQJ5AYEjv0IDECBAgAABAgQIECBAgAABAgKHGyBAgAABAgQIECBAgAABAgTyAgJHfoUGIECAAAECBAgQIECAAAECBAQON0CAAAECBAgQIECAAAECBAjkBQSO/AoNQIAAAQIECBAgQIAAAQIECAgcboAAAQIECBAgQIAAAQIECBDICwgc+RUagAABAgQIECBAgAABAgQIEBA43AABAgQIECBAgAABAgQIECCQFxA48is0AAECBAgQIECAAAECBAgQICBwuAECBAgQIECAAAECBAgQIEAgLyBw5FdoAAIECBAgQIAAAQIECBAgQEDgcAMECBAgQIAAAQIECBAgQIBAXkDgyK/QAAQIECBAgAABAgQIECBAgIDA4QYIECBAgAABAgQIECBAgACBvIDAkV+hAQgQIECAAAECBAgQIECAAAGBww0QIECAAAECBAgQIECAAAECeQGBI79CAxAgQIAAAQIECBAgQIAAAQIChxsgQIAAAQIECBAgQIAAAQIE8gICR36FBiBAgAABAgQIECBAgAABAgQEDjdAgAABAgQIECBAgAABAgQI5AUEjvwKDUCAAAECBAgQIECAAAECBAgIHG6AAAECBAgQIECAAAECBAgQyAsIHPkVGoAAAQIECBAgQIAAAQIECBAQONwAAQIECBAgQIAAAQIECBAgkBcQOPIrNAABAgQIECBAgAABAgQIECAgcLgBAgQIECBAgAABAgQIECBAIC8gcORXaAACBAgQIECAAAECBAgQIEBA4HADBAgQIECAAAECBAgQIECAQF5A4Miv0AAECBAgQIAAAQIECBAgQICAwOEGCBAgQIAAAQIECBAgQIAAgbyAwJFfoQEIECBAgAABAgQIECBAgAABgcMNECBAgAABAgQIECBAgAABAnkBgSO/QgMQIECAAAECBAgQIECAAAECAocbIECAAAECBAgQIECAAAECBPICAkd+hQYgQIAAAQIECBAgQIAAAQIEBA43QIAAAQIECBAgQIAAAQIECOQFBI78Cg1AgAABAgQIECBAgAABAgQICBxugAABAgQIECBAgAABAgQIEMgLCBz5FRqAAAECBAgQIECAAAECBAgQEDjcAAECBAgQIECAAAECBAgQIJAXEDjyKzQAAQIECBAgQIAAAQIECBAgIHC4AQIECBAgQIAAAQIECBAgQCAvIHDkV2gAAgQIECBAgAABAgQIECBAQOBwAwQIECBAgAABAgQIECBAgEBeQODIr9AABAgQIECAAAECBAgQIECAgMDhBggQIECAAAECBAgQIECAAIG8gMCRX6EBCBAgQIAAAQIECBAgQIAAAYHDDRAgQIAAAQIECBAgQIAAAQJ5AYEjv0IDECBAgAABAgQIECBAgAABAgKHGyBAgAABAgQIECBAgAABAgTyAgJHfoUGIECAAAECBAgQIECAAAECBAQON0CAAAECBAgQIECAAAECBAjkBQSO/AoNQIAAAQIECBAgQIAAAQIECAgcboAAAQIECBAgQIAAAQIECBDICwgc+RUagAABAgQIECBAgAABAgQIEBA43AABAgQIECBAgAABAgQIECCQFxA48is0AAECBAgQIECAAAECBAgQICBwuAECBAgQIECAAAECBAgQIEAgLyBw5FdoAAIECBAgQIAAAQIECBAgQEDgcAMECBAgQIAAAQIECBAgQIBAXkDgyK/QAAQIECBAgAABAgQIECBAgIDA4QYIECBAgAABAgQIECBAgACBvIDAkV+hAQgQIECAAAECBAgQIECAAAGBww0QIECAAAECBAgQIECAAAECeQGBI79CAxAgQIAAAQIECBAgQIAAAQIChxsgQIAAAQIECBAgQIAAAQIE8gICR36FBiBAgAABAgQIECBAgAABAgQEDjdAgAABAgQIECBAgAABAgQI5AUEjvwKDUCAAAECBAgQIECAAAECBAgIHG6AAAECBAgQIECAAAECBAgQyAsIHPkVGoAAAQIECBAgQIAAAQIECBAQONwAAQIECBAgQIAAAQIECBAgkBcQOPIrNAABAgQIECBAgAABAgQIECAgcLgBAgQIECBAgAABAgQIECBAIC8gcORXaAACBAgQIECAAAECBAgQIEBA4HADBAgQIECAAAECBAgQIECAQF5A4Miv0AAECBAgQIAAAQIECBAgQICAwOEGCBAgQIAAAQIECBAgQIAAgbyAwJFfoQEIECBAgAABAgQIECBAgAABgcMNECBAgAABAgQIECBAgAABAnkBgSO/QgMQIECAAAECBAgQIECAAAECAocbIECAAAECBAgQIECAAAECBPICAkd+hQYgQIAAAQIECBAgQIAAAQIEBA43QIAAAQIECBAgQIAAAQIECOQFBI78Cg1AgAABAgQIECBAgAABAgQICBxugAABAgQIECBAgAABAgQIEMgLCBz5FRqAAAECBAgQIECAAAECBAgQEDjcAAECBAgQIECAAAECBAgQIJAXEDjyKzQAAQIECBAgQIAAAQIECBAgIHC4AQIECBAgQIAAAQIECBAgQCAvIHDkV2gAAgQIECBAgAABAgQIECBAQOBwAwQIECBAgAABAgQIECBAgEBeQODIr9AABAgQIECAAAECBAgQIECAgMDhBggQIECAAAECBAgQIECAAIG8gMCRX6EBCBAgQIAAAQIECBAgQIAAAYHDDRAgQIAAAQIECBAgQIAAAQJ5AYEjv0IDECBAgAABAgQIECBAgAABAgKHGyBAgAABAgQIECBAgAABAgTyAgJHfoUGIECAAPAKgAUAACAASURBVAECBAgQIECAAAECBAQON0CAAAECBAgQIECAAAECBAjkBQSO/AoNQIAAAQIECBAgQIAAAQIECAgcboAAAQIECBAgQIAAAQIECBDICwgc+RUagAABAgQIECBAgAABAgQIEBA43AABAgQIECBAgAABAgQIECCQFxA48is0AAECBAgQIECAAAECBAgQICBwuAECBAgQIECAAAECBAgQIEAgLyBw5FdoAAIECBAgQIAAAQIECBAgQEDgcAMECBAgQIAAAQIECBAgQIBAXkDgyK/QAAQIECBAgAABAgQIECBAgIDA4QYIECBAgAABAgQIECBAgACBvIDAkV+hAQgQIECAAAECBAgQIECAAAGBww0QIECAAAECBAgQIECAAAECeQGBI79CAxAgQIAAAQIECBAgQIAAAQIChxsgQIAAAQIECBAgQIAAAQIE8gICR36FBiBAgAABAgQIECBAgAABAgQEDjdAgAABAgQIECBAgAABAgQI5AUEjvwKDUCAAAECBAgQIECAAAECBAgIHG6AAAECBAgQIECAAAECBAgQyAsIHPkVGoAAAQIECBAgQIAAAQIECBAQONwAAQIECBAgQIAAAQIECBAgkBcQOPIrNAABAgQIECBAgAABAgQIECAgcLgBAgQIECBAgAABAgQIECBAIC8gcORXaAACBAgQIECAAAECBAgQIEBA4HADBAgQIECAAAECBAgQIECAQF5A4Miv0AAECBAgQIAAAQIECBAgQICAwOEGCBAgQIAAAQIECBAgQIAAgbyAwJFfoQEIECBAgAABAgQIECBAgAABgcMNECBAgAABAgQIECBAgAABAnkBgSO/QgMQIECAAAECBAgQIECAAAECAocbIECAAAECBAgQIECAAAECBPICAkd+hQYgQIAAAQIECBAgQIAAAQIEBA43QIAAAQIECBAgQIAAAQIECOQFBI78Cg1AgAABAgQIECBAgAABAgQICBxugAABAgQIECBAgAABAgQIEMgLCBz5FRqAAAECBAgQIECAAAECBAgQEDjcAAECBAgQIECAAAECBAgQIJAXEDjyKzQAAQIECBAgQIAAAQIECBAgIHC4AQIECBAgQIAAAQIECBAgQCAvIHDkV2gAAgQIECBAgAABAgQIECBAQOBwAwQIECBAgAABAgQIECBAgEBeQODIr9AABAgQIECAAAECBAgQIECAgMDhBggQIECAAAECBAgQIECAAIG8gMCRX6EBCBAgQIAAAQIECBAgQIAAAYHDDRAgQIAAAQIECBAgQIAAAQJ5AYEjv0IDECBAgAABAgQIECBAgAABAgKHGyBAgAABAgQIECBAgAABAgTyAgJHfoUGIECAAAECBAgQIECAAAECBAQON0CAAAECBAgQIECAAAECBAjkBQSO/AoNQIAAAQIECBAgQIAAAQIECAgcboAAAQIECBAgQIAAAQIECBDICwgc+RUagAABAgQIECBAgAABAgQIEBA43AABAgQIECBAgAABAgQIECCQFxA48is0AAECBAgQIECAAAECBAgQICBwuAECBAgQIECAAAECBAgQIEAgLyBw5FdoAAIECBAgQIAAAQIECBAgQEDgcAMECBAgQIAAAQIECBAgQIBAXkDgyK/QAAQIECBAgAABAgQIECBAgIDA4QYIECBAgAABAgQIECBAgACBvIDAkV+hAQgQIECAAAECBAgQIECAAAGBww0QIECAAAECBAgQIECAAAECeQGBI79CAxAgQIAAAQIECBAgQIAAAQIChxsgQIAAAQIECBAgQIAAAQIE8gICR36FBiBAgAABAgQIECBAgAABAgQEDjdAgAABAgQIECBAgAABAgQI5AUEjvwKDUCAAAECBAgQIECAAAECBAgIHG6AAAECBAgQIECAAAECBAgQyAsIHPkVGoAAAQIECBAgQIAAAQIECBAQONwAAQIECBAgQIAAAQIECBAgkBcQOPIrNAABAgQIECBAgAABAgQIECAgcLgBAgQIECBAgAABAgQIECBAIC8gcORXaAACBAgQIECAAAECBAgQIEBA4HADBAgQIECAAAECBAgQIECAQF5A4Miv0AAECBAgQIAAAQIECBAgQICAwOEGCBAgQIAAAQIECBAgQIAAgbyAwJFfoQEIECBAgAABAgQIECBAgAABgcMNECBAgAABAgQIECBAgAABAnkBgSO/QgMQIECAAAECBAgQIECAAAECAocbIECAAAECBAgQIECAAAECBPICAkd+hQYgQIAAAQIECBAgQIAAAQIEBA43QIAAAQIECBAgQIAAAQIECOQFBI78Cg1AgAABAgQIECBAgAABAgQICBxugAABAgQIECBAgAABAgQIEMgLCBz5FRqAAAECBAgQIECAAAECBAgQEDjcAAECBAgQIECAAAECBAgQIJAXEDjyKzQAAQIECBAgQIAAAQIECBAgIHC4AQIECBAgQIAAAQIECBAgQCAvIHDkV2gAAgQIECBAgAABAgQIECBAQOBwAwQIECBAgAABAgQIECBAgEBeQODIr9AABAgQIECAAAECBAgQIECAgMDhBggQIECAAAECBAgQIECAAIG8gMCRX6EBCBAgQIAAAQIECBAgQIAAAYHDDRAgQIAAAQIECBAgQIAAAQJ5AYEjv0IDECBAgAABAgQIECBAgAABAgKHGyBAgAABAgQIECBAgAABAgTyAgJHfoUGIECAAAECBAgQIECAAAECBAQON0CAAAECBAgQIECAAAECBAjkBQSO/AoNQIAAAQIECBAgQIAAAQIECAgcboAAAQIECBAgQIAAAQIECBDICwgc+RUagAABAgQIECBAgAABAgQIEBA43AABAgQIECBAgAABAgQIECCQFxA48is0AAECBAgQIECAAAECBAgQICBwuAECBAgQIECAAAECBAgQIEAgLyBw5FdoAAIECBAgQIAAAQIECBAgQEDgcAMECBAgQIAAAQIECBAgQIBAXkDgyK/QAAQIECBAgAABAgQIECBAgIDA4QYIECBAgAABAgQIECBAgACBvIDAkV+hAQgQIECAAAECBAgQIECAAAGBww0QIECAAAECBAgQIECAAAECeQGBI79CAxAgQIAAAQIECBAgQIAAAQIChxsgQIAAAQIECBAgQIAAAQIE8gICR36FBiBAgAABAgQIECBAgAABAgQEDjdAgAABAgQIECBAgAABAgQI5AUEjvwKDUCAAAECBAgQIECAAAECBAgIHG6AAAECBAgQIECAAAECBAgQyAsIHPkVGoAAAQIECBAgQIAAAQIECBAQONwAAQIECBAgQIAAAQIECBAgkBcQOPIrNAABAgQIECBAgAABAgQIECAgcLgBAgQIECBAgAABAgQIECBAIC8gcORXaAACBAgQIECAAAECBAgQIEBA4HADBAgQIECAAAECBAgQIECAQF5A4Miv0AAECBAgQIAAAQIECBAgQICAwOEGCBAgQIAAAQIECBAgQIAAgbyAwJFfoQEIECBAgAABAgQIECBAgAABgcMNECBAgAABAgQIECBAgAABAnkBgSO/QgMQIECAAAECBAgQIECAAAECAocbIECAAAECBAgQIECAAAECBPICAkd+hQYgQIAAAQIECBAgQIAAAQIEBA43QIAAAQIECBAgQIAAAQIECOQFBI78Cg1AgAABAgQIECBAgAABAgQICBxugAABAgQIECBAgAABAgQIEMgLCBz5FRqAAAECBAgQIECAAAECBAgQEDjcAAECBAgQIECAAAECBAgQIJAXEDjyKzQAAQIECBAgQIAAAQIECBAgIHC4AQIECBAgQIAAAQIECBAgQCAvIHDkV2gAAgQIECBAgAABAgQIECBAQOBwAwQIECBAgAABAgQIECBAgEBeQODIr9AABAgQIECAAAECBAgQIECAgMDhBggQIECAAAECBAgQIECAAIG8gMCRX6EBCBAgQIAAAQIECBAgQIAAAYHDDRAgQIAAAQIECBAgQIAAAQJ5AYEjv0IDECBAgAABAgQIECBAgAABAgKHGyBAgAABAgQIECBAgAABAgTyAgJHfoUGIECAAAECBAgQIECAAAECBAQON0CAAAECBAgQIECAAAECBAjkBQSO/AoNQIAAAQIECBAgQIAAAQIECAgcboAAAQIECBAgQIAAAQIECBDICwgc+RUagAABAgQIECBAgAABAgQIEBA43AABAgQIECBAgAABAgQIECCQFxA48is0AAECBAgQIECAAAECBAgQICBwuAECBAgQIECAAAECBAgQIEAgLyBw5FdoAAIECBAgQIAAAQIECBAgQEDgcAMECBAgQIAAAQIECBAgQIBAXkDgyK/QAAQIECBAgAABAgQIECBAgIDA4QYIECBAgAABAgQIECBAgACBvIDAkV+hAQgQIECAAAECBAgQIECAAAGBww0QIECAAAECBAgQIECAAAECeQGBI79CAxAgQIAAAQIECBAgQIAAAQIChxsgQIAAAQIECBAgQIAAAQIE8gICR36FBiBAgAABAgQIECBAgAABAgQEDjdAgAABAgQIECBAgAABAgQI5AUEjvwKDUCAAAECBAgQIECAAAECBAgIHG6AAAECBAgQIECAAAECBAgQyAsIHPkVGoAAAQIECBAgQIAAAQIECBAQONwAAQIECBAgQIAAAQIECBAgkBcQOPIrNAABAgQIECBAgAABAgQIECAgcLgBAgQIECBAgAABAgQIECBAIC8gcORXaAACBAgQIECAAAECBAgQIEBA4HADBAgQIECAAAECBAgQIECAQF5A4Miv0AAECBAgQIAAAQIECBAgQICAwOEGCBAgQIAAAQIECBAgQIAAgbyAwJFfoQEIECBAgAABAgQIECBAgAABgcMNECBAgAABAgQIECBAgAABAnkBgSO/QgMQIECAAAECBAgQIECAAAECAocbIECAAAECBAgQIECAAAECBPICAkd+hQYgQIAAAQIECBAgQIAAAQIEBA43QIAAAQIECBAgQIAAAQIECOQFBI78Cg1AgAABAgQIECBAgAABAgQICBxugAABAgQIECBAgAABAgQIEMgLCBz5FRqAAAECBAgQIECAAAECBAgQEDjcAAECBAgQIECAAAECBAgQIJAXEDjyKzQAAQIECBAgQIAAAQIECBAgIHC4AQIECBAgQIAAAQIECBAgQCAvIHDkV2gAAgQIECBAgAABAgQIECBAQOBwAwQIECBAgAABAgQIECBAgEBeQODIr9AABAgQIECAAAECBAgQIECAgMDhBggQIECAAAECBAgQIECAAIG8gMCRX6EBCBAgQIAAAQIECBAgQIAAAYHDDRAgQIAAAQIECBAgQIAAAQJ5AYEjv0IDECBAgAABAgQIECBAgAABAgKHGyBAgAABAgQIECBAgAABAgTyAgJHfoUGIECAAAECBAgQIECAAAECBAQON0CAAAECBAgQIECAAAECBAjkBQSO/AoNQIAAAQIECBAgQIAAAQIECAgcboAAAQIECBAgQIAAAQIECBDICwgc+RUagAABAgQIECBAgAABAgQIEBA43AABAgQIECBAgAABAgQIECCQFxA48is0AAECBAgQIECAAAECBAgQICBwuAECBAgQIECAAAECBAgQIEAgLyBw5FdoAAIECBAgQIAAAQIECBAgQEDgcAMECBAgQIAAAQIECBAgQIBAXkDgyK/QAAQIECBAgAABAgQIECBAgIDA4QYIECBAgAABAgQIECBAgACBvIDAkV+hAQgQIECAAAECBAgQIECAAAGBww0QIECAAAECBAgQIECAAAECeQGBI79CAxAgQIAAAQIECBAgQIAAAQIChxsgQIAAAQIECBAgQIAAAQIE8gICR36FBiBAgAABAgQIECBAgAABAgQEDjdAgAABAgQIECBAgAABAgQI5AUEjvwKDUCAAAECBAgQIECAAAECBAgIHG6AAAECBAgQIECAAAECBAgQyAsIHPkVGoAAAQIECBAgQIAAAQIECBAQONwAAQIECBAgQIAAAQIECBAgkBcQOPIrNAABAgQIECBAgAABAgQIECAgcLgBAgQIECBAgAABAgQIECBAIC8gcORXaAACBAgQIECAAAECBAgQIEBA4HADBAgQIECAAAECBAgQIECAQF5A4Miv0AAECBAgQIAAAQIECBAgQICAwOEGCBAgQIAAAQIECBAgQIAAgbyAwJFfoQEIECBAgAABAgQIECBAgAABgcMNECBAgAABAgQIECBAgAABAnkBgSO/QgMQIECAAAECBAgQIECAAAECAocbIECAAAECBAgQIECAAAECBPICAkd+hQYgQIAAAQIECBAgQIAAAQIEBA43QIAAAQIECBAgQIAAAQIECOQFBI78Cg1AgAABAgQIECBAgAABAgQICBxugAABAgQIECBAgAABAgQIEMgLCBz5FRqAAAECBAgQIECAAAECBAgQEDjcAAECBAgQIECAAAECBAgQIJAXEDjyKzQAAQIECBAgQIAAAQIECBAgIHC4AQIECBAgQIAAAQIECBAgQCAvIHDkV2gAAgQIECBAgAABAgQIECBAQOBwAwQIECBAgAABAgQIECBAgEBeQODIr9AABAgQIECAAAECBAgQIECAgMDhBggQIECAAAECBAgQIECAAIG8gMCRX6EBCBAgQIAAAQIECBAgQIAAAYHDDRAgQIAAAQIECBAgQIAAAQJ5AYEjv0IDECBAgAABAgQIECBAgAABAgKHGyBAgAABAgQIECBAgAABAgTyAgJHfoUGIECAAAECBAgQIECAAAECBAQON0CAAAECBAgQIECAAAECBAjkBQSO/AoNQIAAAQIECBAgQIAAAQIECAgcboAAAQIECBAgQIAAAQIECBDICwgc+RUagAABAgQIECBAgAABAgQIEBA43AABAgQIECBAgAABAgQIECCQFxA48is0AAECBAgQIECAAAECBAgQICBwuAECBAgQIECAAAECBAgQIEAgLyBw5FdoAAIECBAgQIAAAQIECBAgQEDgcAMECBAgQIAAAQIECBAgQIBAXkDgyK/QAAQIECBAgAABAgQIECBAgIDA4QYIECBAgAABAgQIECBAgACBvIDAkV+hAQgQIECAAAECBAgQIECAAAGBww0QIECAAAECBAgQIECAAAECeQGBI79CAxAgQIAAAQIECBAgQIAAAQIChxsgQIAAAQIECBAgQIAAAQIE8gICR36FBiBAgAABAgQIECBAgAABAgQEDjdAgAABAgQIECBAgAABAgQI5AUEjvwKDUCAAAECBAgQIECAAAECBAgIHG6AAAECBAgQIECAAAECBAgQyAsIHPkVGoAAAQIECBAgQIAAAQIECBAQONwAAQIECBAgQIAAAQIECBAgkBcQOPIrNAABAgQIECBAgAABAgQIECAgcLgBAgQIECBAgAABAgQIECBAIC8gcORXaAACBAgQIECAAAECBAgQIEBA4HADBAgQIECAAAECBAgQIECAQF5A4Miv0AAECBAgQIAAAQIECBAgQICAwOEGCBAgQIAAAQIECBAgQIAAgbyAwJFfoQEIECBAgAABAgQIECBAgAABgcMNECBAgAABAgQIECBAgAABAnkBgSO/QgMQIECAAAECBAgQIECAAAECAocbIECAAAECBAgQIECAAAECBPICAkd+hQYgQIAAAQIECBAgQIAAAQIEBA43QIAAAQIECBAgQIAAAQIECOQFBI78Cg1AgAABAgQIECBAgAABAgQICBxugAABAgQIECBAgAABAgQIEMgLCBz5FRqAAAECBAgQIECAAAECBAgQEDjcAAECBAgQIECAAAECBAgQIJAXEDjyKzQAAQIECBAgQIAAAQIECBAgIHC4AQIECBAgQIAAAQIECBAgQCAvIHDkV2gAAgQIECBAgAABAgQIECBAQOBwAwQIECBAgAABApMCz8/Pv308Hp/+/tUfPPBf3717980XX3zx+w8fPvzH5OM9igABAgR+cgGB4ycn9w8kQIAAAQIECBD4nwTev3//5dPT0zePx+PP/ojU94/H43cvLy+/J0qAAAECBAQON0CAAAECBAgQIDAj8MOf2vi7/+WD/v7l5eXTn/TwFwECBAj8jAUEjp/x8o1OgAABAgQIEFgS+OFPbvzzZ77pr/1Jjs+U82MECBB4IwICxxtZpDEIECBAgAABAnWB5+fnf3k8Hr/8zDm+f3p6+vLDhw//9pk/78cIECBAIC4gcMQX6PkECBAgQIAAgbcg8Pz8/JvH4/EPr5nl48ePf/vdd9/91Wt+h58lQIAAga6AwNHdnZcTIECAAAECBN6MwPPz89ePx+MvXjnQv7+8vPzilb/DjxMgQIBAVEDgiC7OswkQIECAAAECb0nglf96yn9RPD09/an/dexbugyzECBA4C4gcNytfEmAAAECBAgQIPB/JPD8/Pzxx/jVHz9+/PPvvvvuw4/xu/wOAgQIEGgJCBytfXktAQIECBAgQOBNCggcb3KthiJAgMBPKiBw/KTc/mEECBAgQIAAAQL/nYB/RcVdECBAgMBrBQSO1wr6eQIECBAgQIAAgVcL+I+MvprQLyBAgMDPXkDg+NmfAAACBAgQIECAwP+/wFdfffXrd+/e/eNrXvLu3bu/+fbbb3/3mt/hZwkQIECgKyBwdHfn5QQIECBAgACBNyXw/Pz86T8O+qvPHOr7p6enX/g/qHymnh8jQIDAGxAQON7AEo1AgAABAgQIEHgLAu/fv//y6enpU+T4k8+Y5y9fXl6+/oyf8yMECBAg8EYEBI43skhjECBAgAABAgTegsBnRg5x4y0s3wwECBB4pYDA8UpAP06AAAECBAgQIPDjCvwQOT79aYxf/pHf/P3j8fjty8vLNz/uC/w2AgQIECgKCBzFrXkzAQIECBAgQOBnIPD8/PybTwHj8Xj8+g/+tZV/ejwe3zw9PX3tv7nxMzgEIxIgQOAoIHAcoXxGgAABAgQIECBAgAABAgQI7AoIHLu78TICBAgQIECAAAECBAgQIEDgKCBwHKF8RoAAAQIECBAgQIAAAQIECOwKCBy7u/EyAgQIECBAgAABAgQIECBA4CggcByhfEaAAAECBAgQIECAAAECBAjsCggcu7vxMgIECBAgQIAAAQIECBAgQOAoIHAcoXxGgAABAgQIECBAgAABAgQI7AoIHLu78TICBAgQIECAAAECBAgQIEDgKCBwHKF8RoAAAQIECBAgQIAAAQIECOwKCBy7u/EyAgQIECBAgAABAgQIECBA4CggcByhfEaAAAECBAgQIECAAAECBAjsCggcu7vxMgIECBAgQIAAAQIECBAgQOAoIHAcoXxGgAABAgQIECBAgAABAgQI7AoIHLu78TICBAgQIECAAAECBAgQIEDgKCBwHKF8RoAAAQIECBAgQIAAAQIECOwKCBy7u/EyAgQIECBAgAABAgQIECBA4CggcByhfEaAAAECBAgQIECAAAECBAjsCggcu7vxMgIECBAgQIAAAQIECBAgQOAoIHAcoXxGgAABAgQIECBAgAABAgQI7AoIHLu78TICBAgQIECAAAECBAgQIEDgKCBwHKF8RoAAAQIECBAgQIAAAQIECOwKCBy7u/EyAgQIECBAgAABAgQIECBA4CggcByhfEaAAAECBAgQIECAAAECBAjsCggcu7vxMgIECBAgQIAAAQIECBAgQOAoIHAcoXxGgAABAgQIECBAgAABAgQI7AoIHLu78TICBAgQIECAAAECBAgQIEDgKCBwHKF8RoAAAQIECBAgQIAAAQIECOwKCBy7u/EyAgQIECBAgAABAgQIECBA4CggcByhfEaAAAECBAgQIECAAAECBAjsCggcu7vxMgIECBAgQIAAAQIECBAgQOAoIHAcoXxGgAABAgQIECBAgAABAgQI7AoIHLu78TICBAgQIECAAAECBAgQIEDgKCBwHKF8RoAAAQIECBAgQIAAAQIECOwKCBy7u/EyAgQIECBAgAABAgQIECBA4CggcByhfEaAAAECBAgQIECAAAECBAjsCggcu7vxMgIECBAgQIAAAQIECBAgQOAoIHAcoXxGgAABAgQIECBAgAABAgQI7AoIHLu78TICBAgQIECAAAECBAgQIEDgKCBwHKF8RoAAAQIECBAgQIAAAQIECOwKCBy7u/EyAgQIECBAgAABAgQIECBA4CggcByhfEaAAAECBAgQIECAAAECBAjsCggcu7vxMgIECBAgQIAAAQIECBAgQOAoIHAcoXxGgAABAgQIECBAgAABAgQI7AoIHLu78TICBAgQIECAAAECBAgQIEDgKCBwHKF8RoAAAQIECBAgQIAAAQIECOwKCBy7u/EyAgQIECBAgAABAgQIECBA4CggcByhfEaAAAECBAgQIECAAAECBAjsCggcu7vxMgIECBAgQIAAAQIECBAgQOAoIHAcoXxGgAABAgQIECBAgAABAgQI7AoIHLu78TICBAgQIECAAAECBAgQIEDgKCBwHKF8RoAAAQIECBAgQIAAAQIECOwKCBy7u/EyAgQIECBAgAABAgQIECBA4CggcByhfEaAAAECBAgQIECAAAECBAjsCggcu7vxMgIECBAgQIAAAQIECBAgQOAoIHAcoXxGgAABAgQIECBAgAABAgQI7AoIHLu78TICBAgQIECAAAECBAgQIEDgKCBwHKF8RoAAAQIECBAgQIAAAQIECOwKCBy7u/EyAgQIECBAgAABAgQIECBA4CggcByhfEaAAAECBAgQIECAAAECBAjsCggcu7vxMgIECBAgQIAAAQIECBAgQOAoIHAcoXxGgAABAgQIECBAgAABAgQI7AoIHLu78TICBAgQIECAAAECBAgQIEDgKCBwHKF8RoAAAQIECBAgQIAAAQIECOwKCBy7u/EyAgQIECBAgAABAgQIECBA4CggcByhfEaAAAECBAgQIECAAAECBAjsCggcu7vxMgIEB+TyDQAAHYZJREFUCBAgQIAAAQIECBAgQOAoIHAcoXxGgAABAgQIECBAgAABAgQI7AoIHLu78TICBAgQIECAAAECBAgQIEDgKCBwHKF8RoAAAQIECBAgQIAAAQIECOwKCBy7u/EyAgQIECBAgAABAgQIECBA4CggcByhfEaAAAECBAgQIECAAAECBAjsCggcu7vxMgIECBAgQIAAAQIECBAgQOAoIHAcoXxGgAABAgQIECBAgAABAgQI7AoIHLu78TICBAgQIECAAAECBAgQIEDgKCBwHKF8RoAAAQIECBAgQIAAAQIECOwKCBy7u/EyAgQIECBAgAABAgQIECBA4CggcByhfEaAAAECBAgQIECAAAECBAjsCggcu7vxMgIECBAgQIAAAQIECBAgQOAoIHAcoXxGgAABAgQIECBAgAABAgQI7AoIHLu78TICBAgQIECAAAECBAgQIEDgKCBwHKF8RoAAAQIECBAgQIAAAQIECOwKCBy7u/EyAgQIECBAgAABAgQIECBA4CggcByhfEaAAAECBAgQIECAAAECBAjsCggcu7vxMgIECBAgQIAAAQIECBAgQOAoIHAcoXxGgAABAgQIECBAgAABAgQI7AoIHLu78TICBAgQIECAAAECBAgQIEDgKCBwHKF8RoAAAQIECBAgQIAAAQIECOwKCBy7u/EyAgQIECBAgAABAgQIECBA4CggcByhfEaAAAECBAgQIECAAAECBAjsCggcu7vxMgIECBAgQIAAAQIECBAgQOAoIHAcoXxGgAABAgQIECBAgAABAgQI7AoIHLu78TICBAgQIECAAAECBAgQIEDgKCBwHKF8RoAAAQIECBAgQIAAAQIECOwKCBy7u/EyAgQIECBAgAABAgQIECBA4CggcByhfEaAAAECBAgQIECAAAECBAjsCggcu7vxMgIECBAgQIAAAQIECBAgQOAoIHAcoXxGgAABAgQIECBAgAABAgQI7AoIHLu78TICBAgQIECAAAECBAgQIEDgKCBwHKF8RoAAAQIECBAgQIAAAQIECOwKCBy7u/EyAgQIECBAgAABAgQIECBA4CggcByhfEaAAAECBAgQIECAAAECBAjsCggcu7vxMgIECBAgQIAAAQIECBAgQOAoIHAcoXxGgAABAgQIECBAgAABAgQI7AoIHLu78TICBAgQIECAAAECBAgQIEDgKCBwHKF8RoAAAQIECBAgQIAAAQIECOwKCBy7u/EyAgQIECBAgAABAgQIECBA4CggcByhfEaAAAECBAgQIECAAAECBAjsCggcu7vxMgIECBAgQIAAAQIECBAgQOAoIHAcoXxGgAABAgQIECBAgAABAgQI7AoIHLu78TICBAgQIECAAAECBAgQIEDgKCBwHKF8RoAAAQIECBAgQIAAAQIECOwKCBy7u/EyAgQIECBAgAABAgQIECBA4CggcByhfEaAAAECBAgQIECAAAECBAjsCggcu7vxMgIECBAgQIAAAQIECBAgQOAoIHAcoXxGgAABAgQIECBAgAABAgQI7AoIHLu78TICBAgQIECAAAECBAgQIEDgKCBwHKF8RoAAAQIECBAgQIAAAQIECOwKCBy7u/EyAgQIECBAgAABAgQIECBA4CggcByhfEaAAAECBAgQIECAAAECBAjsCggcu7vxMgIECBAgQIAAAQIECBAgQOAoIHAcoXxGgAABAgQIECBAgAABAgQI7AoIHLu78TICBAgQIECAAAECBAgQIEDgKCBwHKF8RoAAAQIECBAgQIAAAQIECOwKCBy7u/EyAgQIECBAgAABAgQIECBA4CggcByhfEaAAAECBAgQIECAAAECBAjsCggcu7vxMgIECBAgQIAAAQIECBAgQOAoIHAcoXxGgAABAgQIECBAgAABAgQI7AoIHLu78TICBAgQIECAAAECBAgQIEDgKCBwHKF8RoAAAQIECBAgQIAAAQIECOwKCBy7u/EyAgQIECBAgAABAgQIECBA4CggcByhfEaAAAECBAgQIECAAAECBAjsCggcu7vxMgIECBAgQIAAAQIECBAgQOAoIHAcoXxGgAABAgQIECBAgAABAgQI7AoIHLu78TICBAgQIECAAAECBAgQIEDgKCBwHKF8RoAAAQIECBAgQIAAAQIECOwKCBy7u/EyAgQIECBAgAABAgQIECBA4CggcByhfEaAAAECBAgQIECAAAECBAjsCggcu7vxMgIECBAgQIAAAQIECBAgQOAoIHAcoXxGgAABAgQIECBAgAABAgQI7AoIHLu78TICBAgQIECAAAECBAgQIEDgKCBwHKF8RoAAAQIECBAgQIAAAQIECOwKCBy7u/EyAgQIECBAgAABAgQIECBA4CggcByhfEaAAAECBAgQIECAAAECBAjsCggcu7vxMgIECBAgQIAAAQIECBAgQOAoIHAcoXxGgAABAgQIECBAgAABAgQI7AoIHLu78TICBAgQIECAAAECBAgQIEDgKCBwHKF8RoAAAQIECBAgQIAAAQIECOwKCBy7u/EyAgQIECBAgAABAgQIECBA4CggcByhfEaAAAECBAgQIECAAAECBAjsCggcu7vxMgIECBAgQIAAAQIECBAgQOAoIHAcoXxGgAABAgQIECBAgAABAgQI7AoIHLu78TICBAgQIECAAAECBAgQIEDgKCBwHKF8RoAAAQIECBAgQIAAAQIECOwKCBy7u/EyAgQIECBAgAABAgQIECBA4CggcByhfEaAAAECBAgQIECAAAECBAjsCggcu7vxMgIECBAgQIAAAQIECBAgQOAoIHAcoXxGgAABAgQIECBAgAABAgQI7AoIHLu78TICBAgQIECAAAECBAgQIEDgKCBwHKF8RoAAAQIECBAgQIAAAQIECOwKCBy7u/EyAgQIECBAgAABAgQIECBA4CggcByhfEaAAAECBAgQIECAAAECBAjsCggcu7vxMgIECBAgQIAAAQIECBAgQOAoIHAcoXxGgAABAgQIECBAgAABAgQI7AoIHLu78TICBAgQIECAAAECBAgQIEDgKCBwHKF8RoAAAQIECBAgQIAAAQIECOwKCBy7u/EyAgQIECBAgAABAgQIECBA4CggcByhfEaAAAECBAgQIECAAAECBAjsCggcu7vxMgIECBAgQIAAAQIECBAgQOAoIHAcoXxGgAABAgQIECBAgAABAgQI7AoIHLu78TICBAgQIECAAAECBAgQIEDgKCBwHKF8RoAAAQIECBAgQIAAAQIECOwKCBy7u/EyAgQIECBAgAABAgQIECBA4CggcByhfEaAAAECBAgQIECAAAECBAjsCggcu7vxMgIECBAgQIAAAQIECBAgQOAoIHAcoXxGgAABAgQIECBAgAABAgQI7AoIHLu78TICBAgQIECAAAECBAgQIEDgKCBwHKF8RoAAAQIECBAgQIAAAQIECOwKCBy7u/EyAgQIECBAgAABAgQIECBA4CggcByhfEaAAAECBAgQIECAAAECBAjsCggcu7vxMgIECBAgQIAAAQIECBAgQOAoIHAcoXxGgAABAgQIECBAgAABAgQI7AoIHLu78TICBAgQIECAAAECBAgQIEDgKCBwHKF8RoAAAQIECBAgQIAAAQIECOwKCBy7u/EyAgQIECBAgAABAgQIECBA4CggcByhfEaAAAECBAgQIECAAAECBAjsCggcu7vxMgIECBAgQIAAAQIECBAgQOAoIHAcoXxGgAABAgQIECBAgAABAgQI7AoIHLu78TICBAgQIECAAAECBAgQIEDgKCBwHKF8RoAAAQIECBAgQIAAAQIECOwKCBy7u/EyAgQIECBAgAABAgQIECBA4CggcByhfEaAAAECBAgQIECAAAECBAjsCggcu7vxMgIECBAgQIAAAQIECBAgQOAoIHAcoXxGgAABAgQIECBAgAABAgQI7AoIHLu78TICBAgQIECAAAECBAgQIEDgKCBwHKF8RoAAAQIECBAgQIAAAQIECOwKCBy7u/EyAgQIECBAgAABAgQIECBA4CggcByhfEaAAAECBAgQIECAAAECBAjsCggcu7vxMgIECBAgQIAAAQIECBAgQOAoIHAcoXxGgAABAgQIECBAgAABAgQI7AoIHLu78TICBAgQIECAAAECBAgQIEDgKCBwHKF8RoAAAQIECBAgQIAAAQIECOwKCBy7u/EyAgQIECBAgAABAgQIECBA4CggcByhfEaAAAECBAgQIECAAAECBAjsCggcu7vxMgIECBAgQIAAAQIECBAgQOAoIHAcoXxGgAABAgQIECBAgAABAgQI7AoIHLu78TICBAgQIECAAAECBAgQIEDgKCBwHKF8RoAAAQIECBAgQIAAAQIECOwKCBy7u/EyAgQIECBAgAABAgQIECBA4CggcByhfEaAAAECBAgQIECAAAECBAjsCggcu7vxMgIECBAgQIAAAQIECBAgQOAoIHAcoXxGgAABAgQIECBAgAABAgQI7AoIHLu78TICBAgQIECAAAECBAgQIEDgKCBwHKF8RoAAAQIECBAgQIAAAQIECOwKCBy7u/EyAgQIECBAgAABAgQIECBA4CggcByhfEaAAAECBAgQIECAAAECBAjsCggcu7vxMgIECBAgQIAAAQIECBAgQOAoIHAcoXxGgAABAgQIECBAgAABAgQI7AoIHLu78TICBAgQIECAAAECBAgQIEDgKCBwHKF8RoAAAQIECBAgQIAAAQIECOwKCBy7u/EyAgQIECBAgAABAgQIECBA4CggcByhfEaAAAECBAgQIECAAAECBAjsCggcu7vxMgIECBAgQIAAAQIECBAgQOAoIHAcoXxGgAABAgQIECBAgAABAgQI7AoIHLu78TICBAgQIECAAAECBAgQIEDgKCBwHKF8RoAAAQIECBAgQIAAAQIECOwKCBy7u/EyAgQIECBAgAABAgQIECBA4CggcByhfEaAAAECBAgQIECAAAECBAjsCggcu7vxMgIECBAgQIAAAQIECBAgQOAoIHAcoXxGgAABAgQIECBAgAABAgQI7AoIHLu78TICBAgQIECAAAECBAgQIEDgKCBwHKF8RoAAAQIECBAgQIAAAQIECOwKCBy7u/EyAgQIECBAgAABAgQIECBA4CggcByhfEaAAAECBAgQIECAAAECBAjsCggcu7vxMgIECBAgQIAAAQIECBAgQOAoIHAcoXxGgAABAgQIECBAgAABAgQI7AoIHLu78TICBAgQIECAAAECBAgQIEDgKCBwHKF8RoAAAQIECBAgQIAAAQIECOwKCBy7u/EyAgQIECBAgAABAgQIECBA4CggcByhfEaAAAECBAgQIECAAAECBAjsCggcu7vxMgIECBAgQIAAAQIECBAgQOAoIHAcoXxGgAABAgQIECBAgAABAgQI7AoIHLu78TICBAgQIECAAAECBAgQIEDgKCBwHKF8RoAAAQIECBAgQIAAAQIECOwKCBy7u/EyAgQIECBAgAABAgQIECBA4CggcByhfEaAAAECBAgQIECAAAECBAjsCggcu7vxMgIECBAgQIAAAQIECBAgQOAoIHAcoXxGgAABAgQIECBAgAABAgQI7AoIHLu78TICBAgQIECAAAECBAgQIEDgKCBwHKF8RoAAAQIECBAgQIAAAQIECOwKCBy7u/EyAgQIECBAgAABAgQIECBA4CggcByhfEaAAAECBAgQIECAAAECBAjsCggcu7vxMgIECBAgQIAAgf9sxw6JAAAAEIj1b02Fl4h5BDccBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBRwcEQoMQIECBAgQIAAAQIECBAgQOBXwMHxu41mBAgQIECAAAECBAgQIECAQBQYSPdvOfe969kAAAAASUVORK5CYII=","e":1},{"id":"image_6","w":1080,"h":1080,"u":"","p":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABDgAAAQ4CAYAAADsEGyPAAAgAElEQVR4Xuzd35Hb9rkGYHCx93YqiFJBlFvNwtlUEKWC2BVEp4KjVBCdCixXELmCrAWNbo9UQawKYt2LwpmfDtYjrYg/JLgi3+WzM74Sl8T3vJ+d4BUIrCo/BAgQIECAAAECBAgQIECAAIFwgVX48Tt8AgQIECBAgAABAgQIECBAgECl4LAEBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQILK6KqsAACAASURBVECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIAAAQIECBAgQIAAAQIKDjtAgAABAgQIECBAgAABAgQIxAsoOOIjNAABAgQIECBAgAABAgQIECCg4LADBAgQIECAAAECBAgQIECAQLyAgiM+QgMQIECAAAECBAgQIECAAAECCg47QIAAAQIECBAgQIAAAQIECMQLKDjiIzQAAQIECBAgQIAAAQIECBAgoOCwAwQIECBAgAABAgQIECBAgEC8gIIjPkIDECBAgAABAgQIECBAgAABAgoOO0CAAAECBAgQIECAAAECBAjECyg44iM0AAECBAgQIECAAAECBAgQIKDgsAMECBAgQIAAAQIECBAgQIBAvICCIz5CAxAgQIBAssA333zzuOu6yzLDarW6Ojs7e3J1dfVL8kyOnQABAgQIECBwCAEFxyHUfSYBAgQIEKiq6uLi4slqtfrbDYyf2rb9UHhM/TRN821VVeW19/py5OnV1dXPU7/nzwkQIECAAAECd1FAwXEXUzUTAQIECBy9wOXl5dfr9fo/mw50vV7/4eXLl6+Ghuh/96qqqt/feM3bqqq+bdv22dEDOEACBAgQIECAwJ4FFBx7BvV2BAgQIEBgjsDFxcXlarX616bXdl33pxcvXpQCY+NP0zRPq6r668Afv63r+p6vucxJwWsIECBAgACBuySg4LhLaZqFAAECBGIEFhYc3cSg37VtW0oQPwQIECBAgACBkxFQcJxM1AYlQCBZ4MGDB/fPz88flhnevXv3bOzrC8lzntKx32bBsVqt/v78+fPHp+RpVgIECBAgQICAgsMOECBA4MgF+htJfn/jMP0N/ZHnNnV4CwuO8pSVr0Y+w35MBeDPCRAgQIAAgTsnoOC4c5EaiACBuyQwciNK91kID3pJwTHw9JVrEbsRvhsOnwABAgQIENhNQMGxm5vfIkCAwBcRWHIS/EUO0IfsLLA024Ebjb5dr9eXvsK0cyx+kQABAgQIEAgWUHAEh+fQCRC4+wJLT4LvvlDuhPvItrzH2dnZZVHouu7nuq6feXpK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AAECBAgQiBdQcMRHaAACBAgQIECAAAECBAgQIEBAwWEHCBAgQIAAAQIECBAgQIAAgXgBBUd8hAYgQIAAAQIECBAgQIAAAQIEFBx2gAABAgQIECBAgAABAgQIEIgXUHDER2gAAgQIECBAgAABAgQIECBAQMFhBwgQIECAAAECBAgQIECAAIF4AQVHfIQGIECAAAECBAgQIECAAAECBBQcdoAAAQIECBAgQIAAAQIECBCIF1BwxEdoAAIECBAgQIAAAQIECBAgQEDBYQcIECBAgAABAgQIECBAgACBeAEFR3yEBiBAgAABAgQIECBAgAABAgQUHHaAAAECBAgQIECAAAECBAgQiBdQcMRHaAACBAgQIECAAAECBAgQIEBAwWEHCBAgQIAAAQIECBAgQIAAgXgBBUd8hAYgQIAAAQIECBAgQIAAAQIEFBx2gAABAgQIECBAgAABAgQIEIgXUHDER2gAAgQIECBAgAABAgQIECBAQMFhBwgQIECAAAECBAgQIECAAIF4AQVHfIQGIECAAAECBAgQIECAAAECBBQcdoAAAQIECBAgQIAAAQIECBCIF1BwxEdoAAIECBAgQIAAAQIECBAgQEDBYQcIECBAgAABAgQIECBAgACBeAEFR3yEBiBAgAABAgQIECBAgAABAgQUHHaAAAECBAgQIECAAAECBAgQiBdQcMRHaAACBAgQIECAAAECBAgQIEBAwWEHCBAgQIAAAQIECBAgQIAAgXgBBUd8hAYgQIAAAQIECBAgQIAAAQIEFBx2gAABAgQIECBAgAABAgQIEIgXUHDER2gAAgQIECBAgAABAgQIECBAQMFhBwgQIECAAAECBAgQIECAAIF4AQVHfIQGIECAAAECBAgQIECAAAECBBQcdoAAAQIECBAgQIAAAQIECBCIF1BwxEdoAAIECBAgQIAAAQIECBAgQEDBYQcIECBAgAABAgQIECBAgACBeAEFR3yEBiBAgAABAgQIECBAgAABAgQUHHaAAAECBAgQIECAAAECBAgQiBdQcMRHaAACBAgQIECAAAECBAgQIEBAwWEHCBAgQIAAAQIECBAgQIAAgXgBBUd8hAYgQIAAAQIECBAgQIAAAQIEFBx2gAABAgQIECBAgAABAgQIEIgXUHDER2gAAgQIECBAgAABAgQIECBAQMFhBwgQIECAAAECBAgQIECAAIF4AQVHfIQGIECAAAECBAgQIECAAAECBBQcdoAAAQIECBAgQIAAAQIECBCIF1BwxEdoAAIECBAgQIAAAQIECBAgQEDBYQcIECBAgAABAgQIECBAgACBeAEFR3yEBiBAgAABAgQIECBAgAABAgQUHHaAAAECBAgQIECAAAECBAgQiBdQcMRHaAACBAgQIECAAAECBAgQIEBAwWEHCBAgQIAAAQIECBAgQIAAgXgBBUd8hAYgQIAAAQIECBAgQIAAAQIEFBx2gAABAgQIECBAgAABAgQIEIgXUHDER2gAAgQIECBAgAABAgQIECBAQMFhBwgQIECAAAECBAgQIECAAIF4AQVHfIQGIECAAAECBAgQIECAAAECBBQcdoAAAQIECBAgQIAAAQIECBCIF1BwxEdoAAIECBAgQIAAAQIECBAgQEDBYQcIECBAgAABAgQIECBAgACBeAEFR3yEBiBAgAABAgQIECBAgAABAgQUHHaAAAECBAgQIECAAAECBAgQiBdQcMRHaAACBAgQIECAAAECBAgQIEBAwWEHCBAgQIAAAQIECBAgQIAAgXgBBUd8hAYgQIAAAQIECBAgQIAAAQIEFBx2gAABAgQIECBAgAABAgQIEIgXUHDER2gAAgQIECBAgAABAgQIECBAQMFhBwgQIECAAAECBAgQIECAAIF4AQVHfIQGIECAAAECBAgQIECAAAECBBQcdoAAAQIECBAgQIAAAQIECBCIF1BwxEdoAAIECBAgQIAAAQIECBAgQEDBYQcIECBAgAABAgQIECBAgACBeAEFR3yEBiBAgAABAgQIECBAgAABAgQUHHaAAAECBAgQIECAAAECBAgQiBdQcMRHaAACBAgQIECAAAECBAgQIEBAwWEHCBAgQIAAAQIECBAgQIAAgXgBBUd8hAYgQIAAAQIECBAgQIAAAQIEFBx2gAABAgQIECBAgAABAgQIEIgXUHDER2gAAgQIECBAgAABAgQIECBAQMFhBwgQIECAAAECBAgQIECAAIF4AQVHfIQGIECAAAECBAgQIECAAAECBBQcdoAAAQIECBAgQIAAAQIECBCIF1BwxEdoAAIECBAgQIAAAQIECBAgQEDBYQcIECBAgAABAgQIECBAgACBeAEFR3yEBiBAgAABAgQIECBAgAABAgQUHHaAAAECBAgQIECAAAECBAgQiBdQcMRHaAACBAgQIECAAAECBAgQIEBAwWEHCBAgQIAAAQIECBAgQIAAgXgBBUd8hAYgQIAAAQIECBAgQIAAAQIEFBx2gAABAgQIECBAgAABAgQIEIgXUHDER2gAAgQIECBAgAABAgQIECBAQMFhBwgQIECAAAECBAgQIECAAIF4AQVHfIQGIECAAAECBAgQIECAAAECBBQcdoAAAQIECBAgQIAAAQIECBCIF1BwxEdoAAIECBAgQIAAAQIECBAgQEDBYQcIECBAgAABAgQIECBAgACBeAEFR3yEBiBAgAABAgQIECBAgAABAgQUHHaAAAECBAgQIECAAAECBAgQiBdQcMRHaAACBAgQIECAAAECBAgQIEBAwWEHCBAgQIAAAQIECBAgQIAAgXgBBUd8hAYgQIAAAQIECBAgQIAAAQIEFBx2gAABAgQIECBAgAABAgQIEIgXUHDER2gAAgQIECBAgAABAgQIECBAQMFhBwgQIECAAAECBAgQIECAAIF4AQVHfIQGIECAAAECBAgQIECAAAECBBQcdoAAAQIECBAgQIAAAQIECBCIF1BwxEdoAAIECBAgQIAAAQIECBAgQEDBYQcIECBAgAABAgQIECBAgACBeAEFR3yEBiBAgAABAgQIECBAgAABAgQUHHaAAAECBAgQIECAAAECBAgQiBdQcMRHaAACBAgQIECAAAECBAgQIEBAwWEHCBAgQIAAAQIECBAgQIAAgXgBBUd8hAYgQIAAAQIECBAgQIAAAQIEFBx2gAABAgQIECBAgAABAgQIEIgXUHDER2gAAgQIECBAgAABAgQIECBAQMFhBwgQIECAAAECBAgQIECAAIF4AQVHfIQGIECAAAECBAgQIECAAAECBBQcdoAAAQIECBAgQIAAAQIECBCIF1BwxEdoAAIECBAgQIAAAQIECBAgQEDBYQcIECBAgAABAgQIECBAgACBeAEFR3yEBiBAgAABAgQIECBAgAABAgQUHHaAAAECBAgQIECAAAECBAgQiBdQcMRHaAACBAgQIECAAAECBAgQIEBAwWEHCBAgQIAAAQIECBAgQIAAgXgBBUd8hAYgQIAAAQIECBAgQIAAAQIEFBx2gAABAgQIECBAgAABAgQIEIgXUHDER2gAAgQIECBAgAABAgQIECBAQMFhBwgQIECAAAECBAgQIECAAIF4AQVHfIQGIECAAAECBAgQIECAAAECBBQcdoAAAQIECBAgQIAAAQIECBCIF1BwxEdoAAIECBAgQIAAAQIECBAgQEDBYQcIECBAgAABAgQIECBAgACBeAEFR3yEBiBAgAABAgQIECBAgAABAgQUHHaAAAECBAgQIECAAAECBAgQiBdQcMRHaAACBAgQIECAAAECBAgQIEBAwWEHCBAgQIAAAQIECBAgQIAAgXgBBUd8hAYgQIAAAQIECBAgQIAAAQIEFBx2gAABAgQIECBAgAABAgQIEIgXUHDER2gAAgQIECBAgAABAgQIECBAQMFhBwgQIECAAAECBAgQIECAAIF4AQVHfIQGIECAAAECBAgQIECAAAECBBQcdoAAAQIECBAgQIAAAQIECBCIF1BwxEdoAAIECBAgQIAAAQIECBAgQEDBYQcIECBAgAABAgQIECBAgACBeAEFR3yEBiBAgAABAgQIECBAgAABAgQUHHaAAAECBAgQIECAAAECBAgQiBdQcMRHaAACBAgQIECAAAECBAgQIEBAwWEHCBAgQIAAAQIECBAgQIAAgXgBBUd8hAYgQIAAAQIECBAgQIAAAQIEFBx2gAABAgQIECBAgAABAgQIEIgXUHDER2gAAgQI/F87dlADAAADIcy/a3SQVMK6e0GAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwjAafQZAAAH3klEQVQQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvYDAsX+hAwgQIECAAAECBAgQIECAAAGBwwYIECBAgAABAgQIECBAgACBvUCsBGHPV4DBmQAAAABJRU5ErkJggg==","e":1}],"layers":[{"ddd":0,"ind":1,"ty":2,"nm":"Cup/loading animation.ai","cl":"ai","refId":"image_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[540,540,0],"ix":2},"a":{"a":0,"k":[540,540,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":71.0000028918893,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":2,"nm":"cup shape/loading animation.ai","cl":"ai","td":1,"refId":"image_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[540,540,0],"ix":2},"a":{"a":0,"k":[540,540,0],"ix":1},"s":{"a":0,"k":[94.556,94.556,100],"ix":6}},"ao":0,"ip":0,"op":71.0000028918893,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":2,"nm":"wave/loading animation.ai","cl":"ai","tt":1,"refId":"image_2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.607,"y":0.595},"o":{"x":0.529,"y":0},"t":0,"s":[660.805,540,0],"to":[-30.035,0,0],"ti":[68.54,0,0]},{"i":{"x":0.507,"y":1},"o":{"x":0.21,"y":0.779},"t":30,"s":[469.965,532,0],"to":[-30.703,0,0],"ti":[13.455,0,0]},{"t":70.0000028511585,"s":[399.866,540,0]}],"ix":2},"a":{"a":0,"k":[540,540,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":71.0000028918893,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":2,"nm":"dot 1/loading animation.ai","cl":"ai","refId":"image_3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[608,678,0],"ix":2},"a":{"a":0,"k":[608,678,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":10,"s":[131.78,131.78,100]},{"t":20.0000008146167,"s":[100,100,100]}],"ix":6}},"ao":0,"ip":0,"op":71.0000028918893,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":2,"nm":"dot 2/loading animation.ai","cl":"ai","refId":"image_4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[646,676,0],"ix":2},"a":{"a":0,"k":[646,676,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":25,"s":[100,100,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":35,"s":[131.78,131.78,100]},{"t":45.0000018328876,"s":[100,100,100]}],"ix":6}},"ao":0,"ip":0,"op":71.0000028918893,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":2,"nm":"Dot 3/loading animation.ai","cl":"ai","refId":"image_5","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[684.966,675.302,0],"ix":2},"a":{"a":0,"k":[684.966,675.302,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":50,"s":[100,100,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":60,"s":[131.78,131.78,100]},{"t":70.0000028511585,"s":[100,100,100]}],"ix":6}},"ao":0,"ip":0,"op":71.0000028918893,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":2,"nm":"text/loading animation.ai","cl":"ai","refId":"image_6","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[540,540,0],"ix":2},"a":{"a":0,"k":[540,540,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"ip":0,"op":71.0000028918893,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index a942b60..ca76bf8 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -1,11 +1,18 @@ + - + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 449df2b..a0dbd7e 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -2,7 +2,9 @@ #03a9f4 #0288d1 + #f6f6f6 #ffab40 + #2c2c2c #ffc77f #30000000 @android:color/white diff --git a/app/src/main/res/values/ids.xml b/app/src/main/res/values/ids.xml new file mode 100644 index 0000000..2291a30 --- /dev/null +++ b/app/src/main/res/values/ids.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 059db9b..b2a5755 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,5 +1,6 @@ 易开发 + 该功能需要读写内部存储权限,点击前往设置 GuideActivity Settings 上一步 @@ -11,18 +12,20 @@ 无障碍功能已开启 该设备未root,无法开启 基本设置 - 高级功能设置 + 高级功能 + 备注 开启无障碍辅助 开启悬浮窗权限 - 推荐开启,用于获取应用数据库等信息,不开启部分信息无法显示,Xposed无法使用 + 推荐开启,用于获取应用SharedPreference、数据库等数据,关闭后部分信息无法显示 开启root权限 启用Xposed特性 支持脱壳,更多功能持续开发中,点击进入管理 辅助功能用于界面布局分析等 关于 软件开发中,交流群:627962572 - 界面分析 - 点击分析 + 界面布局 + 功能需要root权限,请开启后使用! + 点击查看 首次安装时间 最后更新时间 守护进程 @@ -36,7 +39,7 @@ 恢复 保存失败,请按错误提示修改后再提交 检测到值有修改,是否保存? - 保存成功,由于缓存原因,新数据重启后才会生效 + 保存成功,由于缓存原因,新数据重启后才会生效,是否立即重启%1$s? 退出 是否退出应用? 确定 @@ -47,24 +50,23 @@ 基本信息 加固类型 正在分析 - 请稍后... + 请稍后… 正在获取应用信息 请开启无障碍功能 该表数据为空 获取数据库信息失败 - 获取中... + 获取中… 数据库列表 应用设置 - 备份Apk文件 - 备份data目录 + 备份应用 + 还原应用 重启应用 停止应用 清理应用数据 卸载应用 请开启root权限 - 部分功能需要root权限,请打开后使用! 是否清除数据和缓存? 清理完成 是否停止该应用? @@ -74,13 +76,49 @@ 备份失败 备份成功,文件已备份到内部存储下com.wrbug.developerHelper目录中,是否立即分享给好友? 分享失败 - 导出脱壳数据 正在打包文件 导出失败! - 无脱壳文件 - Xposed设置 - 管理、移除需要脱壳的应用 - 脱壳应用管理 移除该项 请先开启Xposed设置 + 检查更新 + 正在检查新版本… + 暂无新版本 + 检查失败… + 发现新版本 + 下载 + 通知栏常驻 + 用于进程保活,当应用经常被杀死时,请打开该功能 + 请选择需要备份的内容,备份所有数据支持无损恢复 + 正在备份数据 + 备份失败 + 跳过该备份 + 备份正在进行中,请勿关闭该窗口 + 应用安装包 + Data目录 + sdcard/Data目录 + 备份已完成,您可以前往 %1$s 目录查看备份文件 + 等待中 + 打包备份文件 + 暂无数据 + %1$d个备份(%2$s) + 上一次:%1$s + [系统] + [未安装] + 其他功能 + 无需备份 + 备份管理 + 恢复应用 + 管理/恢复已备份的应用 + 已取消 + %1$s 的备份 + 删除 + 恢复 + 是否删除该备份? + 删除备份失败,请重试 + 选择需要恢复的数据 + 恢复%1$s + 未安装该应用,且apk未备份,无法恢复 + 当前应用版本[%1$s]与备份版本[%2$s]不一致,备份数据可能不兼容,建议勾选恢复安装包,是否继续 + 正在恢复%1$s数据 + 正在启动 %1$s diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index ddc3fd5..906b089 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -7,6 +7,7 @@ @color/colorPrimaryDark @color/colorAccent @android:color/white + @color/colorWindowBackground @@ -36,6 +37,25 @@ @android:color/white + + + + + + + + + diff --git a/app/src/test/java/com/wrbug/developerhelper/ExampleUnitTest.kt b/app/src/test/java/com/wrbug/developerhelper/ExampleUnitTest.kt deleted file mode 100644 index 34b59a3..0000000 --- a/app/src/test/java/com/wrbug/developerhelper/ExampleUnitTest.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.wrbug.developerhelper - -import org.junit.Test - -import org.junit.Assert.* - -/** - * Example local unit test, which will execute on the development machine (host). - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -class ExampleUnitTest { - @Test - fun addition_isCorrect() { - assertEquals(4, 2 + 2) - } -} diff --git a/basecommon/build.gradle b/basecommon/build.gradle deleted file mode 100644 index 922788e..0000000 --- a/basecommon/build.gradle +++ /dev/null @@ -1,41 +0,0 @@ -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-kapt'//kapt 插件 - -android { - compileSdkVersion 28 - - - - defaultConfig { - minSdkVersion 21 - targetSdkVersion 28 - versionCode 1 - versionName "1.0" - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } -} -def lifecycle_version = "2.0.0" -dependencies { - implementation fileTree(include: ['*.jar'], dir: 'libs') - implementation 'androidx.appcompat:appcompat:1.0.2' - testImplementation 'junit:junit:4.12' - androidTestImplementation 'com.android.support.test:runner:1.0.2' - androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation 'com.google.android.material:material:1.1.0-alpha01' - implementation 'android.arch.lifecycle:extensions:1.1.1' - annotationProcessor "android.arch.lifecycle:compiler:1.1.1" - implementation project(':commonutil') - implementation project(':commonwidget') -} -repositories { - mavenCentral() -} diff --git a/basecommon/src/androidTest/java/com/wrbug/developerhelper/basecommon/ExampleInstrumentedTest.java b/basecommon/src/androidTest/java/com/wrbug/developerhelper/basecommon/ExampleInstrumentedTest.java deleted file mode 100644 index 7e11c97..0000000 --- a/basecommon/src/androidTest/java/com/wrbug/developerhelper/basecommon/ExampleInstrumentedTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.wrbug.developerhelper.basecommon; - -import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import static org.junit.Assert.*; - -/** - * Instrumented test, which will execute on an Android device. - * - * @see Testing documentation - */ -@RunWith(AndroidJUnit4.class) -public class ExampleInstrumentedTest { - @Test - public void useAppContext() { - // Context of the app under test. - Context appContext = InstrumentationRegistry.getTargetContext(); - - assertEquals("com.wrbug.developerhelper.basecommon.test", appContext.getPackageName()); - } -} diff --git a/basecommon/src/main/java/com/wrbug/developerhelper/basecommon/AppCompatActivityExt.kt b/basecommon/src/main/java/com/wrbug/developerhelper/basecommon/AppCompatActivityExt.kt deleted file mode 100644 index e81bde5..0000000 --- a/basecommon/src/main/java/com/wrbug/developerhelper/basecommon/AppCompatActivityExt.kt +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2017 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.wrbug.developerhelper.basecommon - -import androidx.annotation.IdRes -import androidx.appcompat.app.ActionBar -import androidx.appcompat.app.AppCompatActivity -import androidx.fragment.app.Fragment -import androidx.fragment.app.FragmentManager -import androidx.fragment.app.FragmentTransaction -import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProviders - -/** - * Various extension functions for AppCompatActivity. - */ - - -fun AppCompatActivity.setupActionBar(@IdRes toolbarId: Int, action: ActionBar.() -> Unit) { - setSupportActionBar(findViewById(toolbarId)) - supportActionBar?.run { - setDisplayHomeAsUpEnabled(false) - action() - } -} - -fun AppCompatActivity.obtainViewModel(viewModelClass: Class) = - ViewModelProviders.of(this).get(viewModelClass) - -/** - * Runs a FragmentTransaction, then calls commit(). - */ -private inline fun FragmentManager.transact(action: FragmentTransaction.() -> Unit) { - beginTransaction().apply { - action() - }.commit() -} - - diff --git a/basecommon/src/main/java/com/wrbug/developerhelper/basecommon/BaseActivity.kt b/basecommon/src/main/java/com/wrbug/developerhelper/basecommon/BaseActivity.kt deleted file mode 100644 index 3dd8673..0000000 --- a/basecommon/src/main/java/com/wrbug/developerhelper/basecommon/BaseActivity.kt +++ /dev/null @@ -1,86 +0,0 @@ -package com.wrbug.developerhelper.basecommon - -import android.annotation.TargetApi -import android.content.pm.PackageManager -import android.os.Build -import android.os.Bundle -import android.view.View -import androidx.appcompat.app.AppCompatActivity -import com.google.android.material.snackbar.Snackbar -import java.util.ArrayList - -abstract class BaseActivity : AppCompatActivity() { - private lateinit var toastRootView: View - protected lateinit var context: BaseActivity - private var mPermissionCallback: PermissionCallback? = null - - companion object { - private const val PERMISSION_REQUEST_CODE = 0xAADF1 - } - - override fun onCreate(savedInstanceState: Bundle?) { - context = this - super.onCreate(savedInstanceState) - } - - override fun setContentView(layoutResID: Int) { - toastRootView = layoutInflater.inflate(layoutResID, null) - setContentView(toastRootView) - } - - fun showSnack(msg: String) { - Snackbar.make(toastRootView, msg, Snackbar.LENGTH_SHORT).show() - } - - fun showSnack(id: Int) { - Snackbar.make(toastRootView, id, Snackbar.LENGTH_SHORT).show() - } - - - @TargetApi(Build.VERSION_CODES.M) - fun requestPermission(permissions: Array, callback: PermissionCallback) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { - callback.granted() - return - } - val list = ArrayList() - for (permission in permissions) { - val hasPermission = checkSelfPermission(permission) - if (hasPermission != PackageManager.PERMISSION_GRANTED) { - list.add(permission) - } - } - if (list.isEmpty()) { - callback.granted() - return - } - mPermissionCallback = callback - requestPermissions(list.toTypedArray(), PERMISSION_REQUEST_CODE) - } - - override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { - if (requestCode == PERMISSION_REQUEST_CODE) { - val list = ArrayList() - for (i in grantResults.indices) { - if (grantResults[i] != PackageManager.PERMISSION_GRANTED) { - list.add(permissions[i]) - } - } - if (list.isEmpty()) { - mPermissionCallback?.granted() - return - } - mPermissionCallback?.denied(list) - return - } - super.onRequestPermissionsResult(requestCode, permissions, grantResults) - } - - abstract class PermissionCallback { - abstract fun granted() - - open fun denied(permissions: List) { - - } - } -} \ No newline at end of file diff --git a/basecommon/src/main/java/com/wrbug/developerhelper/basecommon/ToastExts.kt b/basecommon/src/main/java/com/wrbug/developerhelper/basecommon/ToastExts.kt deleted file mode 100644 index bd5c601..0000000 --- a/basecommon/src/main/java/com/wrbug/developerhelper/basecommon/ToastExts.kt +++ /dev/null @@ -1,33 +0,0 @@ -package com.wrbug.developerhelper.basecommon - -import android.app.Application -import android.os.Handler -import android.os.Looper -import androidx.annotation.StringRes -import com.wrbug.developerhelper.commonwidget.flexibletoast.FlexibleToast - -//fun Activity.showToast(msg: CharSequence?) { -// Toast.makeText(this, msg, Toast.LENGTH_SHORT).show() -// -//} - - -fun showToast(msg: CharSequence) { - BaseApp.instance.showToast(msg.toString()) -} - -fun showToast(@StringRes id: Int) { - BaseApp.instance.showToast(id) -} - -fun Application.showToast(msg: CharSequence) { - val appHandler = Handler() - val flexibleToast = FlexibleToast(this) - val builder = FlexibleToast.Builder(this).setGravity(FlexibleToast.GRAVITY_BOTTOM) - builder.setSecondText(msg.toString()) - if (Looper.myLooper() !== Looper.getMainLooper()) { - appHandler.post { flexibleToast.toastShow(builder) } - } else { - flexibleToast.toastShow(builder) - } -} diff --git a/basecommon/src/main/res/values/strings.xml b/basecommon/src/main/res/values/strings.xml deleted file mode 100644 index 717ea7a..0000000 --- a/basecommon/src/main/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - baseCommon - diff --git a/basecommon/src/test/java/com/wrbug/developerhelper/basecommon/ExampleUnitTest.java b/basecommon/src/test/java/com/wrbug/developerhelper/basecommon/ExampleUnitTest.java deleted file mode 100644 index 99a6b24..0000000 --- a/basecommon/src/test/java/com/wrbug/developerhelper/basecommon/ExampleUnitTest.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.wrbug.developerhelper.basecommon; - -import org.junit.Test; - -import static org.junit.Assert.*; - -/** - * Example local unit test, which will execute on the development machine (host). - * - * @see Testing documentation - */ -public class ExampleUnitTest { - @Test - public void addition_isCorrect() { - assertEquals(4, 2 + 2); - } -} \ No newline at end of file diff --git a/build.gradle b/build.gradle index d875d87..183b457 100644 --- a/build.gradle +++ b/build.gradle @@ -1,34 +1,11 @@ -// Top-level build file where you can add configuration options common to all sub-projects/modules. - -buildscript { - ext.kotlin_version = '1.3.11' - ext.gradle_version = '3.2.0' - repositories { - google() - jcenter() - maven { - url "https://jitpack.io" - } - } - dependencies { - classpath "com.android.tools.build:gradle:$gradle_version" - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - - // NOTE: Do not place your application dependencies here; they belong - // in the individual module build.gradle files - } -} -allprojects { - repositories { - google() - jcenter() - maven { - url "https://jitpack.io" - } - } +// Top-level build file where you can add configuration options common to all sub-projects/modules. +plugins { + id 'com.android.application' version '8.2.2' apply false + id 'com.android.library' version '8.2.2' apply false + id 'org.jetbrains.kotlin.android' version '1.9.22' apply false } task clean(type: Delete) { delete rootProject.buildDir -} +} \ No newline at end of file diff --git a/common.gradle b/common.gradle new file mode 100644 index 0000000..a84c948 --- /dev/null +++ b/common.gradle @@ -0,0 +1,19 @@ +apply plugin: 'kotlin-parcelize' + +android { + buildFeatures { + viewBinding true + } + kotlinOptions { + jvmTarget = '1.8' + } + lintOptions { + abortOnError true + disable 'UnusedResources' + ignoreWarnings true + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} \ No newline at end of file diff --git a/commonutil/build.gradle b/commonutil/build.gradle index b18203f..b39d840 100644 --- a/commonutil/build.gradle +++ b/commonutil/build.gradle @@ -1,14 +1,13 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' +apply from: "${rootDir}/common.gradle" android { - compileSdkVersion 28 - - - + namespace "com.wrbug.developerhelper.commonutil" + compileSdk 34 defaultConfig { - minSdkVersion 21 - targetSdkVersion 28 + minSdkVersion 23 + targetSdkVersion 34 versionCode 1 versionName "1.0" @@ -27,19 +26,16 @@ android { dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') - implementation 'com.jaredrummler:android-shell:1.0.0' - implementation 'androidx.appcompat:appcompat:1.0.2' + implementation 'androidx.appcompat:appcompat:1.7.0' testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test:runner:1.1.0-alpha3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0-alpha3' - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation project(':mmkv') - implementation "org.jetbrains.anko:anko-commons:0.10.8" - implementation 'com.google.code.gson:gson:2.8.5' - implementation 'com.elvishew:xlog:1.6.1' + api "org.jetbrains.anko:anko-commons:0.10.8" + api 'com.google.code.gson:gson:2.8.5' + api 'com.elvishew:xlog:1.6.1' api 'com.squareup.okhttp3:okhttp:3.12.1' api group: 'org.jsoup', name: 'jsoup', version: '1.11.3' -} -repositories { - mavenCentral() + api "io.reactivex.rxjava3:rxjava:3.0.3" + api 'io.reactivex.rxjava3:rxandroid:3.0.0' } diff --git a/commonutil/src/androidTest/java/com/wrbug/developerhelper/commonutil/ExampleInstrumentedTest.java b/commonutil/src/androidTest/java/com/wrbug/developerhelper/commonutil/ExampleInstrumentedTest.java deleted file mode 100644 index 87b387b..0000000 --- a/commonutil/src/androidTest/java/com/wrbug/developerhelper/commonutil/ExampleInstrumentedTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.wrbug.developerhelper.commonutil; - -import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import static org.junit.Assert.*; - -/** - * Instrumented test, which will execute on an Android device. - * - * @see Testing documentation - */ -@RunWith(AndroidJUnit4.class) -public class ExampleInstrumentedTest { - @Test - public void useAppContext() { - // Context of the app under test. - Context appContext = InstrumentationRegistry.getTargetContext(); - - assertEquals("com.wrbug.developerhelper.commonutil.test", appContext.getPackageName()); - } -} diff --git a/commonutil/src/main/AndroidManifest.xml b/commonutil/src/main/AndroidManifest.xml index 68282c7..cf5f98d 100644 --- a/commonutil/src/main/AndroidManifest.xml +++ b/commonutil/src/main/AndroidManifest.xml @@ -1,2 +1,8 @@ + xmlns:tools="http://schemas.android.com/tools" + package="com.wrbug.developerhelper.commonutil"> + + + diff --git a/commonutil/src/main/assets/busybox_arm64 b/commonutil/src/main/assets/busybox_arm64 new file mode 100644 index 0000000..cf9e5be Binary files /dev/null and b/commonutil/src/main/assets/busybox_arm64 differ diff --git a/commonutil/src/main/assets/busybox_x86_64 b/commonutil/src/main/assets/busybox_x86_64 new file mode 100755 index 0000000..77523e2 Binary files /dev/null and b/commonutil/src/main/assets/busybox_x86_64 differ diff --git a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/AppInfoManager.kt b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/AppInfoManager.kt index ab0b1d1..9cbce45 100644 --- a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/AppInfoManager.kt +++ b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/AppInfoManager.kt @@ -4,13 +4,12 @@ import com.wrbug.developerhelper.commonutil.entity.ApkInfo import com.wrbug.developerhelper.commonutil.shell.ShellManager import java.io.File -object AppInfoManager { - private val appMap = HashMap() +object AppInfoManager { /** * 获取所有应用 */ - fun getAllApps(): HashMap { + private fun getAllApps(): HashMap { val apkMap = HashMap() val pManager = CommonUtils.application.packageManager // 获取手机内所有应用 @@ -21,18 +20,21 @@ object AppInfoManager { return apkMap } - fun getAppByPackageName(packageName: String): ApkInfo? { - if (appMap.containsKey(packageName)) { - return appMap[packageName] - } - appMap.putAll(getAllApps()) + fun isInstalled(packageName: String): Boolean { + return getAppByPackageName(packageName) != null + } - return appMap[packageName] + fun getAppByPackageName(packageName: String): ApkInfo? { + return getAllApps()[packageName] } fun getSharedPreferencesFiles(packageName: String): Array { - val path = "/data/data/$packageName/shared_prefs" + return getSharedPreferencesFiles(Constant.dataDir, packageName) + } + + private fun getSharedPreferencesFiles(dir: String, packageName: String): Array { + val path = "$dir/$packageName/shared_prefs" val list = ShellManager.lsDir(path) val files = ArrayList() for (file in list) { @@ -46,5 +48,4 @@ object AppInfoManager { return files.toTypedArray() } - } \ No newline at end of file diff --git a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/AppManagerUtils.kt b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/AppManagerUtils.kt index 1a50b09..06e1e1f 100644 --- a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/AppManagerUtils.kt +++ b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/AppManagerUtils.kt @@ -3,6 +3,7 @@ package com.wrbug.developerhelper.commonutil import android.content.Context import android.content.Intent import android.net.Uri +import android.widget.Toast import com.wrbug.developerhelper.commonutil.shell.ShellManager @@ -20,4 +21,18 @@ object AppManagerUtils { fun forceStopApp(packageName: String): Boolean { return ShellManager.forceStopApp(packageName) } + + fun restartApp(context: Context, packageName: String) { + if (!forceStopApp(packageName)) { + Toast.makeText(context, "重启失败", Toast.LENGTH_SHORT).show() + return + } + startApp(context, packageName) + } + + fun startApp(context: Context, packageName: String) { + val intent = context.packageManager.getLaunchIntentForPackage(packageName) + intent?.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + context.startActivity(intent) + } } \ No newline at end of file diff --git a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/ApplicationLifeCycleCallback.kt b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/ApplicationLifeCycleCallback.kt new file mode 100644 index 0000000..ad4562c --- /dev/null +++ b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/ApplicationLifeCycleCallback.kt @@ -0,0 +1,10 @@ +package com.wrbug.developerhelper.commonutil + +import android.app.Application +import android.content.Context + +interface ApplicationLifeCycleCallback { + fun attachBaseContext(context: Context) + + fun onCreate(application: Application) +} \ No newline at end of file diff --git a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/BundleExt.kt b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/BundleExt.kt new file mode 100644 index 0000000..0c9706b --- /dev/null +++ b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/BundleExt.kt @@ -0,0 +1,43 @@ +package com.wrbug.developerhelper.commonutil + +import android.content.Intent +import android.os.Build +import android.os.Bundle +import android.os.Parcelable +import androidx.core.content.IntentCompat +import androidx.core.os.BundleCompat +import java.io.Serializable + + +inline fun Bundle.getParcelableCompat(key: String): T? { + return BundleCompat.getParcelable(this, key, T::class.java) +} + +inline fun Bundle.getSerializableCompat(key: String): T? { + return if (Build.VERSION.SDK_INT >= 33) { + getSerializable(key, T::class.java) + } else { + getSerializable(key) as T? + } +} + + +inline fun Intent.getSerializableCompat(key: String): T? { + return if (Build.VERSION.SDK_INT >= 33) { + getSerializableExtra(key, T::class.java) + } else { + getSerializableExtra(key) as T? + } +} + +inline fun Intent.getParcelableCompat(key: String): T? { + return IntentCompat.getParcelableExtra(this, key, T::class.java) +} + +inline fun Intent.getParcelableArrayListCompat(key: String): List? { + return IntentCompat.getParcelableArrayListExtra(this, key, T::class.java) +} + +inline fun Bundle.getParcelableArrayListCompat(key: String): List? { + return BundleCompat.getParcelableArrayList(this, key, T::class.java) +} \ No newline at end of file diff --git a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/ClipboardUtils.kt b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/ClipboardUtils.kt index 79e719d..4f54f22 100644 --- a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/ClipboardUtils.kt +++ b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/ClipboardUtils.kt @@ -31,7 +31,7 @@ object ClipboardUtils { try { val cm = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager // 将文本内容放到系统剪贴板里。 - cm.primaryClip = ClipData.newPlainText(null, text) + cm.setPrimaryClip(ClipData.newPlainText(null, text)) } catch (t: Throwable) { } diff --git a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/CommonUtils.kt b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/CommonUtils.kt index f112bd2..d7430fa 100644 --- a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/CommonUtils.kt +++ b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/CommonUtils.kt @@ -2,10 +2,41 @@ package com.wrbug.developerhelper.commonutil import android.app.Application import android.content.Context +import com.wrbug.developerhelper.commonutil.entity.CpuABI +import com.wrbug.developerhelper.commonutil.shell.ShellUtils +import org.jetbrains.anko.doAsync + object CommonUtils { lateinit var application: Application fun register(ctx: Context) { application = ctx.applicationContext as Application + releaseBusyBox(ctx) + } + + private fun releaseBusyBox(ctx: Context) { + doAsync { + val name = when (getCPUABI()) { + CpuABI.ARM -> "busybox_arm64" + CpuABI.X86 -> "busybox_x86_64" + } + val data = ctx.resources.assets.open(name).readBytes() + val file = ShellUtils.busyBoxFile + file.writeBytes(data) + ShellUtils.run("chmod +x " + file.absolutePath) + } + } + + + private fun getCPUABI(): CpuABI { + val result = ShellUtils.run("getprop ro.product.cpu.abi") + if (result.isSuccessful.not()) { + return CpuABI.ARM + } + return if (result.getStdout().contains("x86")) { + CpuABI.X86 + } else { + CpuABI.ARM + } } } \ No newline at end of file diff --git a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/Constant.kt b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/Constant.kt new file mode 100644 index 0000000..bb71dcf --- /dev/null +++ b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/Constant.kt @@ -0,0 +1,23 @@ +package com.wrbug.developerhelper.commonutil + +import android.os.Build + +object Constant { + const val ONE_KB = 1024L + const val ONE_MB = ONE_KB * 1024 + const val ONE_GB = ONE_MB * 1024 + private const val DATA_MIRROR_DIR = "/data_mirror/data_ce/null/0" + private const val DATA_DIR = "/data/data" + + fun getDataDir(packageName: String): String { + return "$dataDir/$packageName" + } + + val dataDir by lazy { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + DATA_MIRROR_DIR + } else { + DATA_DIR + } + } +} \ No newline at end of file diff --git a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/FileExt.kt b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/FileExt.kt new file mode 100644 index 0000000..ec660ce --- /dev/null +++ b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/FileExt.kt @@ -0,0 +1,9 @@ +package com.wrbug.developerhelper.commonutil + +import java.io.File + +fun File.safeRead(): String { + return runCatching { + readText() + }.getOrDefault("") +} \ No newline at end of file diff --git a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/GlobalEvent.kt b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/GlobalEvent.kt new file mode 100644 index 0000000..23e7cfb --- /dev/null +++ b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/GlobalEvent.kt @@ -0,0 +1,25 @@ +package com.wrbug.developerhelper.commonutil + +object GlobalEvent { + private val cache = hashMapOf Unit>>() + + fun register(action: Action, bindKey: Any, callback: () -> Unit) { + cache[action] = (cache[action] ?: HashMap()).apply { + put(bindKey, callback) + } + } + + fun unRegister(action: Action, bindKey: Any) { + cache[action]?.remove(bindKey) + } + + fun postEvent(action: Action) { + cache[action]?.values?.forEach { + it() + } + } + + enum class Action { + CloseAll + } +} \ No newline at end of file diff --git a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/JsonHelper.kt b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/JsonHelper.kt index 62be985..0d743bf 100644 --- a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/JsonHelper.kt +++ b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/JsonHelper.kt @@ -3,6 +3,7 @@ package com.wrbug.developerhelper.commonutil import com.google.gson.Gson import com.google.gson.JsonElement import com.google.gson.JsonObject +import com.google.gson.reflect.TypeToken import java.lang.reflect.Type @@ -12,6 +13,19 @@ import java.lang.reflect.Type * @author wrbug * @since 2017/9/29 */ + +fun Any.toJson(): String? { + return try { + JsonHelper.toJson(this) + } catch (t: Throwable) { + null + } +} + +inline fun String.fromJson(): T? { + return JsonHelper.fromJson(this, object : TypeToken() {}.type) +} + object JsonHelper { val gson = Gson() diff --git a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/LogUtilsExt.kt b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/LogUtilsExt.kt index ef9a1a4..b9acf57 100644 --- a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/LogUtilsExt.kt +++ b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/LogUtilsExt.kt @@ -1,13 +1,26 @@ package com.wrbug.developerhelper.commonutil +import com.elvishew.xlog.LogConfiguration +import com.elvishew.xlog.LogLevel import com.elvishew.xlog.XLog +import com.elvishew.xlog.internal.DefaultsFactory import com.wrbug.developerhelper.commonutil.JsonHelper +private var init = false fun Any.print() { + if (!init) { + XLog.init( + LogConfiguration.Builder().logLevel(LogLevel.ALL).tag("developerHelper.print-->").build(), + DefaultsFactory.createPrinter() + ) + init = true + } + XLog.i("----------易开发Log----------") when (this) { is CharSequence, is Number, is Boolean -> XLog.i(this) + is Throwable -> XLog.e(this) else -> XLog.json(JsonHelper.toJson(this)) } XLog.i("----------结束----------") diff --git a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/NotificationExt.kt b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/NotificationExt.kt new file mode 100644 index 0000000..cc0d8c7 --- /dev/null +++ b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/NotificationExt.kt @@ -0,0 +1,54 @@ +package com.wrbug.developerhelper.commonutil + + +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.content.Context +import android.os.Build +import androidx.annotation.RequiresApi +import androidx.core.app.NotificationCompat + + +fun notifyNotification( + context: Context, + channelId: String, + channelName: String, + notificationId: Int, + block: NotificationCompat.Builder.() -> Unit +) { + val notification = createNotification(context, channelId, channelName, block) + val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + manager.notify(notificationId, notification) +} + + +fun createNotification( + context: Context, + channelId: String, + channelName: String, + block: NotificationCompat.Builder.() -> Unit +): Notification { + val builder = NotificationCompat.Builder(context, channelId) + block.invoke(builder) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + createNotificationChannel(context, channelId, channelName) + } + return builder.build() +} + +@RequiresApi(Build.VERSION_CODES.O) +private fun createNotificationChannel( + context: Context, + channelId: String, + channelName: String +): NotificationChannel { + val notificationManager = + context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + val channel = + NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_LOW) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + notificationManager.createNotificationChannel(channel) + } + return channel +} \ No newline at end of file diff --git a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/NumberExts.kt b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/NumberExts.kt new file mode 100644 index 0000000..ebb50a5 --- /dev/null +++ b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/NumberExts.kt @@ -0,0 +1,3 @@ +package com.wrbug.developerhelper.commonutil + +fun Int?.toInt(): Int = this ?: 0 \ No newline at end of file diff --git a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/ProcessUtil.kt b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/ProcessUtil.kt new file mode 100644 index 0000000..c8164c7 --- /dev/null +++ b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/ProcessUtil.kt @@ -0,0 +1,75 @@ +package com.wrbug.developerhelper.commonutil + +import android.app.ActivityManager +import android.content.Context +import android.os.Process +import android.text.TextUtils + +import java.io.FileInputStream +import java.io.IOException + +object ProcessUtil { + + fun isMainProc(context: Context): Boolean { + val myPid = Process.myPid() + var procName = readProcName(context, myPid) + if (TextUtils.isEmpty(procName)) { + procName = readProcName(myPid) + } + return context.packageName == procName + } + + + fun readProcName(context: Context, myPid: Int): String? { + var myProcess: ActivityManager.RunningAppProcessInfo? = null + val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager + try { + val list = activityManager.runningAppProcesses + if (list != null) { + for (info in list) { + if (info.pid == myPid) { + myProcess = info + break + } + } + } + } catch (e: Exception) { + e.printStackTrace() + } + + if (myProcess != null) { + return myProcess.processName + } + + return null + } + + fun readProcName(myPid: Int=Process.myPid()): String? { + var fileInputStream: FileInputStream? = null + try { + fileInputStream = FileInputStream("/proc/$myPid/cmdline") + val buffer = ByteArray(128) + val len = fileInputStream.read(buffer) + if (len <= 0) { + return null + } + var index = 0 + while (index < buffer.size) { + if (buffer[index] > 128 || buffer[index] <= 0) { + break + } + index++ + } + return String(buffer, 0, index) + } catch (ignore: Exception) { + ignore.printStackTrace() + } finally { + try { + fileInputStream?.close() + } catch (ignore: IOException) { + } + + } + return null + } +} diff --git a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/ReflectExts.kt b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/ReflectExts.kt new file mode 100644 index 0000000..5066229 --- /dev/null +++ b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/ReflectExts.kt @@ -0,0 +1,115 @@ +package com.wrbug.developerhelper.commonutil +import java.lang.reflect.Field +import java.lang.reflect.Method + +fun Class<*>.setValue(obj: Any?, fieldName: String, value: Any?) { + this.matchField(fieldName)?.set(obj, value) +} + +fun Class.getValue(obj: T?, fieldName: String): Any? { + return this.matchField(fieldName)?.get(obj) +} + +fun Any.getFieldValue(fieldName: String): Any? { + return javaClass.getValue(this, fieldName) +} + +fun Any.setFieldValue(fieldName: String, value: Any?) { + javaClass.setValue(this, fieldName, value) +} + +fun Any.callMethod(methodName: String, vararg args: Any?): Any? { + return this.javaClass.matchMethod(methodName, *args)?.invoke(this, *args) +} + +fun Class.callMethod(obj: T?, methodName: String, vararg args: Any?): Any? { + return this.matchMethod(methodName, *args)?.invoke(obj, *args) +} + +fun Class<*>.matchMethod(methodName: String, vararg args: Any?): Method? { + return findDeclaredMethod(methodName, *args) ?: findMethod(methodName, *args) +} + +fun Class<*>.matchField(fieldName: String): Field? { + return findDeclaredField(fieldName) ?: findField(fieldName) +} + +fun Class<*>.findDeclaredField(fieldName: String): Field? { + return runCatching { + this.getDeclaredField(fieldName).apply { + if (!isAccessible) { + isAccessible = true + } + } + }.getOrElse { + if (this == Any::class.java) { + return null + } + this.superclass.findDeclaredField(fieldName) + } +} + +fun Class<*>.findDeclaredMethod(methodName: String, vararg args: Any?): Method? { + val method = + this.declaredMethods.asSequence() + .filter { it.name == methodName && it.parameterCount == args.size } + .find { + val size = args.size + for (i in 0 until size) { + val arg = args[i] ?: continue + if (!it.parameterTypes[i].isAssignableFrom(arg.javaClass)) { + return@find false + } + } + return@find true + }?.apply { + if (!isAccessible) { + isAccessible = true + } + } + return method ?: if (this == Any::class.java) { + null + } else { + superclass.findDeclaredMethod(methodName, *args) + } +} + +fun Class<*>.findField(fieldName: String): Field? { + return runCatching { + this.getField(fieldName).apply { + if (!isAccessible) { + isAccessible = true + } + } + }.getOrElse { + if (this == Any::class.java) { + return null + } + this.superclass.findField(fieldName) + } +} + +fun Class<*>.findMethod(methodName: String, vararg args: Any?): Method? { + val method = + this.methods.asSequence() + .filter { it.name == methodName && it.parameterCount == args.size } + .find { + val size = args.size + for (i in 0..size) { + val arg = args[i] ?: continue + if (!it.parameterTypes[i].isAssignableFrom(arg.javaClass)) { + return@find false + } + } + return@find true + }?.apply { + if (!isAccessible) { + isAccessible = true + } + } + return method ?: if (this == Any::class.java) { + null + } else { + superclass.findDeclaredMethod(methodName, *args) + } +} \ No newline at end of file diff --git a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/RootUtils.kt b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/RootUtils.kt new file mode 100644 index 0000000..6f7864a --- /dev/null +++ b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/RootUtils.kt @@ -0,0 +1,9 @@ +package com.wrbug.developerhelper.commonutil + +import com.wrbug.developerhelper.mmkv.ConfigKv +import com.wrbug.developerhelper.mmkv.manager.MMKVManager + +object RootUtils { + val configKv = MMKVManager.get(ConfigKv::class.java) + fun isRoot() = configKv.isOpenRoot() +} \ No newline at end of file diff --git a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/RxJavaExt.kt b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/RxJavaExt.kt new file mode 100644 index 0000000..4bfefc3 --- /dev/null +++ b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/RxJavaExt.kt @@ -0,0 +1,31 @@ +package com.wrbug.developerhelper.commonutil + +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers +import io.reactivex.rxjava3.core.Single +import io.reactivex.rxjava3.core.SingleEmitter +import io.reactivex.rxjava3.core.SingleOnSubscribe +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.disposables.Disposable +import io.reactivex.rxjava3.schedulers.Schedulers + +fun Single.runOnIO(): Single { + return this.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()) +} + +fun Single.observeOnMain(): Single { + return this.observeOn(AndroidSchedulers.mainThread()) +} + +fun safeCreateSingle(source: (SingleEmitter) -> Unit): Single { + return Single.create { emit -> + runCatching { + source(emit) + }.getOrElse { + emit.onError(it) + } + } +} + +fun Disposable.addTo(compositeDisposable: CompositeDisposable) { + compositeDisposable.add(this) +} \ No newline at end of file diff --git a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/ShellUtils.kt b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/ShellUtils.kt deleted file mode 100644 index 8c36c0f..0000000 --- a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/ShellUtils.kt +++ /dev/null @@ -1,60 +0,0 @@ -package com.wrbug.developerhelper.commonutil - -import com.jaredrummler.android.shell.CommandResult -import com.jaredrummler.android.shell.Shell -import com.wrbug.developerhelper.mmkv.ConfigKv -import com.wrbug.developerhelper.mmkv.manager.MMKVManager -import org.jetbrains.anko.doAsync - -object ShellUtils { - val configKv = MMKVManager.get(ConfigKv::class.java) - fun run(cmds: Array, callback: ShellResultCallback) { - doAsync { - val run = Shell.SH.run(*cmds) - callback.onComplete(run) - } - } - - fun run(vararg cmds: String): CommandResult { - return Shell.SH.run(*cmds) - } - - fun runWithSu(cmds: Array, callback: ShellResultCallback) { - if (!configKv.isOpenRoot()) { - callback.onError("未开启root权限") - return - } - doAsync { - val run = Shell.SU.run(*cmds) - callback.onComplete(run) - } - } - - fun runWithSuIgnoreSetting(vararg cmds: String): CommandResult { - return Shell.SU.run(*cmds) - } - - - fun runWithSu(vararg cmds: String): CommandResult { - if (configKv.isOpenRoot().not()) { - return CommandResult(arrayListOf("未开启root权限"), arrayListOf("未开启root权限"), 1) - } - return Shell.SU.run(*cmds) - } - - fun isRoot(): Boolean { - return Shell.SU.available() - } - - abstract class ShellResultCallback(vararg args: Any) { - protected var args = args - open fun onComplete(result: CommandResult) { - - } - - open fun onError(msg: String) { - - } - } - -} \ No newline at end of file diff --git a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/SpannableBuilder.kt b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/SpannableBuilder.kt new file mode 100644 index 0000000..acd44ff --- /dev/null +++ b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/SpannableBuilder.kt @@ -0,0 +1,125 @@ +package com.wrbug.developerhelper.commonutil + +import android.content.Context +import android.graphics.Typeface +import android.text.Spannable +import android.text.SpannableStringBuilder +import android.text.Spanned +import android.text.TextPaint +import android.text.style.ClickableSpan +import android.text.style.ForegroundColorSpan +import android.text.style.StrikethroughSpan +import android.text.style.StyleSpan +import android.text.style.TextAppearanceSpan +import android.view.View + +class SpannableBuilder private constructor(private val context: Context, private val text: String) { + + private val spanMap = hashMapOf>>() + + companion object { + fun with(context: Context, strRes: Int): SpannableBuilder { + return SpannableBuilder(context, strRes) + } + + fun with(context: Context, text: String): SpannableBuilder { + return SpannableBuilder(context, text) + } + } + + private var strRes: Int = -1 + + private constructor(context: Context, strRes: Int) : this(context, "") { + this.strRes = strRes + } + + fun addSpanWithTextAppearance( + value: String, + textAppearance: Int, + color: Int? = null, + index: Int = 0 + ): SpannableBuilder { + addSpan(value, TextAppearanceSpan(context, textAppearance), index) + if (color != null) { + addSpan(value, ForegroundColorSpan(color), index) + } + return this + } + + fun addSpanWithClickListener( + key: String, + linkColor: Int, + index: Int = 0, + listener: () -> Unit + ): SpannableBuilder { + addSpan(key, object : ClickableSpan() { + override fun onClick(widget: View) { + listener() + } + + override fun updateDrawState(ds: TextPaint) { + ds.isUnderlineText = true + ds.color = linkColor + } + }, index) + return this + } + + fun addSpanWithColor(value: String, color: Int, index: Int = 0): SpannableBuilder { + addSpan(value, ForegroundColorSpan(color), index) + return this + } + + + fun addSpanWithDeleteLine(value: String, index: Int = 0): SpannableBuilder { + addSpan(value, StrikethroughSpan(), index) + return this + } + + fun addSpanWithBold(value: String, index: Int = 0): SpannableBuilder { + addSpan(value, StyleSpan(Typeface.BOLD), index) + return this + } + + fun addCustomSpan(value: String, what: Any, index: Int = 0): SpannableBuilder { + addSpan(value, what, index) + return this + } + + private fun addSpan(key: String, what: Any, index: Int) { + spanMap[key] = (spanMap[key] ?: arrayListOf()).apply { add(index to what) } + } + + fun build(): Spannable { + val originStr = if (strRes == -1) { + text + } else { + context.getString(strRes) + } + val spannableBuilder = SpannableStringBuilder(originStr) + spanMap.forEach { (key, pair) -> + pair.forEach { (index, what) -> + val strIndex = index.let { + var i = it + var currentIndex = 0 + while (i != 0) { + i-- + currentIndex = originStr.indexOf(key, currentIndex) + key.length + } + originStr.indexOf(key, currentIndex) + } + if (strIndex == -1) { + return@forEach + } + spannableBuilder.setSpan( + what, + strIndex, + strIndex + key.length, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE + ) + } + + } + return spannableBuilder + } +} \ No newline at end of file diff --git a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/StringConvertUtils.kt b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/StringConvertUtils.kt index 4aca828..4161d02 100644 --- a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/StringConvertUtils.kt +++ b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/StringConvertUtils.kt @@ -2,9 +2,9 @@ package com.wrbug.developerhelper.commonutil import java.math.BigDecimal import java.math.BigInteger +import java.math.RoundingMode import java.util.regex.Pattern - fun String.isInt(): Boolean { if (isNumber()) { return BigInteger(this).toLong() < Integer.MAX_VALUE && BigInteger(this).toLong() > Integer.MIN_VALUE @@ -39,32 +39,48 @@ fun String.toBoolean(): Boolean { return false } -fun String.toInt(): Int { +fun String?.toInt(): Int { + this ?: return 0 if (isInt()) { return BigInteger(this).toInt() } return 0 } - -fun String.toLong(): Long { +fun String?.toLong(): Long { + this ?: return 0 if (isNumber()) { return BigInteger(this).toLong() } return 0L } - -fun String.toFloat(): Float { +fun String?.toFloat(): Float { + this ?: return 0F if (isDecimal()) { return BigDecimal(this).toFloat() } return 0F } -fun String.toDouble(): Double { +fun String?.toDouble(): Double { + this ?: return 0.0 if (isDecimal()) { return BigDecimal(this).toDouble() } return 0.0 +} + +fun Long.byteToShowSize(): String { + if (this >= Constant.ONE_GB) { + return (this.toDouble() / Constant.ONE_GB).toBigDecimal() + .setScale(2, RoundingMode.HALF_DOWN).toPlainString() + " GB" + } + if (this >= Constant.ONE_MB) { + return (this.toDouble() / Constant.ONE_MB).toBigDecimal() + .setScale(2, RoundingMode.HALF_DOWN).toPlainString() + " MB" + } + return (this.toDouble() / Constant.ONE_KB).toBigDecimal() + .setScale(2, RoundingMode.HALF_DOWN).toPlainString() + " KB" + } \ No newline at end of file diff --git a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/StringExt.kt b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/StringExt.kt new file mode 100644 index 0000000..5df7270 --- /dev/null +++ b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/StringExt.kt @@ -0,0 +1,8 @@ +package com.wrbug.developerhelper.commonutil + +fun String?.ifNotEmpty(call: (String) -> String): String { + if (isNullOrEmpty()) { + return "" + } + return call(this) +} \ No newline at end of file diff --git a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/UiUtils.kt b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/UiUtils.kt index be487bf..fe73ddd 100644 --- a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/UiUtils.kt +++ b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/UiUtils.kt @@ -10,21 +10,17 @@ fun Context.dp2px(dpVal: Float): Int = UiUtils.dp2px(this, dpVal) fun Fragment.dp2px(dpVal: Float): Int = UiUtils.dp2px(activity!!, dpVal) fun Dialog.dp2px(dpVal: Float): Int = UiUtils.dp2px(context, dpVal) fun View.dp2px(dpVal: Float): Int = UiUtils.dp2px(context, dpVal) +fun Float.dpInt(context: Context) = UiUtils.dp2px(context, this) +fun Int.dpInt(context: Context = CommonUtils.application) = UiUtils.dp2px(context, toFloat()) object UiUtils { - + private var statusBarHeight: Int = -1 fun dp2px(context: Context = CommonUtils.application, dpVal: Float): Int { return TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_DIP, - dpVal, - context.resources?.displayMetrics + TypedValue.COMPLEX_UNIT_DIP, dpVal, context.resources?.displayMetrics ).toInt() } - fun sp2px(context: Context = CommonUtils.application, spVal: Float): Int { - return TypedValue.applyDimension(2, spVal, context.resources?.displayMetrics).toInt() - } - fun px2dp(context: Context = CommonUtils.application, pxVal: Float): Float { val scale = context.resources?.displayMetrics?.density!! return pxVal / scale @@ -54,12 +50,16 @@ object UiUtils { * 获取状态栏的高度 */ fun getStatusHeight(context: Context = CommonUtils.application): Int { + if (statusBarHeight != -1) { + return statusBarHeight + } var result: Int? = 10 val resourceId = context.resources?.getIdentifier("status_bar_height", "dimen", "android") ?: 0 if (resourceId > 0) { result = context.resources?.getDimensionPixelOffset(resourceId) } - return result ?: 0 + statusBarHeight = result ?: 0 + return statusBarHeight } } \ No newline at end of file diff --git a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/ZipUtil.java b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/ZipUtil.java new file mode 100644 index 0000000..f61ac17 --- /dev/null +++ b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/ZipUtil.java @@ -0,0 +1,229 @@ +package com.wrbug.developerhelper.commonutil; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.zip.CRC32; +import java.util.zip.CheckedOutputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; +import java.util.zip.ZipOutputStream; + +public class ZipUtil { + + /** + * 缓冲器大小 + */ + private static final int BUFFER = 512; + + /** + * 压缩得到的文件的后缀名 + */ + private static final String SUFFIX = ".zip"; + + /** + * 得到源文件路径的所有文件 + * + * @param dirFile 压缩源文件路径 + */ + public static List getAllFile(File dirFile) { + + List fileList = new ArrayList<>(); + + File[] files = dirFile.listFiles(); + for (File file : files) {//文件 + if (file.isFile()) { + fileList.add(file); + System.out.println("add file:" + file.getName()); + } else {//目录 + if (file.listFiles().length != 0) {//非空目录 + fileList.addAll(getAllFile(file));//把递归文件加到fileList中 + } else {//空目录 + fileList.add(file); + System.out.println("add empty dir:" + file.getName()); + } + } + } + return fileList; + } + + /** + * 获取相对路径 + * + * @param dirPath 源文件路径 + * @param file 准备压缩的单个文件 + */ + public static String getRelativePath(String dirPath, File file) { + File dirFile = new File(dirPath); + String relativePath = file.getName(); + + while (true) { + file = file.getParentFile(); + if (file == null) break; + if (file.equals(dirFile)) { + break; + } else { + relativePath = file.getName() + "/" + relativePath; + } + } + return relativePath; + } + + + /** + * @param destPath 解压目标路径 + * @param fileName 解压文件的相对路径 + */ + public static File createFile(String destPath, String fileName) { + + String[] dirs = fileName.split("/");//将文件名的各级目录分解 + File file = new File(destPath); + + if (dirs.length > 1) {//文件有上级目录 + for (int i = 0; i < dirs.length - 1; i++) { + file = new File(file, dirs[i]);//依次创建文件对象知道文件的上一级目录 + } + + if (!file.exists()) { + file.mkdirs();//文件对应目录若不存在,则创建 + try { + System.out.println("mkdirs: " + file.getCanonicalPath()); + } catch (IOException e) { + e.printStackTrace(); + } + } + + file = new File(file, dirs[dirs.length - 1]);//创建文件 + + return file; + } else { + if (!file.exists()) {//若目标路径的目录不存在,则创建 + file.mkdirs(); + try { + System.out.println("mkdirs: " + file.getCanonicalPath()); + } catch (IOException e) { + e.printStackTrace(); + } + } + + file = new File(file, dirs[0]);//创建文件 + + return file; + } + + } + + /** + * 没有指定压缩目标路径进行压缩,用默认的路径进行压缩 + * + * @param dirPath 压缩源文件路径 + */ + public static void compress(String dirPath) { + + int firstIndex = dirPath.indexOf("/"); + int lastIndex = dirPath.lastIndexOf("/"); + String zipFileName = dirPath.substring(0, firstIndex + 1) + dirPath.substring(lastIndex + 1); + compress(dirPath, zipFileName); + } + + /** + * 压缩文件 + * + * @param dirPath 压缩源文件路径 + * @param zipFileName 压缩目标文件路径 + */ + public static void compress(String dirPath, String zipFileName) { + + + zipFileName = zipFileName + SUFFIX;//添加文件的后缀名 + + File dirFile = new File(dirPath); + List fileList = getAllFile(dirFile); + + byte[] buffer = new byte[BUFFER]; + ZipEntry zipEntry = null; + int readLength = 0; //每次读取出来的长度 + + try { + // 对输出文件做CRC32校验 + CheckedOutputStream cos = new CheckedOutputStream(new FileOutputStream( + zipFileName), new CRC32()); + ZipOutputStream zos = new ZipOutputStream(cos); + + for (File file : fileList) { + + if (file.isFile()) { //若是文件,则压缩文件 + + zipEntry = new ZipEntry(getRelativePath(dirPath, file)); // + zipEntry.setSize(file.length()); + zipEntry.setTime(file.lastModified()); + zos.putNextEntry(zipEntry); + + InputStream is = new BufferedInputStream(new FileInputStream(file)); + + while ((readLength = is.read(buffer, 0, BUFFER)) != -1) { + zos.write(buffer, 0, readLength); + } + is.close(); + System.out.println("file compress:" + file.getCanonicalPath()); + } else { //若是空目录,则写入zip条目中 + + zipEntry = new ZipEntry(getRelativePath(dirPath, file)); + zos.putNextEntry(zipEntry); + System.out.println("dir compress: " + file.getCanonicalPath() + "/"); + } + } + zos.close(); //最后得关闭流,不然压缩最后一个文件会出错 + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * 解压 + */ + public static void decompress(String zipFileName, String destPath) { + + try { + + zipFileName = zipFileName + SUFFIX; + ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFileName)); + ZipEntry zipEntry = null; + byte[] buffer = new byte[BUFFER];//缓冲器 + int readLength = 0;//每次读出来的长度 + while ((zipEntry = zis.getNextEntry()) != null) { + if (zipEntry.isDirectory()) {//若是目录 + File file = new File(destPath + "/" + zipEntry.getName()); + if (!file.exists()) { + file.mkdirs(); + System.out.println("mkdirs:" + file.getCanonicalPath()); + continue; + } + }//若是文件 + File file = createFile(destPath, zipEntry.getName()); + System.out.println("file created: " + file.getCanonicalPath()); + OutputStream os = new FileOutputStream(file); + while ((readLength = zis.read(buffer, 0, BUFFER)) != -1) { + os.write(buffer, 0, readLength); + } + os.close(); + System.out.println("file uncompressed: " + file.getCanonicalPath()); + } + + + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/entity/ApkInfo.kt b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/entity/ApkInfo.kt index 5e8b861..b7a912c 100644 --- a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/entity/ApkInfo.kt +++ b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/entity/ApkInfo.kt @@ -3,16 +3,16 @@ package com.wrbug.developerhelper.commonutil.entity import android.content.pm.ApplicationInfo import android.content.pm.PackageInfo import android.graphics.drawable.Drawable -import android.os.Parcel import android.os.Parcelable import com.wrbug.developerhelper.commonutil.CommonUtils +import kotlinx.parcelize.Parcelize -class ApkInfo(val packageInfo: PackageInfo, val applicationInfo: ApplicationInfo) : Parcelable { - - constructor(parcel: Parcel) : this( - parcel.readParcelable(PackageInfo::class.java.classLoader), - parcel.readParcelable(ApplicationInfo::class.java.classLoader) - ) +@Parcelize +class ApkInfo( + val packageInfo: PackageInfo, + val applicationInfo: ApplicationInfo, + var topActivity: String = "" +) : Parcelable { fun getIco(): Drawable { return applicationInfo.loadIcon(CommonUtils.application.packageManager) @@ -20,26 +20,15 @@ class ApkInfo(val packageInfo: PackageInfo, val applicationInfo: ApplicationInfo fun getAppName(): String { val label = CommonUtils.application.packageManager.getApplicationLabel(applicationInfo) - return if (label == null) "" else label.toString() - } - - override fun writeToParcel(parcel: Parcel, flags: Int) { - parcel.writeParcelable(packageInfo, flags) - parcel.writeParcelable(applicationInfo, flags) + return label.toString() } - override fun describeContents(): Int { - return 0 + fun isSystemApp(): Boolean { + return applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM == 1 } - companion object CREATOR : Parcelable.Creator { - override fun createFromParcel(parcel: Parcel): ApkInfo { - return ApkInfo(parcel) - } - - override fun newArray(size: Int): Array { - return arrayOfNulls(size) - } + fun generateBackupApkFileName(): String { + return packageInfo.versionName + ".apk" } } \ No newline at end of file diff --git a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/entity/CpuABI.kt b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/entity/CpuABI.kt new file mode 100644 index 0000000..952e1b8 --- /dev/null +++ b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/entity/CpuABI.kt @@ -0,0 +1,5 @@ +package com.wrbug.developerhelper.commonutil.entity + +enum class CpuABI { + ARM,X86 +} \ No newline at end of file diff --git a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/entity/FragmentInfo.kt b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/entity/FragmentInfo.kt index ab23081..cea0527 100644 --- a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/entity/FragmentInfo.kt +++ b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/entity/FragmentInfo.kt @@ -1,71 +1,23 @@ package com.wrbug.developerhelper.commonutil.entity -import android.os.Parcel import android.os.Parcelable - -class FragmentInfo() : Parcelable { - var name = "" - var fragmentId = "" - var containerId = "" - var tag = "" - var state = 0 - var index = 0 - var who = "" - var backStackNesting = 0 - var added = false - var removing = false - var fromLayout = false - var inLayout = false - var hidden = true - var detached = false - - constructor(parcel: Parcel) : this() { - name = parcel.readString() - fragmentId = parcel.readString() - containerId = parcel.readString() - tag = parcel.readString() - state = parcel.readInt() - index = parcel.readInt() - who = parcel.readString() - backStackNesting = parcel.readInt() - added = parcel.readByte() != 0.toByte() - removing = parcel.readByte() != 0.toByte() - fromLayout = parcel.readByte() != 0.toByte() - inLayout = parcel.readByte() != 0.toByte() - hidden = parcel.readByte() != 0.toByte() - detached = parcel.readByte() != 0.toByte() - } - - override fun writeToParcel(parcel: Parcel, flags: Int) { - parcel.writeString(name) - parcel.writeString(fragmentId) - parcel.writeString(containerId) - parcel.writeString(tag) - parcel.writeInt(state) - parcel.writeInt(index) - parcel.writeString(who) - parcel.writeInt(backStackNesting) - parcel.writeByte(if (added) 1 else 0) - parcel.writeByte(if (removing) 1 else 0) - parcel.writeByte(if (fromLayout) 1 else 0) - parcel.writeByte(if (inLayout) 1 else 0) - parcel.writeByte(if (hidden) 1 else 0) - parcel.writeByte(if (detached) 1 else 0) - } - - override fun describeContents(): Int { - return 0 - } - - companion object CREATOR : Parcelable.Creator { - override fun createFromParcel(parcel: Parcel): FragmentInfo { - return FragmentInfo(parcel) - } - - override fun newArray(size: Int): Array { - return arrayOfNulls(size) - } - } - +import kotlinx.parcelize.Parcelize + +@Parcelize +data class FragmentInfo( + var name: String = "", + var containerId: String = "", + var tag: String = "", + var state: Int = 0, + var index: Int = 0, + var who: String = "", + var backStackNesting: Int = 0, + var added: Boolean = false, + var removing: Boolean = false, + var fromLayout: Boolean = false, + var inLayout: Boolean = false, + var hidden: Boolean = true, + var detached: Boolean = false +): Parcelable { } \ No newline at end of file diff --git a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/entity/TopActivityInfo.kt b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/entity/TopActivityInfo.kt index d5e415a..2dd2b58 100644 --- a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/entity/TopActivityInfo.kt +++ b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/entity/TopActivityInfo.kt @@ -1,36 +1,22 @@ package com.wrbug.developerhelper.commonutil.entity -import android.os.Parcel import android.os.Parcelable +import kotlinx.parcelize.Parcelize -class TopActivityInfo() : Parcelable { - var activity = "" - var viewIdHex = HashMap() +@Parcelize +class TopActivityInfo( + var packageName: String = "", + var activity: String = "", var fragments: Array? = null +): Parcelable { - constructor(parcel: Parcel) : this() { - activity = parcel.readString() - fragments = parcel.createTypedArray(FragmentInfo) - } - - override fun writeToParcel(parcel: Parcel, flags: Int) { - parcel.writeString(activity) - parcel.writeTypedArray(fragments, flags) - } - - override fun describeContents(): Int { - return 0 - } - - companion object CREATOR : Parcelable.Creator { - override fun createFromParcel(parcel: Parcel): TopActivityInfo { - return TopActivityInfo(parcel) - } - - override fun newArray(size: Int): Array { - return arrayOfNulls(size) + fun setFullActivity(ac: String) { + kotlin.runCatching { + val arr = ac.split("/") + packageName = arr[0] + activity = arr[0] + arr[1] } } - + var viewIdHex = HashMap() } \ No newline at end of file diff --git a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/shell/Callback.kt b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/shell/Callback.kt deleted file mode 100644 index 4bc41c9..0000000 --- a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/shell/Callback.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.wrbug.developerhelper.commonutil.shell - -interface Callback { - fun onSuccess(data: T) - fun onFailed(msg: String = "") {} -} \ No newline at end of file diff --git a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/shell/CommandResult.kt b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/shell/CommandResult.kt new file mode 100644 index 0000000..4894b56 --- /dev/null +++ b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/shell/CommandResult.kt @@ -0,0 +1,28 @@ +package com.wrbug.developerhelper.commonutil.shell + +data class CommandResult( + val stdout: List, + val stderr: List, + val exitCode: Int, + val details: Shell.Command.Result.Details? +) { + val isSuccessful: Boolean + get() = exitCode == 0 + + fun getStdout(): String { + return toString(stdout) + } + + /** + * Get the standard error. + * + * @return The standard error as a string. + */ + fun getStderr(): String { + return toString(stderr) + } + + private fun toString(lines: List?): String { + return lines?.joinToString("\n").orEmpty() + } +} diff --git a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/shell/Shell.kt b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/shell/Shell.kt new file mode 100644 index 0000000..4841d47 --- /dev/null +++ b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/shell/Shell.kt @@ -0,0 +1,826 @@ +package com.wrbug.developerhelper.commonutil.shell + +import com.wrbug.developerhelper.commonutil.toInt +import java.io.BufferedReader +import java.io.DataOutputStream +import java.io.IOException +import java.io.InputStream +import java.io.InputStreamReader +import java.io.OutputStream +import java.util.Collections +import java.util.UUID +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit +import java.util.concurrent.locks.ReentrantLock +import java.util.regex.Pattern + +/** Environment variable. */ +typealias Variable = String +/** Environment variable value. */ +typealias Value = String +/** A [Map] for the environment variables used in the shell. */ +typealias EnvironmentMap = Map + + +/** + * A shell starts a [Process] with the provided shell and additional/optional environment variables. + * The shell handles maintaining the [Process] and reads standard output and standard error streams, + * returning stdout, stderr, and the last exit code as a [Command.Result] when a command is complete. + * + * Example usage: + * + * val sh = Shell("sh") + * val result = sh.run("echo 'Hello, World!'") + * assert(result.isSuccess) + * assert(result.stdout() == "Hello, World") + * + * @property path The path to the shell to start. + * @property environment Map of all environment variables to include with the system environment. + * Default value is an empty map. + * @throws Shell.NotFoundException If the shell cannot be opened this runtime exception is thrown. + * @author Jared Rummler (jaredrummler@gmail.com) + * @since 05-05-2021 + */ +class Shell @Throws(NotFoundException::class) @JvmOverloads constructor( + val path: String, + val environment: EnvironmentMap = emptyMap() +) { + + + /** + * Construct a new [Shell] with optional environment variable arguments as a [Pair]. + * + * @param shell The path to the shell to start. + * @param environment varargs of all environment variables as a [Pair] which are included + * with the system environment. + */ + constructor(shell: String, vararg environment: Pair) : + this(shell, environment.toEnvironmentMap()) + + /** + * Construct a new [Shell] with optional environment variable arguments as an array. + * + * @param shell The path to the shell to start. + * @param environment varargs of all environment variables as a [Pair] which are included + * with the system environment. + */ + constructor(shell: String, environment: Array) : + this(shell, environment.toEnvironmentMap()) + + /** + * Get the current state of the shell + */ + var state: State = State.Idle + private set + + private val onResultListeners = mutableSetOf() + private val onStdOutListeners = mutableSetOf() + + private val onStdErrListeners = mutableSetOf() + private val stdin: StandardInputStream + private val stdoutReader: StreamReader + private val stderrReader: StreamReader + private var watchdog: Watchdog? = null + + private val process: Process + + init { + try { + process = runWithEnv(path, environment) + stdin = StandardInputStream(process.outputStream) + stdoutReader = StreamReader.createAndStart(THREAD_NAME_STDOUT, process.inputStream) + stderrReader = StreamReader.createAndStart(THREAD_NAME_STDERR, process.errorStream) + } catch (cause: Exception) { + throw NotFoundException(String.format(EXCEPTION_SHELL_CANNOT_OPEN, path), cause) + } + } + + /** + * Add a listener that will be invoked each time a command finishes. + * + * @param listener The listener to receive callbacks when commands finish executing. + * @return This shell instance for chaining calls. + */ + fun addOnCommandResultListener(listener: OnCommandResultListener) = apply { + onResultListeners.add(listener) + } + + /** + * Remove a listener previously added to stop receiving callbacks when commands finish. + * + * @param listener The listener registered via [addOnCommandResultListener]. + * @return This shell instance for chaining calls. + */ + fun removeOnCommandResultListener(listener: OnCommandResultListener) = apply { + onResultListeners.remove(listener) + } + + /** + * Add a listener that will be invoked each time the STDOUT stream reads a new line. + * + * @param listener The listener to receive callbacks when the STDOUT stream reads a line. + * @return This shell instance for chaining calls. + */ + fun addOnStdoutLineListener(listener: OnLineListener) = apply { + onStdOutListeners.add(listener) + } + + /** + * Remove a listener previously added to stop receiving callbacks for STDOUT read lines. + * + * @param listener The listener registered via [addOnStdoutLineListener]. + * @return This shell instance for chaining calls. + */ + fun removeOnStdoutLineListener(listener: OnLineListener) = apply { + onStdOutListeners.remove(listener) + } + + /** + * Add a listener that will be invoked each time the STDERR stream reads a new line. + * + * @param listener The listener to receive callbacks when the STDERR stream reads a line. + * @return This shell instance for chaining calls. + */ + fun addOnStderrLineListener(listener: OnLineListener) = apply { + onStdErrListeners.add(listener) + } + + /** + * Remove a listener previously added to stop receiving callbacks for STDERR read lines. + * + * @param listener The listener registered via [addOnStderrLineListener]. + * @return This shell instance for chaining calls. + */ + fun removeOnStderrLineListener(listener: OnLineListener) = apply { + onStdErrListeners.remove(listener) + } + + /** + * Run a command in the current shell and return its [result][Command.Result]. + * + * @param command The command to execute. + * @param config The [options][Command.Config] to set when running the command. + * @return The [result][Command.Result] containing stdout, stderr, status of running the command. + * @throws ClosedException if the shell was closed prior to running the command. + * @see shutdown + * @see run + */ + @Throws(ClosedException::class) + @Synchronized + fun run( + command: String, + config: Command.Config.Builder.() -> Unit, + ) = run(command, Command.Config.Builder().apply(config).create()) + + /** + * Run a command in the current shell and return its [result][Command.Result]. + * + * @param command The command to execute. + * @param config The [options][Command.Config] to set when running the command. + * @return The [result][Command.Result] containing stdout, stderr, status of running the command. + * @throws ClosedException if the shell was closed prior to running the command. + * @see shutdown + * @see run + */ + @Throws(ClosedException::class) + @Synchronized + @JvmOverloads + fun run( + command: String, + config: Command.Config = Command.Config.default(), + ): Command.Result { + // If the shell is shutdown, throw a ShellClosedException. + if (state == State.Shutdown) throw ClosedException(EXCEPTION_SHELL_SHUTDOWN) + + val stdout = Collections.synchronizedList(mutableListOf()) + val stderr = Collections.synchronizedList(mutableListOf()) + + val watchdog = Watchdog().also { watchdog = it } + var exitCode = Command.Status.INVALID + val uuid = config.uuid + + val onComplete = { marker: Command.Marker -> + when (marker.uuid) { + uuid -> + try { // Reached the end of reading the stream for the command. + if (marker.status != Command.Status.INVALID) { + exitCode = marker.status + } + } finally { + watchdog.signal() + } + } + } + + val lock = ReentrantLock() + val output = Collections.synchronizedList(mutableListOf()) + + // Function to process stderr and stdout streams. + fun onLine( + buffer: MutableList, + listeners: Set, + onLine: (line: String) -> Unit, + ) = { line: String -> + try { + lock.lock() + if (config.notify) { + listeners.forEach { listener -> listener.onLine(line) } + } + buffer.add(line) + output.add(line) + onLine(line) + } finally { + lock.unlock() + } + } + + stdoutReader.onComplete = onComplete + stderrReader.onComplete = onComplete + stdoutReader.onReadLine = onLine(stdout, onStdOutListeners, config.onStdOut) + stderrReader.onReadLine = when (config.redirectStdErr) { + true -> onLine(stdout, onStdOutListeners, config.onStdOut) + else -> onLine(stderr, onStdErrListeners, config.onStdErr) + } + + val startTime = System.currentTimeMillis() + try { + state = State.Running + // Write the command and command end marker to stdin. + write(command, "echo '$uuid' $?", "echo '$uuid' >&2") + // Wait for the result with a timeout, if provided. + if (!watchdog.await(config.timeout)) { + exitCode = Command.Status.TIMEOUT + config.onTimeout() + } + } catch (e: InterruptedException) { + exitCode = Command.Status.TERMINATED + config.onCancelled() + } finally { + this.watchdog = null + state = State.Idle + } + + if (exitCode != Command.Status.SUCCESS) { + // Exit with the error code in a subshell + // This is necessary because we send commands to signal a command was completed + write("$(exit $exitCode)") + } + + // Create the result from running the command. + val result = Command.Result.create( + uuid, + command, + stdout, + stderr, + output, + exitCode, + startTime + ) + + if (config.notify) { + onResultListeners.forEach { listener -> + listener.onResult(result) + } + } + + return result + } + + /** + * Check if the shell is idle. + * + * @return True if the shell is open but not running any commands. + */ + fun isIdle() = state is State.Idle + + /** + * Check if the shell is running a command. + * + * @return True if the shell is executing a command. + */ + fun isRunning() = state is State.Running + + /** + * Check if the shell is shutdown. + * + * @return True if the shell is closed. + * @see shutdown + */ + fun isShutdown() = state is State.Shutdown + + /** + * Check if the shell is alive and able to execute commands. + * + * @return True if the shell is running or idle. + */ + fun isAlive() = try { + process.exitValue(); false + } catch (e: IllegalThreadStateException) { + true + } + + /** + * Interrupt waiting for a command to complete. + */ + fun interrupt() { + watchdog?.abort() + } + + /** + * Shutdown the shell instance. After a shell is shutdown it can no longer execute commands + * and should be garbage collected. + */ + @Synchronized + fun shutdown() { + try { + write("exit") + process.waitFor() + stdin.closeQuietly() + onStdOutListeners.clear() + onStdErrListeners.clear() + stdoutReader.join() + stderrReader.join() + process.destroy() + } catch (ignored: IOException) { + } finally { + state = State.Shutdown + } + } + + private fun write(vararg commands: String) = try { + commands.forEach { command -> stdin.write(command) } + stdin.flush() + } catch (ignored: IOException) { + } + + private fun DataOutputStream.closeQuietly() = try { + close() + } catch (ignored: IOException) { + } + + /** + * Contains data classes used for running commands in a [Shell]. + * + * @see Command.Result + * @see Command.Config + * @see Command.Status + */ + object Command { + + /** + * The result of running a command in a shell. + * + * @property stdout A list of lines read from the standard input stream. + * @property stderr A list of lines read from the standard error stream. + * @property exitCode The status code of running the command. + * @property details Additional command result details. + */ + data class Result( + val stdout: List, + val stderr: List, + val output: List, + val exitCode: Int, + val details: Details? + ) { + + /** + * True when the exit code is equal to 0. + */ + val isSuccess: Boolean get() = exitCode == Status.SUCCESS + + /** + * Get [stdout] and [stderr] as a string, separated by new lines. + * + * @return The output of running the command in a shell. + */ + fun output(): String = output.joinToString("\n") + + /** + * Get [stdout] as a string, separated by new lines. + * + * @return The standard ouput string. + */ + fun stdout(): String = stdout.joinToString("\n") + + /** + * Get [stdout] as a string, separated by new lines. + * + * @return The standard ouput string. + */ + fun stderr(): String = stderr.joinToString("\n") + + /** + * Additional details pertaining to running a command in a shell. + * + * @property uuid The unique identifier associated with the command. + * @property command The command sent to the shell to execute. + * @property startTime The time—in milliseconds since January 1, 1970, 00:00:00 GMT—when + * the command started execution. + * @property endTime The time—in milliseconds since January 1, 1970, 00:00:00 GMT—when + * the command completed execution. + * @property elapsed The number of milliseconds it took to execute the command. + */ + data class Details internal constructor( + val uuid: UUID, + val command: String, + val startTime: Long, + val endTime: Long, + val elapsed: Long = endTime - startTime + ) + + companion object { + internal fun create( + uuid: UUID, + command: String, + stdout: List, + stderr: List, + output: List, + exitCode: Int, + startTime: Long, + endTime: Long = System.currentTimeMillis(), + ) = Result( + stdout, + stderr, + output, + exitCode, + Details(uuid, command, startTime, endTime) + ) + } + } + + /** + * Optional configuration settings when running a command in a [shell][Shell]. + * + * @property uuid The unique identifier associated with the command. + * @property redirectStdErr True to redirect STDERR to STDOUT. + * @property onStdOut Callback that is invoked when reading a line from stdout. + * @property onStdErr Callback that is invoked when reading a line from stderr. + * @property onCancelled Callback that is invoked when the command is interrupted. + * @property onTimeout Callback that is invoked when the command timed-out. + * @property timeout The time to wait before killing the command. + * @property notify True to notify any [OnLineListener] and [OnCommandResultListener] of the command. + */ + class Config private constructor( + val uuid: UUID = UUID.randomUUID(), + val redirectStdErr: Boolean = false, + val onStdOut: (line: String) -> Unit = {}, + val onStdErr: (line: String) -> Unit = {}, + val onCancelled: () -> Unit = {}, + val onTimeout: () -> Unit = {}, + val timeout: Timeout? = null, + val notify: Boolean = true + ) { + + /** + * Optional configuration settings when running a command in a [shell][Shell]. + * + * @property uuid The unique identifier associated with the command. + * @property redirectErrorStream True to redirect STDERR to STDOUT. + * @property onStdOut Callback that is invoked when reading a line from stdout. + * @property onStdErr Callback that is invoked when reading a line from stderr. + * @property onCancelled Callback that is invoked when the command is interrupted. + * @property onTimeout Callback that is invoked when the command timed-out. + * @property timeout The time to wait before killing the command. + * @property notify True to notify any [OnLineListener] and [OnCommandResultListener] of the command. + */ + class Builder { + var uuid: UUID = UUID.randomUUID() + var redirectErrorStream = false + var onStdOut: (line: String) -> Unit = {} + var onStdErr: (line: String) -> Unit = {} + var onCancelled: () -> Unit = {} + var onTimeout: () -> Unit = {} + var timeout: Timeout? = null + var notify = true + + /** + * Create the [Config] from this builder. + * + * @return A new [Config] for a command. + */ + fun create() = + Config( + uuid, + redirectErrorStream, + onStdOut, + onStdErr, + onCancelled, + onTimeout, + timeout, + notify + ) + } + + companion object { + + /** + * The default configuration for running a command in a shell. + * + * @return The default config. + */ + fun default(): Config = Builder().create() + + /** + * Config that doesn't invoke callbacks for line and command complete listeners. + */ + fun silent(): Config = Builder().apply { notify = false }.create() + } + } + + /** + * The command marker to process standard input/error streams. + * + * @property uuid The unique ID for a command. + * @property status the exit code for the last run command. + */ + internal data class Marker(val uuid: UUID, val status: Int) + + /** Exit codes */ + object Status { + /** OK exit code value */ + const val SUCCESS = 0 + + /** Command timeout exit status */ + const val TIMEOUT = 124 + + /** Command failed exit status */ + const val COMMAND_FAILED = 125 + + /** Command not executable exit status */ + const val NOT_EXECUTABLE = 126 + + /** Command not found exit status */ + const val NOT_FOUND = 127 + + /** Command terminated exit status. */ + const val TERMINATED = 128 + 30 + internal const val INVALID = 0x100 + } + } + + /** + * Interface to receive a callback when reading a line from standard output/error streams. + */ + interface OnLineListener { + + /** + * Called when a line was read from standard output/error streams + * + * @param line The string that was read. + */ + fun onLine(line: String) + } + + /** + * Interface to receive a callback when a command completes. + */ + interface OnCommandResultListener { + + /** + * Called when a command finishes running. + * + * @param result The result of running the command in a shell. + */ + fun onResult(result: Command.Result) + } + + /** + * A timeout used when running a command in a shell. + * + * @property value The value of the time based on the [unit]. + * @property unit The time unit for the [value]. + */ + data class Timeout(val value: Long, val unit: TimeUnit) + + /** + * The exception thrown when a command is passed to a closed shell. + */ + class ClosedException(message: String) : IOException(message) + + /** + * The exception thrown when the shell could not be opened. + */ + class NotFoundException(message: String, cause: Throwable) : RuntimeException(message, cause) + + /** + * Represents the possible states of the shell. + */ + sealed class State { + /** The shell is idle; no commands are in progress. */ + object Idle : State() + + /** The shell is currently running a command. */ + object Running : State() + + /** The shell has been shutdown. */ + object Shutdown : State() + } + + /** + * A class to cause the current thread to wait until a command completes or is aborted. + */ + private class Watchdog : CountDownLatch(STREAM_READER_COUNT) { + + private var aborted = false + + /** + * Releases the thread immediately instead of waiting for [signal] to be invoked twice. + */ + fun abort() { + if (count == 0L) return + aborted = true + while (count > 0) countDown() + } + + /** + * Signal that either standard output or standard input streams are finished processing. + */ + fun signal() = countDown() + + /** + * Causes the current thread to wait until [signal] is called twice. + * + * @param timeout The maximum time to wait before [AbortedException] is thrown. + * @throws AbortedException if the timeout completes before [signal] is called twice + * or if the thread is interrupted. + */ + @Throws(AbortedException::class) + fun await(timeout: Timeout?): Boolean { + return when (timeout) { + null -> { + await(); true + } + + else -> await(timeout.value, timeout.unit) + } + } + + override fun await() = super.await().also { + if (aborted) throw AbortedException() + } + + override fun await(timeout: Long, unit: TimeUnit) = super.await(timeout, unit).also { + if (aborted) throw AbortedException() + } + + companion object { + /** + * The number of times [signal] should be called to release the latch. + */ + private const val STREAM_READER_COUNT = 2 + + /** + * The exception thrown when [abort] is called and the [CountDownLatch] has not finished + */ + class AbortedException : InterruptedException() + } + } + + /** + * The [OutputStream] for writing commands to the shell. + */ + private class StandardInputStream(stream: OutputStream) : DataOutputStream(stream) { + + /** + * The helper function to write commands to the stream with an appended new line character. + * + * @param command The command to write. + */ + fun write(command: String) = write("$command\n".toByteArray(Charsets.UTF_8)) + } + + /** + * A thread that parses the standard/error streams for the shell. + * + * @param name The name of the stream. One of: [THREAD_NAME_STDOUT], [THREAD_NAME_STDERR] + * @param stream Either the [Process.getInputStream] or [Process.getErrorStream] + */ + private class StreamReader private constructor( + name: String, + private val stream: InputStream + ) : Thread(name) { + + /** + * The lambda that is invoked when a line is read from the stream. + */ + var onReadLine: (line: String) -> Unit = {} + + /** + * The lambda that is invoked when a command completes. + */ + var onComplete: (marker: Command.Marker) -> Unit = {} + + override fun run() = BufferedReader(InputStreamReader(stream)).forEachLine { line -> + pattern.matcher(line).let { matcher -> + if (matcher.matches()) { + val uuid = UUID.fromString(matcher.group(GROUP_UUID)) + onComplete( + when (val exitCode = matcher.group(GROUP_CODE)) { + null -> Command.Marker(uuid, Command.Status.INVALID) + else -> Command.Marker(uuid, exitCode.toInt()) + } + ) + } else { + onReadLine(line) + } + } + } + + companion object { + + private const val GROUP_UUID = 1 + + private const val GROUP_CODE = 2 + + // + private val pattern: Pattern = Pattern.compile( + "^([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})\\s?([0-9]{1,3})?$", + Pattern.CASE_INSENSITIVE + ) + + internal fun createAndStart(name: String, stream: InputStream) = + StreamReader(name, stream).also { reader -> reader.start() } + } + } + + companion object { + + private const val THREAD_NAME_STDOUT = "STDOUT" + private const val THREAD_NAME_STDERR = "STDERR" + + private const val EXCEPTION_SHELL_CANNOT_OPEN = "Error opening shell: '%s'" + private const val EXCEPTION_SHELL_SHUTDOWN = "The shell is shutdown" + private val instances by lazy { mutableMapOf() } + + /** + * Returns a [Shell] instance using the [path] as the path to the shell/executable.\ + */ + operator fun get(path: String): Shell = instances[path]?.takeIf { shell -> + shell.isAlive() + } ?: Shell(path).also { shell -> + instances[path] = shell + } + + /** The Bourne shell (sh) */ + val SH: Shell get() = this["sh"] + + /** Switch to root, and run it as a shell */ + val SU: Shell get() = this["su"] + + /** + * Execute a command with the provided environment. + * + * @param command + * The name of the program to execute. E.g. "su" or "sh". + * @param environment + * Map of all environment variables to include with the system environment. + * @return The new [Process] instance. + * @throws IOException + * If the requested program could not be executed. + */ + @Throws(IOException::class) + private fun runWithEnv(command: String, environment: EnvironmentMap): Process = + Runtime.getRuntime().exec(command, (System.getenv() + environment).toArray()) + + /** + * Convert an array to an [EnvironmentMap] with each variable/value separated by '='. + * + * @return The array converted to an [EnvironmentMap]. + */ + private fun Array.toEnvironmentMap(): EnvironmentMap = + mutableMapOf().also { map -> + forEach { str -> + str.split("=").takeIf { arr -> + arr.size == 2 + }?.let { (variable, value) -> + map[variable] = value + } + } + }.toMap() + + /** + * Convert an array of [Pair] to an [EnvironmentMap]. + * + * @return The array of variable/value pairs as a new [EnvironmentMap]. + */ + private fun Array>.toEnvironmentMap(): EnvironmentMap = + mutableMapOf().also { map -> + forEach { (variable, value) -> + map[variable] = value + } + } + + /** + * Converts an [EnvironmentMap] to an array of strings with the variable/value + * separated by an '=' character. + * + * @return An array of environment variables. + */ + private fun EnvironmentMap.toArray(): Array = + mutableListOf().also { list -> + forEach { (variable, value) -> + list.add("$variable=$value") + } + }.toTypedArray() + } +} \ No newline at end of file diff --git a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/shell/ShellException.kt b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/shell/ShellException.kt new file mode 100644 index 0000000..7c6f82b --- /dev/null +++ b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/shell/ShellException.kt @@ -0,0 +1,4 @@ +package com.wrbug.developerhelper.commonutil.shell + +class ShellException(message: String) : Exception(message) { +} \ No newline at end of file diff --git a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/shell/ShellManager.kt b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/shell/ShellManager.kt index 801eb3c..f798ae2 100644 --- a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/shell/ShellManager.kt +++ b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/shell/ShellManager.kt @@ -1,18 +1,20 @@ package com.wrbug.developerhelper.commonutil.shell -import com.jaredrummler.android.shell.CommandResult -import com.wrbug.developerhelper.commonutil.CommonUtils -import com.wrbug.developerhelper.commonutil.ShellUtils -import com.wrbug.developerhelper.commonutil.entity.FragmentInfo +import com.wrbug.developerhelper.commonutil.Constant import com.wrbug.developerhelper.commonutil.entity.LsFileInfo import com.wrbug.developerhelper.commonutil.entity.TopActivityInfo +import com.wrbug.developerhelper.commonutil.ifNotEmpty +import com.wrbug.developerhelper.commonutil.runOnIO +import io.reactivex.rxjava3.core.Single import java.io.File import java.util.regex.Pattern - object ShellManager { + private const val SHELL_TOP_ACTIVITY = "dumpsys activity top" - private const val SHELL_PROCESS_PID_1 = "ps -ef | grep \"%1\$s\" | grep -v %1\$s:| grep -v grep | awk '{print \$2}'" + private const val SHELL_APP_ACTIVITY = "dumpsys activity %1\$s" + private const val SHELL_PROCESS_PID_1 = + "ps -ef | grep \"%1\$s\" | grep -v %1\$s:| grep -v grep | awk '{print \$2}'" private const val SHELL_PROCESS_PID_2 = "top -b -n 1 |grep %1\$s |grep -v grep|grep -v %1\$s:" private const val SHELL_PROCESS_PID_3 = "top -n 1 |grep %1\$s |grep -v grep|grep -v %1\$s:" private var SHELL_OPEN_ACCESSiBILITY_SERVICE = arrayOf( @@ -20,27 +22,24 @@ object ShellManager { "settings put secure accessibility_enabled 1" ) private const val SHELL_LS_FILE = "ls -l %1\$s" - private const val SHELL_GET_ZIP_FILE_LIST = - "app_process -Djava.class.path=/data/local/tmp/zip.dex /data/local/tmp Zip %s" private const val SHELL_CHECK_IS_SQLITE = "od -An -tx %1\$s |grep '694c5153'" private const val SHELL_UNINSTALL_APP = "pm uninstall %1\$s" private const val SHELL_CLEAR_APP_DATA = "pm clear %1\$s" private const val SHELL_FORCE_STOP_APP = "am force-stop %1\$s" - private val SHELL_OPEN_ADB_WIFI = arrayOf("setprop service.adb.tcp.port 5555", "stop adbd", "start adbd") - fun getTopActivity(callback: Callback) { - ShellUtils.runWithSu(arrayOf(SHELL_TOP_ACTIVITY), object : ShellUtils.ShellResultCallback() { - override fun onComplete(result: CommandResult) { - callback.onSuccess(getTopActivity(result)) - } + private val SHELL_OPEN_ADB_WIFI = + arrayOf("setprop service.adb.tcp.port 5555", "stop adbd", "start adbd") - override fun onError(msg: String) { - callback.onSuccess(null) - } - }) + fun getTopActivity(): Single { + return ShellUtils.runWithSuAsync(SHELL_TOP_ACTIVITY, useBusyBox = false).map { + getTopActivity(it) + }.runOnIO() + } + private fun tabCount(str: String): Int { + return (str.length - str.trimStart().length) / 2 } - fun getTopActivity(result: CommandResult): TopActivityInfo { + private fun getTopActivity(result: CommandResult): TopActivityInfo { val stdout = result.getStdout() val topActivityInfo = TopActivityInfo() val task_s = stdout.split("TASK ") @@ -50,10 +49,9 @@ object ShellManager { val pattern = Pattern.compile(regex) val matcher = pattern.matcher(task_) if (matcher.find()) { - topActivityInfo.activity = matcher.group().split(" ")[1] + topActivityInfo.setFullActivity(matcher.group().split(" ")[1]) } - val split = - task_.split("\n[ ]{4}[A-Z]".toRegex()).dropLastWhile { it.isEmpty() } + val split = task_.split("\n[ ]{4}[A-Z]".toRegex()).dropLastWhile { it.isEmpty() } for (s in split) { if (s.contains("iew Hierarchy")) { for (s1 in s.split("\n".toRegex()).dropLastWhile { it.isEmpty() }) { @@ -65,67 +63,9 @@ object ShellManager { continue } topActivityInfo.viewIdHex[split1[5].substring(split1[5].indexOf("id/"))] = - split1[4] - } - } - } else if (s.contains("ocal Activity")) { - } else if (s.contains("ctive Fragments")) { - val list = ArrayList() - val split2 = - s.split("\n {6}#[0-9]+:".toRegex()).filterNot { it.isBlank() } - for (s2 in split2) { - if (s2.contains("mFragmentId=")) { - val name = s2.trim { it <= ' ' }.substring(0, s2.indexOf("{") - 1) - val fragmentInfo = FragmentInfo() - list.add(fragmentInfo) - fragmentInfo.name = name - val split3 = - s2.replace("Child FragmentManager", "Child_FragmentManager").split(" ".toRegex()) - .dropLastWhile { it.isEmpty() } - for (s31 in split3) { - if (s31.contains("Child_FragmentManager")) { - break - } - val s3 = s31.replace("\n", "").replace(" ", "") - when { - s3.startsWith("mFragmentId=") -> fragmentInfo.fragmentId = - s3.replace("mFragmentId=", "") - s3.startsWith("mContainerId=") -> fragmentInfo.containerId = - s3.replace("mContainerId=", "") - s3.startsWith("mTag=") -> fragmentInfo.tag = s3.replace("mTag=", "") - s3.startsWith("mState=") -> fragmentInfo.state = - s3.replace("mState=", "").toInt() - s3.startsWith("mIndex=") -> fragmentInfo.index = - s3.replace("mIndex=", "").toInt() - s3.startsWith("mWho=") -> fragmentInfo.who = s3.replace("mWho=", "") - s3.startsWith("mBackStackNesting=") -> fragmentInfo.backStackNesting = - s3.replace("mBackStackNesting=", "").toInt() - s3.startsWith("mAdded=") -> fragmentInfo.added = s3.replace("mAdded=", "") == - "true" - s3.startsWith("mRemoving=") -> fragmentInfo.removing = s3.replace( - "mRemoving=", - "" - ) == "true" - s3.startsWith("mFromLayout=") -> fragmentInfo.fromLayout = s3.replace( - "mFromLayout=", - "" - ) == - "true" - s3.startsWith("mInLayout=") -> fragmentInfo.inLayout = s3.replace( - "mInLayout=", - "" - ) == "true" - s3.startsWith("mHidden=") -> fragmentInfo.hidden = s3.replace("mHidden=", "") == - "true" - s3.startsWith("mDetached=") -> fragmentInfo.detached = s3.replace( - "mDetached=", - "" - ) == "true" - } - } + split1[4] } } - topActivityInfo.fragments = list.toTypedArray() } } break @@ -151,13 +91,23 @@ object ShellManager { } fun getPid(packageName: String): String { - var result: CommandResult = ShellUtils.runWithSu(String.format(SHELL_PROCESS_PID_1, packageName)) + var result: CommandResult = + ShellUtils.runWithSu( + String.format(SHELL_PROCESS_PID_1, packageName), + useBusyBox = false + ) if (result.isSuccessful) { return result.getStdout() } - result = ShellUtils.runWithSu(String.format(SHELL_PROCESS_PID_2, packageName)) + result = ShellUtils.runWithSu( + String.format(SHELL_PROCESS_PID_2, packageName), + useBusyBox = false + ) if (result.isSuccessful.not()) { - result = ShellUtils.runWithSu(String.format(SHELL_PROCESS_PID_3, packageName)) + result = ShellUtils.runWithSu( + String.format(SHELL_PROCESS_PID_3, packageName), + useBusyBox = false + ) } if (result.isSuccessful.not()) { return "" @@ -166,7 +116,11 @@ object ShellManager { } fun getSqliteFiles(packageName: String): Array { - val dbPath = "/data/data/$packageName/databases" + return getSqliteFiles(Constant.dataDir, packageName) + } + + private fun getSqliteFiles(dir: String, packageName: String): Array { + val dbPath = "$dir/$packageName/databases" val list = lsDir(dbPath) val files = ArrayList() for (file in list) { @@ -182,91 +136,143 @@ object ShellManager { return files.toTypedArray() } - fun openAccessibilityService(callback: Callback? = null) { - ShellUtils.runWithSu(SHELL_OPEN_ACCESSiBILITY_SERVICE, object : ShellUtils.ShellResultCallback() { - override fun onComplete(result: CommandResult) { - callback?.onSuccess(result.isSuccessful && result.getStdout().isEmpty()) - } - - override fun onError(msg: String) { - callback?.onSuccess(false) - } - }) + fun openAccessibilityService(): Single { + return ShellUtils.runWithSuAsync(*SHELL_OPEN_ACCESSiBILITY_SERVICE, useBusyBox = false) + .map { + it.isSuccessful && it.getStdout().isEmpty() + }.onErrorReturn { false }.runOnIO() } fun catFile(filaPath: String): String { - val commandResult = ShellUtils.runWithSu("cat $filaPath") - return commandResult.getStdout() + val result = ShellUtils.runWithSu("cat $filaPath") + return result.getStdout() + } + + fun rmFile(file: File): Boolean { + return rmFile(file.absolutePath) + } + + fun mkDir(file: String): Boolean { + val result = ShellUtils.runWithSu("mkdir $file") + return result.isSuccessful } fun rmFile(file: String): Boolean { - val commandResult = ShellUtils.runWithSu("rm -rf $file") - return commandResult.isSuccessful + val result = ShellUtils.runWithSu("rm -rf $file") + return result.isSuccessful } fun modifyFile(filaPath: String, content: String): Boolean { - val commandResult = ShellUtils.runWithSu("echo $content >> $filaPath") - return commandResult.isSuccessful + val result = ShellUtils.runWithSu("echo $content >> $filaPath") + return result.isSuccessful } fun cpFile(source: String, dst: String, mod: String = "666"): Boolean { val dir = dst.substring(0, dst.lastIndexOf("/")) - var commandResult = ShellUtils.runWithSu("mkdir -p $dir") - if (commandResult.isSuccessful.not()) { + var result = ShellUtils.runWithSu("mkdir -p $dir") + if (result.isSuccessful.not()) { + return false + } + result = ShellUtils.runWithSu("cp -R $source $dst && chmod $mod $dst") + return result.isSuccessful || result.getStderr() + .contains("Operation not permitted") + } + + fun tarCF(tarPath: String, srcPath: String): Boolean { + val dir = tarPath.substring(0, tarPath.lastIndexOf("/")) + var result = ShellUtils.runWithSu("mkdir -p $dir") + if (result.isSuccessful.not()) { return false } - commandResult = ShellUtils.runWithSu("cp -R $source $dst && chmod $mod $dst") - return commandResult.isSuccessful || commandResult.getStderr()?.contains("Operation not permitted") ?: false + result = ShellUtils.runWithSu("tar -pcf $tarPath -C $srcPath .") + return result.isSuccessful || result.getStderr() + .contains("Operation not permitted") } + fun tarXF(tarPath: String, dst: String): Boolean { + val result = ShellUtils.runWithSu("tar -xf $tarPath -C $dst") + return result.isSuccessful + } + + fun catFile(source: String, dst: String, mod: String? = null): Boolean { val cmds = arrayListOf() cmds.add("cat $source > $dst") if (mod != null) { cmds.add("chmod $mod $dst") } - val commandResult = ShellUtils.runWithSu(*(cmds.toTypedArray())) - return commandResult.isSuccessful - } - - fun getZipFileList(path: String): List { - val file = File(CommonUtils.application.cacheDir, "zip.dex") - if (file.exists()) { - ShellUtils.runWithSu("cp ${file.absolutePath} /data/local/tmp", "rm -rf ${file.absolutePath}") - } - val commandResult = ShellUtils.runWithSu(String.format(SHELL_GET_ZIP_FILE_LIST, path)) - return commandResult.stdout + val result = ShellUtils.runWithSu(*(cmds.toTypedArray())) + return result.isSuccessful } fun lsDir(path: String): List { - val commandResult = ShellUtils.runWithSu("ls $path") - return commandResult.stdout + val result = ShellUtils.runWithSu("ls $path") + return result.stdout } - fun findApkDir(packageName: String): String { - val cmd = "ls /data/app/|grep $packageName" - val dir = ShellUtils.runWithSu(cmd).getStdout() - return "/data/app/$dir/base.apk" + fun clearAppData(packageName: String): Boolean { + val result = ShellUtils.runWithSu( + String.format(SHELL_CLEAR_APP_DATA, packageName), + useBusyBox = false + ) + return result.isSuccessful } - fun uninstallApp(packageName: String): Boolean { - val commandResult = ShellUtils.runWithSu(String.format(SHELL_UNINSTALL_APP, packageName)) - return commandResult.isSuccessful + fun forceStopApp(packageName: String): Boolean { + val result = ShellUtils.runWithSu( + String.format(SHELL_FORCE_STOP_APP, packageName), + useBusyBox = false + ) + return result.isSuccessful } - fun clearAppData(packageName: String): Boolean { - val commandResult = ShellUtils.runWithSu(String.format(SHELL_CLEAR_APP_DATA, packageName)) - return commandResult.isSuccessful + fun uninstallApk(packageName: String): Boolean { + val result = ShellUtils.runWithSu("pm uninstall $packageName", useBusyBox = false) + return result.isSuccessful } - fun forceStopApp(packageName: String): Boolean { - val commandResult = ShellUtils.runWithSu(String.format(SHELL_FORCE_STOP_APP, packageName)) - return commandResult.isSuccessful + fun installApk(apkDir: String): Boolean { + val result = ShellUtils.runWithSu("pm install -r $apkDir", useBusyBox = false) + return result.isSuccessful } fun openAdbWifi(): Boolean { - val commandResult = ShellUtils.runWithSu(*SHELL_OPEN_ADB_WIFI) - return commandResult.isSuccessful +// val result = ShellUtils.runWithSu(*SHELL_OPEN_ADB_WIFI) + return false + } + + + fun chownDataDir(dir: String, map: Map>): Boolean { + val result = ShellUtils.runWithSu("ls $dir") + if (!result.isSuccessful) { + return false + } + + val dirs = arrayOf("") + result.stdout + dirs.forEach { + val (user, group) = map[it] ?: map[""] ?: return@forEach + ShellUtils.runWithSu("chown -R $user:$group $dir${it.ifNotEmpty { "/$it" }}") + } + return true + } + + fun getDataDirUserAndGroup(dir: String): Map> { + val result = ShellUtils.runWithSu("ls $dir") + if (!result.isSuccessful) { + return emptyMap() + } + val dirs = result.stdout + val map = hashMapOf>() + ShellUtils.runWithSu("stat -c \"%U %G\" $dir").takeIf { it.isSuccessful }?.let { + map[""] = it.getStdout().split(" ").let { it[0] to it[1] } + } + dirs.forEach { + val r = ShellUtils.runWithSu("stat -c \"%U %G\" $dir/$it") + if (r.isSuccessful) { + map[it] = r.getStdout().split(" ").let { it[0] to it[1] } + } + } + return map } } diff --git a/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/shell/ShellUtils.kt b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/shell/ShellUtils.kt new file mode 100644 index 0000000..10def5f --- /dev/null +++ b/commonutil/src/main/java/com/wrbug/developerhelper/commonutil/shell/ShellUtils.kt @@ -0,0 +1,84 @@ +package com.wrbug.developerhelper.commonutil.shell + +import android.util.Log +import com.wrbug.developerhelper.commonutil.CommonUtils +import com.wrbug.developerhelper.commonutil.RootUtils +import io.reactivex.rxjava3.core.Single +import java.io.File + +object ShellUtils { + private const val TAG = "ShellUtils" + private const val AVAILABLE_TEST_COMMANDS = "echo -BOC- \n id" + val busyBoxFile by lazy { + File(CommonUtils.application.cacheDir, "busybox") + } + + fun run(cmd: String, useBusyBox: Boolean = true): CommandResult { + return Shell.SH.run(appendBusyBox(useBusyBox, cmd)).convert() + } + + fun runWithSuAsync(vararg cmds: String, useBusyBox: Boolean = true): Single { + return Single.just(cmds).map { + if (!RootUtils.isRoot()) { + throw ShellException("未开启root权限") + } + Shell.SU.run(appendBusyBox(useBusyBox, *cmds)).convert() + } + } + + fun runWithSu(vararg cmd: String, useBusyBox: Boolean = true): CommandResult { + if (!RootUtils.isRoot()) { + return CommandResult( + emptyList(), + listOf("未开启root权限"), + -1, null + ) + } + return Shell.SU.run(appendBusyBox(useBusyBox, *cmd)).convert() + } + + private fun Shell.Command.Result.convert(): CommandResult { + return apply { + if (isSuccess.not()) { + Log.e(TAG, stderr.joinToString("\n")) + } + }.let { + CommandResult(it.stdout, it.stderr, exitCode, it.details) + } + } + + private fun appendBusyBox(useBusyBox: Boolean, vararg cmds: String): String { + if (cmds.isEmpty()) { + return "" + } + if (useBusyBox && busyBoxFile.exists() && busyBoxFile.canExecute()) { + return cmds.joinToString("\n") { busyBoxFile.absolutePath + " " + it } + } + return cmds.joinToString("\n") + } + + fun isRoot(): Boolean { + val result = Shell.SU.run(AVAILABLE_TEST_COMMANDS) + return parseAvailableResult(result.stdout, true) + } + + + private fun parseAvailableResult(stdout: List?, checkForRoot: Boolean): Boolean { + if (stdout == null) { + return false + } + // this is only one of many ways this can be done + var echoSeen = false + for (line in stdout) { + if (line.contains("uid=")) { + // id command is working, let's see if we are actually root + return !checkForRoot || line.contains("uid=0") + } else if (line.contains("-BOC-")) { + // if we end up here, at least the su command starts some kind of shell, let's hope it has root privileges - + // no way to know without additional native binaries + echoSeen = true + } + } + return echoSeen + } +} \ No newline at end of file diff --git a/commonutil/src/test/java/com/wrbug/developerhelper/commonutil/ExampleUnitTest.java b/commonutil/src/test/java/com/wrbug/developerhelper/commonutil/ExampleUnitTest.java deleted file mode 100644 index e4cabfb..0000000 --- a/commonutil/src/test/java/com/wrbug/developerhelper/commonutil/ExampleUnitTest.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.wrbug.developerhelper.commonutil; - -import org.junit.Test; - -import static org.junit.Assert.*; - -/** - * Example local unit test, which will execute on the development machine (host). - * - * @see Testing documentation - */ -public class ExampleUnitTest { - @Test - public void addition_isCorrect() { - assertEquals(4, 2 + 2); - } -} \ No newline at end of file diff --git a/commonwidget/.gitignore b/commonwidget/.gitignore deleted file mode 100644 index 796b96d..0000000 --- a/commonwidget/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build diff --git a/commonwidget/proguard-rules.pro b/commonwidget/proguard-rules.pro deleted file mode 100644 index f1b4245..0000000 --- a/commonwidget/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile diff --git a/commonwidget/src/androidTest/java/com/wrbug/developerhelper/commonwidget/ExampleInstrumentedTest.java b/commonwidget/src/androidTest/java/com/wrbug/developerhelper/commonwidget/ExampleInstrumentedTest.java deleted file mode 100644 index 78375d2..0000000 --- a/commonwidget/src/androidTest/java/com/wrbug/developerhelper/commonwidget/ExampleInstrumentedTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.wrbug.developerhelper.commonwidget; - -import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import static org.junit.Assert.*; - -/** - * Instrumented test, which will execute on an Android device. - * - * @see Testing documentation - */ -@RunWith(AndroidJUnit4.class) -public class ExampleInstrumentedTest { - @Test - public void useAppContext() { - // Context of the app under test. - Context appContext = InstrumentationRegistry.getTargetContext(); - - assertEquals("com.wrbug.developerhelper.commonwidget.test", appContext.getPackageName()); - } -} diff --git a/commonwidget/src/main/AndroidManifest.xml b/commonwidget/src/main/AndroidManifest.xml deleted file mode 100644 index 1099276..0000000 --- a/commonwidget/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - diff --git a/commonwidget/src/main/res/values/strings.xml b/commonwidget/src/main/res/values/strings.xml deleted file mode 100644 index f6c0897..0000000 --- a/commonwidget/src/main/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - commonwidget - diff --git a/commonwidget/src/test/java/com/wrbug/developerhelper/commonwidget/ExampleUnitTest.java b/commonwidget/src/test/java/com/wrbug/developerhelper/commonwidget/ExampleUnitTest.java deleted file mode 100644 index 0cf0c5a..0000000 --- a/commonwidget/src/test/java/com/wrbug/developerhelper/commonwidget/ExampleUnitTest.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.wrbug.developerhelper.commonwidget; - -import org.junit.Test; - -import static org.junit.Assert.*; - -/** - * Example local unit test, which will execute on the development machine (host). - * - * @see Testing documentation - */ -public class ExampleUnitTest { - @Test - public void addition_isCorrect() { - assertEquals(4, 2 + 2); - } -} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 4a5855f..02743a0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,4 +14,6 @@ org.gradle.jvmargs=-Xmx1536m # Kotlin code style for this project: "official" or "obsolete": kotlin.code.style=official android.useAndroidX=true -android.enableJetifier=true \ No newline at end of file +android.enableJetifier=true +kotlin.jvm.target.validation.mode = IGNORE +android.defaults.buildfeatures.buildconfig=true \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 9a4163a..534885b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ +#Wed Sep 11 19:29:17 CST 2024 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/basecommon/.gitignore b/ipc/.gitignore similarity index 100% rename from basecommon/.gitignore rename to ipc/.gitignore diff --git a/commonwidget/build.gradle b/ipc/build.gradle similarity index 52% rename from commonwidget/build.gradle rename to ipc/build.gradle index 9e215c4..773da94 100644 --- a/commonwidget/build.gradle +++ b/ipc/build.gradle @@ -1,14 +1,16 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' +apply from: "${rootDir}/common.gradle" android { - compileSdkVersion 28 + namespace "com.wrbug.developerhelper.ipc" + compileSdk 34 defaultConfig { - minSdkVersion 21 - targetSdkVersion 28 + minSdkVersion 16 + targetSdkVersion 34 versionCode 1 versionName "1.0" @@ -19,18 +21,15 @@ android { buildTypes { release { minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } } dependencies { - implementation fileTree(include: ['*.jar'], dir: 'libs') - testImplementation 'junit:junit:4.12' + implementation fileTree(dir: 'libs', include: ['*.jar']) + androidTestImplementation 'com.android.support.test:runner:1.0.2' + androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' implementation project(':commonutil') - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" -} -repositories { - mavenCentral() } diff --git a/basecommon/proguard-rules.pro b/ipc/proguard-rules.pro similarity index 100% rename from basecommon/proguard-rules.pro rename to ipc/proguard-rules.pro diff --git a/mmkv/src/androidTest/java/com/wrbug/developerhelper/mmkv/ExampleInstrumentedTest.java b/ipc/src/androidTest/java/com/wrbug/developerhelper/ipc/ExampleInstrumentedTest.java similarity index 82% rename from mmkv/src/androidTest/java/com/wrbug/developerhelper/mmkv/ExampleInstrumentedTest.java rename to ipc/src/androidTest/java/com/wrbug/developerhelper/ipc/ExampleInstrumentedTest.java index 161e03c..6463c6b 100644 --- a/mmkv/src/androidTest/java/com/wrbug/developerhelper/mmkv/ExampleInstrumentedTest.java +++ b/ipc/src/androidTest/java/com/wrbug/developerhelper/ipc/ExampleInstrumentedTest.java @@ -1,4 +1,4 @@ -package com.wrbug.developerhelper.mmkv; +package com.wrbug.developerhelper.ipc; import android.content.Context; import android.support.test.InstrumentationRegistry; @@ -21,6 +21,6 @@ public void useAppContext() { // Context of the app under test. Context appContext = InstrumentationRegistry.getTargetContext(); - assertEquals("com.wrbug.developerhelper.mmkv.test", appContext.getPackageName()); + assertEquals("com.wrbug.developerhelper.ipc.test", appContext.getPackageName()); } } diff --git a/basecommon/src/main/AndroidManifest.xml b/ipc/src/main/AndroidManifest.xml similarity index 56% rename from basecommon/src/main/AndroidManifest.xml rename to ipc/src/main/AndroidManifest.xml index 443e74f..f10ccab 100644 --- a/basecommon/src/main/AndroidManifest.xml +++ b/ipc/src/main/AndroidManifest.xml @@ -1,2 +1,2 @@ + package="com.wrbug.developerhelper.ipc" /> diff --git a/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/AppXposedProcessData.kt b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/AppXposedProcessData.kt new file mode 100644 index 0000000..59ede7f --- /dev/null +++ b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/AppXposedProcessData.kt @@ -0,0 +1,21 @@ +package com.wrbug.developerhelper.ipc.processshare + +import androidx.annotation.Keep +import com.wrbug.developerhelper.ipc.processshare.annotation.Url +import io.reactivex.rxjava3.core.Observable + +/** + * + * class: AppXposedProcessData.kt + * author: wrbug + * date: 2020-05-14 + * description: + * + */ +@Keep +interface AppXposedProcessData : ProcessData { + fun setAppXposedStatus(map: Map) + + fun setAppXposedStatus(packageName: String, open: Boolean) + fun getAppXposedStatus(): Observable> +} \ No newline at end of file diff --git a/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/DataFinderListProcessData.kt b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/DataFinderListProcessData.kt new file mode 100644 index 0000000..16d5a74 --- /dev/null +++ b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/DataFinderListProcessData.kt @@ -0,0 +1,14 @@ +package com.wrbug.developerhelper.ipc.processshare + +import androidx.annotation.Keep +import com.wrbug.developerhelper.ipc.processshare.annotation.Url +import io.reactivex.rxjava3.core.Observable + +@Keep +interface DataFinderListProcessData : ProcessData { + + fun setData(map: Map) + + fun getData(): Observable> + +} \ No newline at end of file diff --git a/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/DefaultValue.kt b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/DefaultValue.kt new file mode 100644 index 0000000..3430e1d --- /dev/null +++ b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/DefaultValue.kt @@ -0,0 +1,5 @@ +package com.wrbug.developerhelper.ipc.processshare + +@Target(AnnotationTarget.FIELD,AnnotationTarget.FUNCTION) +@kotlin.annotation.Retention(AnnotationRetention.RUNTIME) +annotation class DefaultValue(val value: String) diff --git a/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/DumpDexListProcessData.kt b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/DumpDexListProcessData.kt new file mode 100644 index 0000000..96c4321 --- /dev/null +++ b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/DumpDexListProcessData.kt @@ -0,0 +1,14 @@ +package com.wrbug.developerhelper.ipc.processshare + +import androidx.annotation.Keep +import com.wrbug.developerhelper.ipc.processshare.annotation.Url +import io.reactivex.rxjava3.core.Observable + +@Keep +interface DumpDexListProcessData : ProcessData { + + fun setData(map: Map) + + fun getData(): Observable> + +} \ No newline at end of file diff --git a/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/FileProcessData.kt b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/FileProcessData.kt new file mode 100644 index 0000000..1fbfbf4 --- /dev/null +++ b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/FileProcessData.kt @@ -0,0 +1,24 @@ +package com.wrbug.developerhelper.ipc.processshare + +import androidx.annotation.Keep +import com.wrbug.developerhelper.ipc.processshare.annotation.Url +import io.reactivex.rxjava3.core.Observable + +/** + * + * class: FileProcessData.kt + * author: wrbug + * date: 2020-05-19 + * description: + * + */ +@Keep +interface FileProcessData : ProcessData { + + @Url(TcpUrl.FileProcessDataUrl.GET_DATA_FINDER_ZIP_FILE) + fun getDataFinderZipFile(): Observable + + + @Url(TcpUrl.FileProcessDataUrl.GET_DUMP_SO_ZIP_FILE) + fun getDumpSoZipFile(): Observable +} \ No newline at end of file diff --git a/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/GlobalConfigProcessData.kt b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/GlobalConfigProcessData.kt new file mode 100644 index 0000000..6be6ee0 --- /dev/null +++ b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/GlobalConfigProcessData.kt @@ -0,0 +1,12 @@ +package com.wrbug.developerhelper.ipc.processshare + +import androidx.annotation.Keep +import com.wrbug.developerhelper.ipc.processshare.annotation.Url +import io.reactivex.rxjava3.core.Observable + +@Keep +interface GlobalConfigProcessData : ProcessData { + fun isXposedOpen(): Observable + + fun setXposedOpen(open: Boolean) +} diff --git a/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/MMapProcessDataInvocationHandler.kt b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/MMapProcessDataInvocationHandler.kt new file mode 100644 index 0000000..ebf1804 --- /dev/null +++ b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/MMapProcessDataInvocationHandler.kt @@ -0,0 +1,32 @@ +package com.wrbug.developerhelper.ipc.processshare + +import com.wrbug.developerhelper.commonutil.toJson +import com.wrbug.developerhelper.ipc.processshare.annotation.Url +import com.wrbug.developerhelper.ipc.processshare.tcp.TcpManager +import io.reactivex.rxjava3.core.Observable +import java.lang.reflect.InvocationHandler +import java.lang.reflect.Method + +class MMapProcessDataInvocationHandler : InvocationHandler { + override fun invoke(proxy: Any?, method: Method?, args: Array?): Any? { + method?.let { + val annotation = it.getAnnotation(Url::class.java) ?: throw Exception("@Url") + val url = annotation.value + val message: String = if (args.isNullOrEmpty()) { + "" + } else { + when (val data = args[0]) { + is Boolean, Int, Long, String, Float, Double -> data.toString() + else -> data.toJson() ?: "" + } + } + val result = TcpManager.sendMessage(url, message) + if (method.returnType == Observable::class.java) { + return result + } else { + result.subscribe({}, {}) + } + } + return null + } +} \ No newline at end of file diff --git a/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/ProcessData.kt b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/ProcessData.kt new file mode 100644 index 0000000..9829bb2 --- /dev/null +++ b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/ProcessData.kt @@ -0,0 +1,4 @@ +package com.wrbug.developerhelper.ipc.processshare + +interface ProcessData { +} \ No newline at end of file diff --git a/xposedmodule/src/main/java/com/wrbug/developerhelper/xposed/processshare/ProcessDataManager.kt b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/ProcessDataCreator.kt similarity index 57% rename from xposedmodule/src/main/java/com/wrbug/developerhelper/xposed/processshare/ProcessDataManager.kt rename to ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/ProcessDataCreator.kt index 7133478..5c2f834 100644 --- a/xposedmodule/src/main/java/com/wrbug/developerhelper/xposed/processshare/ProcessDataManager.kt +++ b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/ProcessDataCreator.kt @@ -1,12 +1,12 @@ @file:Suppress("UNCHECKED_CAST") -package com.wrbug.developerhelper.xposed.processshare +package com.wrbug.developerhelper.ipc.processshare -import android.util.ArrayMap +import com.wrbug.developerhelper.ipc.processshare.data.IpcFileDataManager import java.lang.reflect.Proxy -object ProcessDataManager { - private val map = ArrayMap, Any>() +object ProcessDataCreator { + private val map = hashMapOf, Any>() fun get(clazz: Class): T { if (map.containsKey(clazz)) { return map[clazz] as T @@ -17,9 +17,13 @@ object ProcessDataManager { } private fun obtainImpl(clazz: Class): T { + val service = IpcFileDataManager.getService(clazz) + if (service != null) { + return service + } val instance = Proxy.newProxyInstance( clazz.classLoader, arrayOf(clazz), - ProcessDataInvocationHandler(clazz) + ProcessDataInvocationHandler() ) return instance as T } diff --git a/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/ProcessDataInvocationHandler.kt b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/ProcessDataInvocationHandler.kt new file mode 100644 index 0000000..bf9d62c --- /dev/null +++ b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/ProcessDataInvocationHandler.kt @@ -0,0 +1,32 @@ +package com.wrbug.developerhelper.ipc.processshare + +import com.wrbug.developerhelper.commonutil.toJson +import com.wrbug.developerhelper.ipc.processshare.annotation.Url +import com.wrbug.developerhelper.ipc.processshare.tcp.TcpManager +import io.reactivex.rxjava3.core.Observable +import java.lang.reflect.InvocationHandler +import java.lang.reflect.Method + +class ProcessDataInvocationHandler : InvocationHandler { + override fun invoke(proxy: Any?, method: Method?, args: Array?): Any? { + method?.let { + val annotation = it.getAnnotation(Url::class.java) ?: throw Exception("@Url") + val url = annotation.value + val message: String = if (args.isNullOrEmpty()) { + "" + } else { + when (val data = args[0]) { + is Boolean, Int, Long, String, Float, Double -> data.toString() + else -> data.toJson() ?: "" + } + } + val result = TcpManager.sendMessage(url, message) + if (method.returnType == Observable::class.java) { + return result + } else { + result.subscribe({}, {}) + } + } + return null + } +} \ No newline at end of file diff --git a/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/TcpUrl.java b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/TcpUrl.java new file mode 100644 index 0000000..557830b --- /dev/null +++ b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/TcpUrl.java @@ -0,0 +1,9 @@ +package com.wrbug.developerhelper.ipc.processshare; + +public class TcpUrl { + public static class FileProcessDataUrl { + public static final String GET_DATA_FINDER_ZIP_FILE = "FileProcessDataUrl/getDataFinderZipFile"; + public static final String GET_DUMP_SO_ZIP_FILE = "FileProcessDataUrl/getDumpSoZipFile"; + + } +} diff --git a/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/annotation/Url.kt b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/annotation/Url.kt new file mode 100644 index 0000000..8ad1c61 --- /dev/null +++ b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/annotation/Url.kt @@ -0,0 +1,6 @@ +package com.wrbug.developerhelper.ipc.processshare.annotation + + +@Target(AnnotationTarget.FUNCTION) +@Retention(AnnotationRetention.RUNTIME) +annotation class Url(val value: String) diff --git a/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/data/AppXposedProcessDataImpl.kt b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/data/AppXposedProcessDataImpl.kt new file mode 100644 index 0000000..b9e57b9 --- /dev/null +++ b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/data/AppXposedProcessDataImpl.kt @@ -0,0 +1,37 @@ +package com.wrbug.developerhelper.ipc.processshare.data + +import com.wrbug.developerhelper.commonutil.fromJson +import com.wrbug.developerhelper.commonutil.toJson +import com.wrbug.developerhelper.ipc.processshare.AppXposedProcessData +import io.reactivex.rxjava3.core.Observable +import io.reactivex.rxjava3.schedulers.Schedulers + +class AppXposedProcessDataImpl : AppXposedProcessData { + private val file = IpcFileDataManager.getFile("AppXposedProcessData") + private fun getMap(): Map { + return try { + file.getData().mapValues { it.value as Boolean } + } catch (t: Throwable) { + emptyMap() + } + } + + override fun setAppXposedStatus(map: Map) { + val m = HashMap(getMap()) + m.putAll(map) + file.save(m) + } + + override fun setAppXposedStatus(packageName: String, open: Boolean) { + setAppXposedStatus(mapOf(packageName to open)) + } + + override fun getAppXposedStatus(): Observable> { + return Observable.just(getMap()) + .subscribeOn(Schedulers.io()) + .onErrorReturn { + emptyMap() + } + } + +} \ No newline at end of file diff --git a/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/data/DataFinderListProcessDataImpl.kt b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/data/DataFinderListProcessDataImpl.kt new file mode 100644 index 0000000..9586609 --- /dev/null +++ b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/data/DataFinderListProcessDataImpl.kt @@ -0,0 +1,41 @@ +package com.wrbug.developerhelper.ipc.processshare.data + +import com.wrbug.developerhelper.commonutil.fromJson +import com.wrbug.developerhelper.commonutil.toJson +import com.wrbug.developerhelper.ipc.processshare.DataFinderListProcessData +import io.reactivex.rxjava3.core.Observable +import io.reactivex.rxjava3.schedulers.Schedulers + + +/** + * + * class: DataFinderListProcessDataImpl.kt + * author: wrbug + * date: 2020-05-19 + * description: + * + */ +class DataFinderListProcessDataImpl : DataFinderListProcessData { + private val file = IpcFileDataManager.getFile("DataFinderListProcessData") + private fun getMap(): Map { + return try { + file.getData().mapValues { it.value as Boolean } + } catch (t: Throwable) { + emptyMap() + } + } + override fun setData(map: Map) { + val m = HashMap(getMap()) + m.putAll(map) + file.save(m) + } + + override fun getData(): Observable> { + return Observable.just(getMap()) + .subscribeOn(Schedulers.io()) + .onErrorReturn { + emptyMap() + } + } + +} \ No newline at end of file diff --git a/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/data/DumpDexListProcessDataImpl.kt b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/data/DumpDexListProcessDataImpl.kt new file mode 100644 index 0000000..c81bfa7 --- /dev/null +++ b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/data/DumpDexListProcessDataImpl.kt @@ -0,0 +1,43 @@ +package com.wrbug.developerhelper.ipc.processshare.data + +import com.wrbug.developerhelper.commonutil.fromJson +import com.wrbug.developerhelper.commonutil.toJson +import com.wrbug.developerhelper.ipc.processshare.DumpDexListProcessData +import io.reactivex.rxjava3.core.Observable +import io.reactivex.rxjava3.schedulers.Schedulers + + +/** + * + * class: DumpDexListProcessDataImpl.kt + * author: wrbug + * date: 2020-05-15 + * description: + * + */ +class DumpDexListProcessDataImpl : DumpDexListProcessData { + private val file = IpcFileDataManager.getFile("DataFinderListProcessData") + private fun getMap(): Map { + return try { + file.getData().mapValues { it.value as Boolean } + } catch (t: Throwable) { + emptyMap() + } + } + + override fun setData(map: Map) { + val m = HashMap(getMap()) + m.putAll(map) + file.save(m) + } + + override fun getData(): Observable> { + return Observable.just(getMap()) + .subscribeOn(Schedulers.io()) + .onErrorReturn { + emptyMap() + } + } + + +} \ No newline at end of file diff --git a/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/data/GlobalConfigProcessDataImpl.kt b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/data/GlobalConfigProcessDataImpl.kt new file mode 100644 index 0000000..f076831 --- /dev/null +++ b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/data/GlobalConfigProcessDataImpl.kt @@ -0,0 +1,55 @@ +package com.wrbug.developerhelper.ipc.processshare.data + +import com.wrbug.developerhelper.commonutil.fromJson +import com.wrbug.developerhelper.commonutil.toJson +import com.wrbug.developerhelper.ipc.processshare.GlobalConfigProcessData +import io.reactivex.rxjava3.core.Observable +import io.reactivex.rxjava3.schedulers.Schedulers + + +/** + * + * class: GlobalConfigProcessDataImpl.kt + * author: wrbug + * date: 2020-05-15 + * description: + * + */ +class GlobalConfigProcessDataImpl : GlobalConfigProcessData { + companion object { + private const val KEY_IS_XPOSED_OPEN = "isXposedOpen" + } + + private val file = IpcFileDataManager.getFile("DataFinderListProcessData") + private fun getMap(): Map { + return try { + file.getData() + } catch (t: Throwable) { + emptyMap() + } + } + + private fun setValue(key: String, value: Any) { + try { + val map = HashMap(getMap()) + map[key] = value + file.save(map) + } catch (t: Throwable) { + t.printStackTrace() + } + + } + + override fun isXposedOpen(): Observable { + return Observable.just(getMap()[KEY_IS_XPOSED_OPEN] as Boolean) + .subscribeOn(Schedulers.io()) + .onErrorReturn { + false + } + } + + override fun setXposedOpen(open: Boolean) { + setValue(KEY_IS_XPOSED_OPEN, open) + } + +} \ No newline at end of file diff --git a/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/data/IpcFileDataManager.kt b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/data/IpcFileDataManager.kt new file mode 100644 index 0000000..84ba104 --- /dev/null +++ b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/data/IpcFileDataManager.kt @@ -0,0 +1,23 @@ +package com.wrbug.developerhelper.ipc.processshare.data + +import com.wrbug.developerhelper.ipc.processshare.AppXposedProcessData +import com.wrbug.developerhelper.ipc.processshare.DataFinderListProcessData +import com.wrbug.developerhelper.ipc.processshare.DumpDexListProcessData +import com.wrbug.developerhelper.ipc.processshare.GlobalConfigProcessData +import java.io.File + +object IpcFileDataManager { + private const val PATH = "/data/local/tmp/developerhelper" + private val map = hashMapOf, Any>( + AppXposedProcessData::class.java to AppXposedProcessDataImpl(), + DataFinderListProcessData::class.java to DataFinderListProcessDataImpl(), + DumpDexListProcessData::class.java to DumpDexListProcessDataImpl(), + GlobalConfigProcessData::class.java to GlobalConfigProcessDataImpl() + ) + + fun getFile(name: String) = IpcFileInfo(File(PATH, "${name}.dat")) + fun getService(clazz: Class): T? { + return map[clazz] as? T + } +} + diff --git a/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/data/IpcFileInfo.kt b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/data/IpcFileInfo.kt new file mode 100644 index 0000000..ed6d627 --- /dev/null +++ b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/data/IpcFileInfo.kt @@ -0,0 +1,41 @@ +package com.wrbug.developerhelper.ipc.processshare.data + +import com.wrbug.developerhelper.commonutil.Base64 +import com.wrbug.developerhelper.commonutil.fromJson +import com.wrbug.developerhelper.commonutil.shell.Shell +import com.wrbug.developerhelper.commonutil.toJson +import java.io.File + +class IpcFileInfo(private var file: File) { + + fun save(data: Map) { + save((data.toJson() ?: "{}").toByteArray()) + } + + fun getData(): Map { + try { + if (!file.exists()) { + return emptyMap() + } + val text = file.readText().replace("\n", "") + val data = Base64.decode(text) + return data.fromJson>() ?: emptyMap() + } catch (t: Throwable) { + return emptyMap() + } + } + + private fun save(data: ByteArray) { + val encode = Base64.encodeAsString(data) + if (file.exists() && file.canWrite()) { + saveFromJava(encode) + return + } + val commandResult = + Shell.SU.run("echo $encode > ${file.absolutePath} && chmod 777 ${file.absolutePath}") + } + + private fun saveFromJava(data: String) { + file.writeText(data) + } +} \ No newline at end of file diff --git a/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/manager/AppXposedProcessDataManager.kt b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/manager/AppXposedProcessDataManager.kt new file mode 100644 index 0000000..17c23a0 --- /dev/null +++ b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/manager/AppXposedProcessDataManager.kt @@ -0,0 +1,45 @@ +package com.wrbug.developerhelper.ipc.processshare.manager + +import com.wrbug.developerhelper.ipc.processshare.AppXposedProcessData + +class AppXposedProcessDataManager private constructor() : + ProcessDataManager() { + fun setAppXposedStatusList(list: Map) { + processData.setAppXposedStatus(list) + } + + fun setAppXposedStatusList(vararg pairs: Pair) { + setAppXposedStatusList(mapOf(*pairs)) + } + + + fun getAppXposedStatusList(): Map { + return processData.getAppXposedStatus().blockingFirst() + } + + fun isAppXposedOpened(packageName: String): Boolean { + return getAppXposedStatusList()[packageName] == true + } + + fun getOpenedAppXposedList(): List { + val map = getAppXposedStatusList() + val list = ArrayList() + map.forEach { entry -> + if (entry.value) { + list.add(entry.key) + } + } + return list + } + + fun setAppXposedStatus(packageName: String, open: Boolean) { + processData.setAppXposedStatus(packageName, open) + + } + + companion object { + val instance = AppXposedProcessDataManager() + + + } +} \ No newline at end of file diff --git a/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/manager/DataFinderListProcessDataManager.kt b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/manager/DataFinderListProcessDataManager.kt new file mode 100644 index 0000000..e58dee4 --- /dev/null +++ b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/manager/DataFinderListProcessDataManager.kt @@ -0,0 +1,38 @@ +package com.wrbug.developerhelper.ipc.processshare.manager + +import com.wrbug.developerhelper.commonutil.fromJson +import com.wrbug.developerhelper.ipc.processshare.DataFinderListProcessData +import com.wrbug.developerhelper.ipc.processshare.DumpDexListProcessData +import io.reactivex.rxjava3.core.Observable + + +/** + * + * class: DataFinderListProcessDataManager.kt + * author: wrbug + * date: 2020-05-19 + * description: + * + */ +class DataFinderListProcessDataManager private constructor() : + ProcessDataManager() { + fun setData(map: Map) { + processData.setData(map) + } + + fun setData(vararg pairs: Pair) { + processData.setData(hashMapOf(*pairs)) + } + + fun getData(): HashMap { + return HashMap(processData.getData().blockingFirst()) + } + + fun containPackage(packageName: String): Boolean { + return getData()[packageName] ?: false + } + + companion object { + val instance = DataFinderListProcessDataManager() + } +} \ No newline at end of file diff --git a/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/manager/DumpDexListProcessDataManager.kt b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/manager/DumpDexListProcessDataManager.kt new file mode 100644 index 0000000..ab10d88 --- /dev/null +++ b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/manager/DumpDexListProcessDataManager.kt @@ -0,0 +1,37 @@ +package com.wrbug.developerhelper.ipc.processshare.manager + +import com.wrbug.developerhelper.commonutil.fromJson +import com.wrbug.developerhelper.ipc.processshare.DumpDexListProcessData +import io.reactivex.rxjava3.core.Observable + + +/** + * + * class: DumpDexListProcessDataManager.kt + * author: wrbug + * date: 2020-05-15 + * description: + * + */ +class DumpDexListProcessDataManager private constructor() : + ProcessDataManager() { + fun setData(map: Map) { + processData.setData(map) + } + + fun setData(vararg pairs: Pair) { + processData.setData(hashMapOf(*pairs)) + } + + fun getData(): HashMap { + return HashMap(processData.getData().blockingFirst()) + } + + fun containPackage(packageName: String): Boolean { + return getData()[packageName] ?: false + } + + companion object { + val instance = DumpDexListProcessDataManager() + } +} \ No newline at end of file diff --git a/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/manager/FileProcessDataManager.kt b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/manager/FileProcessDataManager.kt new file mode 100644 index 0000000..020763f --- /dev/null +++ b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/manager/FileProcessDataManager.kt @@ -0,0 +1,34 @@ +package com.wrbug.developerhelper.ipc.processshare.manager + +import com.wrbug.developerhelper.commonutil.Base64 +import com.wrbug.developerhelper.ipc.processshare.FileProcessData +import io.reactivex.rxjava3.core.Observable + + +/** + * + * class: FileProcessDataManager.kt + * author: wrbug + * date: 2020-05-19 + * description: + * + */ +class FileProcessDataManager private constructor() : + ProcessDataManager() { + + fun getDataFinderZipFile(): Observable { + return processData.getDataFinderZipFile().map { + Base64.decode(it.toCharArray()) + } + } + + fun getDumpSoZipFile(): Observable { + return processData.getDumpSoZipFile().map { + Base64.decode(it.toCharArray()) + } + } + + companion object { + val instance = FileProcessDataManager() + } +} \ No newline at end of file diff --git a/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/manager/GlobalConfigProcessDataManager.kt b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/manager/GlobalConfigProcessDataManager.kt new file mode 100644 index 0000000..c339b0e --- /dev/null +++ b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/manager/GlobalConfigProcessDataManager.kt @@ -0,0 +1,29 @@ +package com.wrbug.developerhelper.ipc.processshare.manager + +import com.wrbug.developerhelper.commonutil.print +import com.wrbug.developerhelper.ipc.processshare.GlobalConfigProcessData +import io.reactivex.rxjava3.core.Observable + + +/** + * + * class: GlobalConfigProcessDataManager.kt + * author: wrbug + * date: 2020-05-14 + * description: + * + */ +class GlobalConfigProcessDataManager private constructor() : + ProcessDataManager() { + fun isXposedOpen(): Boolean { + return processData.isXposedOpen().blockingFirst() + } + + fun setXposedOpen(open: Boolean) { + processData.setXposedOpen(open) + } + + companion object { + val instance = GlobalConfigProcessDataManager() + } +} \ No newline at end of file diff --git a/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/manager/ProcessDataManager.kt b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/manager/ProcessDataManager.kt new file mode 100644 index 0000000..b387c3e --- /dev/null +++ b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/manager/ProcessDataManager.kt @@ -0,0 +1,23 @@ +package com.wrbug.developerhelper.ipc.processshare.manager + +import com.wrbug.developerhelper.ipc.processshare.ProcessData +import com.wrbug.developerhelper.ipc.processshare.ProcessDataCreator +import java.lang.reflect.ParameterizedType + + +/** + * + * class: ProcessDataCreator.kt + * author: wrbug + * date: 2020-05-14 + * description: + * + */ +open class ProcessDataManager { + + + protected val processData: T by lazy { + val type = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0] + ProcessDataCreator.get(type as Class) + } +} \ No newline at end of file diff --git a/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/tcp/MessageHandler.kt b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/tcp/MessageHandler.kt new file mode 100644 index 0000000..c9f4fb3 --- /dev/null +++ b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/tcp/MessageHandler.kt @@ -0,0 +1,5 @@ +package com.wrbug.developerhelper.ipc.processshare.tcp + +interface MessageHandler { + fun handle(action: String, message: String): String +} \ No newline at end of file diff --git a/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/tcp/MessageReceiver.kt b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/tcp/MessageReceiver.kt new file mode 100644 index 0000000..3344e9d --- /dev/null +++ b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/tcp/MessageReceiver.kt @@ -0,0 +1,5 @@ +package com.wrbug.developerhelper.ipc.processshare.tcp + +interface MessageReceiver { + fun onReceived(message: String) +} \ No newline at end of file diff --git a/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/tcp/TCPClient.kt b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/tcp/TCPClient.kt new file mode 100644 index 0000000..0b016b0 --- /dev/null +++ b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/tcp/TCPClient.kt @@ -0,0 +1,21 @@ +import java.io.BufferedReader +import java.io.DataOutputStream +import java.io.InputStreamReader +import java.net.Socket + +object TCPClient { + private var clientSocket: Socket? = null + @Throws(Exception::class) + @JvmStatic + fun main(args: Array) { + println("请输入要转换的字符串:") + clientSocket = Socket("localhost", 23456) + val outToServer = DataOutputStream(clientSocket!!.getOutputStream()) + val inFromServer = BufferedReader(InputStreamReader(clientSocket!!.getInputStream())) + for (i in 5 downTo 1) { + outToServer.writeBytes("123456" + '\n') + println("FROM SERVER:" + inFromServer.readLine()) + } + clientSocket!!.close() + } +} \ No newline at end of file diff --git a/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/tcp/TCPServer.kt b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/tcp/TCPServer.kt new file mode 100644 index 0000000..8342bec --- /dev/null +++ b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/tcp/TCPServer.kt @@ -0,0 +1,253 @@ +package com.wrbug.developerhelper.ipc.processshare.tcp + +import com.wrbug.developerhelper.commonutil.print +import org.jetbrains.anko.doAsync +import java.io.* +import java.net.InetAddress +import java.net.ServerSocket +import java.net.Socket +import java.net.SocketAddress +import java.util.* + +class TCPServer { + private var mMessageListener: OnMessageReceived? = null + private var mConnectListener: OnConnect? = null + private var mDisconnectListener: OnDisconnect? = null + private var mServerClosedListener: OnServerClose? = null + private var mServerStartListener: OnServerStart? = null + private var serverSocket: ServerSocket? = null + private var lastClientIndex: Short = 0 + private val clients: MutableMap = HashMap() + var isServerRunning = false + private set + + fun startServer(port: String?) { + startServer(Integer.valueOf(port)) + } + + fun startServer(port: Int) { + doAsync { + + isServerRunning = true + var socket: Socket? = null + try { + serverSocket = ServerSocket(port) + } catch (e: IOException) { + e.printStackTrace() + isServerRunning = false + } + ("startServer: " + (mServerStartListener != null)).print() + if (mServerStartListener != null) { + mServerStartListener!!.serverStarted(port) + } + while (isServerRunning) { + "Accepting client".print() + try { + socket = serverSocket!!.accept() + val client = Client(socket) + lastClientIndex++ + clients[lastClientIndex.toInt()] = client + Thread(client).start() + client.setIndex(lastClientIndex.toInt()) + if (mConnectListener != null) { + mConnectListener!!.connected( + socket, + socket.localAddress, + +socket.localPort, + socket.localSocketAddress, + lastClientIndex.toInt() + ) + } + } catch (e: IOException) { + isServerRunning = false + break + } + } + if (mServerClosedListener != null) { + mServerClosedListener!!.serverClosed(port) + } + + } + } + + fun closeServer() { + try { + "closeServer: ".print() + isServerRunning = false + serverSocket!!.close() + kickAll() + } catch (e: IOException) { + e.printStackTrace() + } + } + + fun kickAll() { + for (client in clients.values) { + client.kill() + } + } + + fun kick(clientIndex: Int) { + clients[clientIndex]!!.kill() + } + + fun sendln(clientIndex: Int, message: String?) { + clients[clientIndex]?.output?.run { + println(message) + flush() + } + } + + fun send(clientIndex: Int, message: String?) { + clients[clientIndex]?.output?.run { + print(message) + flush() + } + } + + fun broadcast(message: String?) { + for (client in clients.values) { + client.output?.run { + print(message) + flush() + } + } + } + + fun broadcastln(message: String?) { + for (client in clients.values) { + client.output?.run { + println(message) + flush() + } + } + } + + fun getClients(): Map { + return clients + } + + val clientsCount: Int + get() = clients.size + + //---------------------------------------------[Listeners]----------------------------------------------// + fun setOnMessageReceivedListener(listener: OnMessageReceived?) { + mMessageListener = listener + } + + fun setOnConnectListener(listener: OnConnect?) { + mConnectListener = listener + } + + fun setOnDisconnectListener(listener: OnDisconnect?) { + mDisconnectListener = listener + } + + fun setOnServerClosedListener(listener: OnServerClose?) { + mServerClosedListener = listener + } + + fun setOnServerStartListener(listener: OnServerStart?) { + mServerStartListener = listener + } + + //---------------------------------------------[Interfaces]---------------------------------------------// + interface OnMessageReceived { + fun messageReceived(message: String?, clientIndex: Int) + } + + interface OnConnect { + fun connected( + socket: Socket?, + localAddress: InetAddress?, + port: Int, + localSocketAddress: SocketAddress?, + clientIndex: Int + ) + } + + interface OnDisconnect { + fun disconnected( + socket: Socket?, + localAddress: InetAddress?, + port: Int, + localSocketAddress: SocketAddress?, + clientIndex: Int + ) + } + + interface OnServerClose { + fun serverClosed(port: Int) + } + + interface OnServerStart { + fun serverStarted(port: Int) + } + + //--------------------------------------------[Client class]--------------------------------------------// + inner class Client(private val socket: Socket) : Runnable { + var output: PrintWriter? = null + private var input: BufferedReader? = null + private var clientIndex = 0 + override fun run() { + while (isServerRunning) { + "Read line (Client: $clientIndex)".print() + try { + val line = input!!.readLine() + println(line) + if (mMessageListener != null) { + if (line == null) { + socket.close() + clients.remove(clientIndex) + if (mDisconnectListener != null) { + mDisconnectListener!!.disconnected( + socket, + socket.localAddress, + +socket.localPort, + socket.localSocketAddress, + clientIndex + ) + } + break + } else { + mMessageListener!!.messageReceived(line, clientIndex) + } + } + } catch (e: IOException) { + e.printStackTrace() + } + } + } + + fun kill() { + try { + socket.shutdownInput() + } catch (e: Exception) { + } + try { + socket.shutdownOutput() + } catch (e: Exception) { + } + try { + socket.close() + } catch (e: IOException) { + e.printStackTrace() + } + } + + fun setIndex(index: Int) { + clientIndex = index + } + + init { + try { + input = BufferedReader(InputStreamReader(socket.getInputStream())) + output = + PrintWriter(BufferedWriter(OutputStreamWriter(socket.getOutputStream())), true) + } catch (e: IOException) { + e.printStackTrace() + } + } + } + +} \ No newline at end of file diff --git a/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/tcp/TcpManager.kt b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/tcp/TcpManager.kt new file mode 100644 index 0000000..8355d97 --- /dev/null +++ b/ipc/src/main/java/com/wrbug/developerhelper/ipc/processshare/tcp/TcpManager.kt @@ -0,0 +1,66 @@ +package com.wrbug.developerhelper.ipc.processshare.tcp + +import com.google.gson.annotations.SerializedName +import com.wrbug.developerhelper.commonutil.fromJson +import com.wrbug.developerhelper.commonutil.print +import com.wrbug.developerhelper.commonutil.toJson +import io.reactivex.rxjava3.core.Observable +import io.reactivex.rxjava3.schedulers.Schedulers +import java.io.BufferedReader +import java.io.DataOutputStream +import java.io.InputStreamReader +import java.net.Socket +import kotlin.concurrent.thread + +object TcpManager { + private const val PORT = 23412 + private val tcpServer = TCPServer() + var messageHandler: MessageHandler? = null + + + fun startServer() { + tcpServer.setOnMessageReceivedListener(object : TCPServer.OnMessageReceived { + override fun messageReceived(message: String?, clientIndex: Int) { + val data = message?.fromJson() + if (data == null || data.action.isNullOrEmpty()) { + tcpServer.sendln(clientIndex, "") + return + } + "action=${data.action} data=${data.data} $clientIndex".print() + tcpServer.sendln( + clientIndex, + messageHandler?.handle(data.action ?: "", data.data ?: "") ?: "" + ) + } + }) + tcpServer.startServer(PORT) + } + + + fun sendMessage( + action: String, + message: String + ): Observable { + return Observable.create { + val clientSocket = Socket("localhost", PORT) + val outToServer = DataOutputStream(clientSocket.getOutputStream()) + val inFromServer = BufferedReader(InputStreamReader(clientSocket.getInputStream())) + val data = TCPData(action, message) + outToServer.writeBytes(data.toJson() + '\n') + val result = inFromServer.readLine() + clientSocket.close() + "onReceived: $result".print() + it.onNext(result ?: "") + }.onErrorResumeNext { + it.printStackTrace() + Observable.just("") + }.subscribeOn(Schedulers.io()) + } + + data class TCPData( + @SerializedName("action") + var action: String?, + @SerializedName("data") + var data: String? + ) +} \ No newline at end of file diff --git a/ipc/src/main/res/values/strings.xml b/ipc/src/main/res/values/strings.xml new file mode 100644 index 0000000..0ecb285 --- /dev/null +++ b/ipc/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + ipc + diff --git a/mmkv/build.gradle b/mmkv/build.gradle index 1f89e36..5c77bb5 100644 --- a/mmkv/build.gradle +++ b/mmkv/build.gradle @@ -1,21 +1,26 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' +apply from: "${rootDir}/common.gradle" android { - compileSdkVersion 28 - - + namespace "com.wrbug.developerhelper.mmkv" + compileSdk 34 defaultConfig { - minSdkVersion 21 - targetSdkVersion 28 + minSdkVersion 23 + targetSdkVersion 34 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + kotlinOptions { jvmTarget = "17" } buildTypes { release { minifyEnabled false @@ -27,14 +32,7 @@ android { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation 'com.tencent:mmkv:1.0.14' - implementation 'androidx.appcompat:appcompat:1.0.2' - testImplementation 'junit:junit:4.12' - androidTestImplementation 'androidx.test:runner:1.1.0-alpha3' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0-alpha3' - implementation 'com.google.code.gson:gson:2.8.5' - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" -} -repositories { - mavenCentral() + implementation 'com.tencent:mmkv:1.3.9' + implementation 'androidx.appcompat:appcompat:1.7.0' + implementation 'com.google.code.gson:gson:2.10' } diff --git a/mmkv/src/test/java/com/wrbug/developerhelper/mmkv/ExampleUnitTest.java b/mmkv/src/test/java/com/wrbug/developerhelper/mmkv/ExampleUnitTest.java deleted file mode 100644 index 9b42f39..0000000 --- a/mmkv/src/test/java/com/wrbug/developerhelper/mmkv/ExampleUnitTest.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.wrbug.developerhelper.mmkv; - -import org.junit.Test; - -import static org.junit.Assert.*; - -/** - * Example local unit test, which will execute on the development machine (host). - * - * @see Testing documentation - */ -public class ExampleUnitTest { - @Test - public void addition_isCorrect() { - assertEquals(4, 2 + 2); - } -} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index cf8020c..6dde286 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1,24 @@ -include ':app', ':basecommon', ':commonutil', ':mmkv', ':xposedmodule', ':commonwidget' +pluginManagement { + repositories { + maven { url 'https://maven.aliyun.com/repository/public/' } + gradlePluginPortal() + google() + mavenCentral() + maven { + url "https://jitpack.io" + } + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + maven { url 'https://maven.aliyun.com/repository/public/' } + google() + mavenCentral() + maven { + url "https://jitpack.io" + } + } +} + +include ':app', ':commonutil', ':mmkv', ":ipc" diff --git a/xposedmodule/.gitignore b/xposedmodule/.gitignore deleted file mode 100644 index 796b96d..0000000 --- a/xposedmodule/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build diff --git a/xposedmodule/CMakeLists.txt b/xposedmodule/CMakeLists.txt deleted file mode 100644 index 73bac34..0000000 --- a/xposedmodule/CMakeLists.txt +++ /dev/null @@ -1,59 +0,0 @@ -# For more information about using CMake with Android Studio, read the -# documentation: https://d.android.com/studio/projects/add-native-code.html - -# Sets the minimum version of CMake required to build the native library. - -cmake_minimum_required(VERSION 3.4.1) - -# Creates and names a library, sets it as either STATIC -# or SHARED, and provides the relative paths to its source code. -# You can define multiple libraries, and CMake builds them for you. -# Gradle automatically packages shared libraries with your APK. - -add_library( # Sets the name of the library. - nativeDump - - # Sets the library as a shared library. - SHARED - - # Provides a relative path to your source file(s). - src/main/cpp/native.cpp - src/main/cpp/dlopen.c - src/main/cpp/inlineHook.c - src/main/cpp/relocate.c - src/main/cpp/And64InlineHook.cpp - src/main/cpp/util/deviceutils.cpp - src/main/cpp/util/fileutils.cpp) - -# Searches for a specified prebuilt library and stores the path as a -# variable. Because CMake includes system libraries in the search path by -# default, you only need to specify the name of the public NDK library -# you want to add. CMake verifies that the library exists before -# completing its build. - -find_library( # Sets the name of the path variable. - log-lib - - # Specifies the name of the NDK library that - # you want CMake to locate. - log ) - -# Specifies libraries CMake should link to your target library. You -# can link multiple libraries, such as libraries you define in this -# build script, prebuilt third-party libraries, or system libraries. - -target_link_libraries( # Specifies the target library. - nativeDump - - # Links the target library to the log library - # included in the NDK. - ${log-lib} ) - - -if(${ANDROID_ABI} STREQUAL "armeabi-v7a") - include_directories(${ANDROID_SYSROOT}/usr/include/arm-linux-androidabi) -elseif(${ANDROID_ABI} STREQUAL "arm64-v8a") - include_directories(${ANDROID_SYSROOT}/usr/include/aarch64-linux-android) -else() - include_directories(${ANDROID_SYSROOT}/usr/include/arm-linux-androidabi) -endif() \ No newline at end of file diff --git a/xposedmodule/build.gradle b/xposedmodule/build.gradle deleted file mode 100644 index 87277e8..0000000 --- a/xposedmodule/build.gradle +++ /dev/null @@ -1,66 +0,0 @@ -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' - -android { - compileSdkVersion 28 - - - - defaultConfig { - minSdkVersion 21 - targetSdkVersion 28 - versionCode 1 - versionName "1.0" - - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" - externalNativeBuild { - cmake { - cppFlags "-std=c++14", "-fms-extensions" - } - } - ndk { - abiFilters "armeabi", "armeabi-v7a", "arm64-v8a" - } - - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } - externalNativeBuild { - cmake { - path "CMakeLists.txt" - } - } -} - -dependencies { - implementation fileTree(include: ['*.jar'], dir: 'libs') - testImplementation 'junit:junit:4.12' - androidTestImplementation 'androidx.test:runner:1.1.1' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' - implementation 'org.jetbrains.anko:anko-commons:0.10.8' - implementation project(':commonutil') - implementation 'com.jaredrummler:android-shell:1.0.0' - implementation project(':mmkv') - compileOnly 'de.robv.android.xposed:api:82' - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation 'com.google.code.gson:gson:2.8.5' - implementation project(':basecommon') -} -repositories { - mavenCentral() -} -task buildSo(dependsOn: "compileReleaseSources") { - doLast { - println "更新so" - exec { - ignoreExitValue true - commandLine "pwd" - commandLine "sh", "./script/so.sh" - } - } -} \ No newline at end of file diff --git a/xposedmodule/proguard-rules.pro b/xposedmodule/proguard-rules.pro deleted file mode 100644 index f1b4245..0000000 --- a/xposedmodule/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile diff --git a/xposedmodule/script/so.sh b/xposedmodule/script/so.sh deleted file mode 100644 index 5a3a845..0000000 --- a/xposedmodule/script/so.sh +++ /dev/null @@ -1,11 +0,0 @@ -result=$(ls build/intermediates/cmake/release/obj)|grep 'No such file or directory' -echo $result -if [ -z "${result}" ]; then - echo '目录已生成' - cp build/intermediates/cmake/release/obj/arm64-v8a/libnativeDump.so src/main/assets/nativeDumpV8a.so - cp build/intermediates/cmake/release/obj/armeabi-v7a/libnativeDump.so src/main/assets/nativeDumpV7a.so - cp build/intermediates/cmake/release/obj/armeabi/libnativeDump.so src/main/assets/nativeDump.so - echo '完成' -else - echo '目录未生成' -fi \ No newline at end of file diff --git a/xposedmodule/src/androidTest/java/com/wrbug/developerhelper/xposed/ExampleInstrumentedTest.java b/xposedmodule/src/androidTest/java/com/wrbug/developerhelper/xposed/ExampleInstrumentedTest.java deleted file mode 100644 index 178345d..0000000 --- a/xposedmodule/src/androidTest/java/com/wrbug/developerhelper/xposed/ExampleInstrumentedTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.wrbug.developerhelper.xposed; - -import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import static org.junit.Assert.*; - -/** - * Instrumented test, which will execute on an Android device. - * - * @see Testing documentation - */ -@RunWith(AndroidJUnit4.class) -public class ExampleInstrumentedTest { - @Test - public void useAppContext() { - // Context of the app under test. - Context appContext = InstrumentationRegistry.getTargetContext(); - - assertEquals("com.wrbug.developerhelper.xposed.test", appContext.getPackageName()); - } -} diff --git a/xposedmodule/src/main/AndroidManifest.xml b/xposedmodule/src/main/AndroidManifest.xml deleted file mode 100644 index c249e0a..0000000 --- a/xposedmodule/src/main/AndroidManifest.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - diff --git a/xposedmodule/src/main/assets/xposed_init b/xposedmodule/src/main/assets/xposed_init deleted file mode 100644 index 06ffbeb..0000000 --- a/xposedmodule/src/main/assets/xposed_init +++ /dev/null @@ -1 +0,0 @@ -com.wrbug.developerhelper.xposed.XposedInit \ No newline at end of file diff --git a/xposedmodule/src/main/cpp/And64InlineHook.cpp b/xposedmodule/src/main/cpp/And64InlineHook.cpp deleted file mode 100755 index 564fbeb..0000000 --- a/xposedmodule/src/main/cpp/And64InlineHook.cpp +++ /dev/null @@ -1,599 +0,0 @@ -/* - * @date : 2018/01/24 - * @author : rrrfff@foxmail.com - * https://github.com/rrrfff/And64InlineHook - */ -/* - MIT License - - Copyright (c) 2017 rrrfff@foxmail.com - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. - */ -#define __STDC_FORMAT_MACROS - -#include -#include -#include -#include -#include -#include -#if defined(__aarch64__) - -#include "And64InlineHook.hpp" - -#define A64_MAX_INSTRUCTIONS 5 -#define A64_MAX_REFERENCES (A64_MAX_INSTRUCTIONS * 2) -#define A64_NOP 0xd503201fu -#define A64_JNIEXPORT __attribute__((visibility("default"))) -#define A64_LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "A64_HOOK", __VA_ARGS__)) -#ifndef NDEBUG -# define A64_LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "A64_HOOK", __VA_ARGS__)) -#else -# define A64_LOGI(...) ((void)0) -#endif // NDEBUG -typedef uint32_t *__restrict *__restrict instruction; -typedef struct { - struct fix_info { - uint32_t *bp; - uint32_t ls; // left-shift counts - uint32_t ad; // & operand - }; - struct insns_info { - union { - uint64_t insu; - int64_t ins; - void *insp; - }; - fix_info fmap[A64_MAX_REFERENCES]; - }; - int64_t basep; - int64_t endp; - insns_info dat[A64_MAX_INSTRUCTIONS]; - -public: - inline bool is_in_fixing_range(const int64_t absolute_addr) { - return absolute_addr >= this->basep && absolute_addr < this->endp; - } - - inline intptr_t get_ref_ins_index(const int64_t absolute_addr) { - return static_cast((absolute_addr - this->basep) / sizeof(uint32_t)); - } - - inline intptr_t get_and_set_current_index(uint32_t *__restrict inp, uint32_t *__restrict outp) { - intptr_t current_idx = this->get_ref_ins_index(reinterpret_cast(inp)); - this->dat[current_idx].insp = outp; - return current_idx; - } - - inline void reset_current_ins(const intptr_t idx, uint32_t *__restrict outp) { - this->dat[idx].insp = outp; - } - - void - insert_fix_map(const intptr_t idx, uint32_t *bp, uint32_t ls = 0u, uint32_t ad = 0xffffffffu) { - for (auto &f : this->dat[idx].fmap) { - if (f.bp == NULL) { - f.bp = bp; - f.ls = ls; - f.ad = ad; - return; - } //if - } - // What? GGing.. - } - - void process_fix_map(const intptr_t idx) { - for (auto &f : this->dat[idx].fmap) { - if (f.bp == NULL) break; - *(f.bp) = *(f.bp) | - (((int32_t(this->dat[idx].ins - reinterpret_cast(f.bp)) >> 2) - << f.ls) & f.ad); - f.bp = NULL; - } - } -} context; - -//------------------------------------------------------------------------- - -static bool __fix_branch_imm(instruction inpp, instruction outpp, context *ctxp) { - constexpr uint32_t mbits = 6u; - constexpr uint32_t mask = 0xfc000000u; // 0b11111100000000000000000000000000 - constexpr uint32_t rmask = 0x03ffffffu; // 0b00000011111111111111111111111111 - constexpr uint32_t op_b = 0x14000000u; // "b" ADDR_PCREL26 - constexpr uint32_t op_bl = 0x94000000u; // "bl" ADDR_PCREL26 - - const uint32_t ins = *(*inpp); - const uint32_t opc = ins & mask; - switch (opc) { - case op_b: - case op_bl: { - intptr_t current_idx = ctxp->get_and_set_current_index(*inpp, *outpp); - int64_t absolute_addr = reinterpret_cast(*inpp) + - (static_cast(ins << mbits) - >> (mbits - 2u)); // sign-extended - int64_t new_pc_offset = - static_cast(absolute_addr - reinterpret_cast(*outpp)) - >> 2; // shifted - bool special_fix_type = ctxp->is_in_fixing_range(absolute_addr); - // whether the branch should be converted to absolute jump - if (!special_fix_type && llabs(new_pc_offset) > rmask) { - bool b_aligned = (reinterpret_cast(*outpp + 2) & 7u) == 0u; - if (opc == op_b) { - if (b_aligned == true) { - (*outpp)[0] = A64_NOP; - ctxp->reset_current_ins(current_idx, ++(*outpp)); - } //if - (*outpp)[0] = 0x58000051u; // LDR X17, #0x8 - (*outpp)[1] = 0xd61f0220u; // BR X17 - memcpy(*outpp + 2, &absolute_addr, sizeof(absolute_addr)); - *outpp += 4; - } else { - if (b_aligned == false) { - (*outpp)[0] = A64_NOP; - ctxp->reset_current_ins(current_idx, ++(*outpp)); - } //if - (*outpp)[0] = 0x58000071u; // LDR X17, #12 - (*outpp)[1] = 0x1000009eu; // ADR X30, #16 - (*outpp)[2] = 0xd61f0220u; // BR X17 - memcpy(*outpp + 3, &absolute_addr, sizeof(absolute_addr)); - *outpp += 5; - } //if - } else { - if (special_fix_type) { - intptr_t ref_idx = ctxp->get_ref_ins_index(absolute_addr); - if (ref_idx <= current_idx) { - new_pc_offset = static_cast(ctxp->dat[ref_idx].ins - - reinterpret_cast(*outpp)) - >> 2; - } else { - ctxp->insert_fix_map(ref_idx, *outpp, 0u, rmask); - new_pc_offset = 0; - } //if - } //if - - (*outpp)[0] = opc | (new_pc_offset & ~mask); - ++(*outpp); - } //if - - ++(*inpp); - return ctxp->process_fix_map(current_idx), true; - } - } - return false; -} - -//------------------------------------------------------------------------- - -static bool __fix_cond_comp_test_branch(instruction inpp, instruction outpp, context *ctxp) { - constexpr uint32_t lsb = 5u; - constexpr uint32_t lmask01 = 0xff00001fu; // 0b11111111000000000000000000011111 - constexpr uint32_t mask0 = 0xff000010u; // 0b11111111000000000000000000010000 - constexpr uint32_t op_bc = 0x54000000u; // "b.c" ADDR_PCREL19 - constexpr uint32_t mask1 = 0x7f000000u; // 0b01111111000000000000000000000000 - constexpr uint32_t op_cbz = 0x34000000u; // "cbz" Rt, ADDR_PCREL19 - constexpr uint32_t op_cbnz = 0x35000000u; // "cbnz" Rt, ADDR_PCREL19 - constexpr uint32_t lmask2 = 0xfff8001fu; // 0b11111111111110000000000000011111 - constexpr uint32_t mask2 = 0x7f000000u; // 0b01111111000000000000000000000000 - constexpr uint32_t op_tbz = 0x36000000u; // 0b00110110000000000000000000000000 "tbz" Rt, BIT_NUM, ADDR_PCREL14 - constexpr uint32_t op_tbnz = 0x37000000u; // 0b00110111000000000000000000000000 "tbnz" Rt, BIT_NUM, ADDR_PCREL14 - - const uint32_t ins = *(*inpp); - uint32_t lmask = lmask01; - if ((ins & mask0) != op_bc) { - uint32_t opc = ins & mask1; - if (opc != op_cbz && opc != op_cbnz) { - opc = ins & mask2; - if (opc != op_tbz && opc != op_tbnz) { - return false; - } //if - lmask = lmask2; - } //if - } //if - - intptr_t current_idx = ctxp->get_and_set_current_index(*inpp, *outpp); - int64_t absolute_addr = reinterpret_cast(*inpp) + ((ins & ~lmask) >> (lsb - 2u)); - int64_t new_pc_offset = - static_cast(absolute_addr - reinterpret_cast(*outpp)) >> 2; // shifted - bool special_fix_type = ctxp->is_in_fixing_range(absolute_addr); - if (!special_fix_type && llabs(new_pc_offset) > (~lmask >> lsb)) { - if ((reinterpret_cast(*outpp + 4) & 7u) != 0u) { - (*outpp)[0] = A64_NOP; - ctxp->reset_current_ins(current_idx, ++(*outpp)); - } //if - (*outpp)[0] = (((8u >> 2u) << lsb) & ~lmask) | (ins & lmask); // B.C #0x8 - (*outpp)[1] = 0x14000005u; // B #0x14 - (*outpp)[2] = 0x58000051u; // LDR X17, #0x8 - (*outpp)[3] = 0xd61f0220u; // BR X17 - memcpy(*outpp + 4, &absolute_addr, sizeof(absolute_addr)); - *outpp += 6; - } else { - if (special_fix_type) { - intptr_t ref_idx = ctxp->get_ref_ins_index(absolute_addr); - if (ref_idx <= current_idx) { - new_pc_offset = static_cast(ctxp->dat[ref_idx].ins - - reinterpret_cast(*outpp)) >> 2; - } else { - ctxp->insert_fix_map(ref_idx, *outpp, lsb, ~lmask); - new_pc_offset = 0; - } //if - } //if - - (*outpp)[0] = (static_cast(new_pc_offset << lsb) & ~lmask) | (ins & lmask); - ++(*outpp); - } //if - - ++(*inpp); - return ctxp->process_fix_map(current_idx), true; -} - -//------------------------------------------------------------------------- - -static bool __fix_loadlit(instruction inpp, instruction outpp, context *ctxp) { - const uint32_t ins = *(*inpp); - - // memory prefetch("prfm"), just skip it - // http://infocenter.arm.com/help/topic/com.arm.doc.100069_0608_00_en/pge1427897420050.html - if ((ins & 0xff000000u) == 0xd8000000u) { - ctxp->process_fix_map(ctxp->get_and_set_current_index(*inpp, *outpp)); - ++(*inpp); - return true; - } //if - - constexpr uint32_t msb = 8u; - constexpr uint32_t lsb = 5u; - constexpr uint32_t mask_30 = 0x40000000u; // 0b01000000000000000000000000000000 - constexpr uint32_t mask_31 = 0x80000000u; // 0b10000000000000000000000000000000 - constexpr uint32_t lmask = 0xff00001fu; // 0b11111111000000000000000000011111 - constexpr uint32_t mask_ldr = 0xbf000000u; // 0b10111111000000000000000000000000 - constexpr uint32_t op_ldr = 0x18000000u; // 0b00011000000000000000000000000000 "LDR Wt/Xt, label" | ADDR_PCREL19 - constexpr uint32_t mask_ldrv = 0x3f000000u; // 0b00111111000000000000000000000000 - constexpr uint32_t op_ldrv = 0x1c000000u; // 0b00011100000000000000000000000000 "LDR St/Dt/Qt, label" | ADDR_PCREL19 - constexpr uint32_t mask_ldrsw = 0xff000000u; // 0b11111111000000000000000000000000 - constexpr uint32_t op_ldrsw = 0x98000000u; // "LDRSW Xt, label" | ADDR_PCREL19 | load register signed word - // LDR S0, #0 | 0b00011100000000000000000000000000 | 32-bit - // LDR D0, #0 | 0b01011100000000000000000000000000 | 64-bit - // LDR Q0, #0 | 0b10011100000000000000000000000000 | 128-bit - // INVALID | 0b11011100000000000000000000000000 | may be 256-bit - - uint32_t mask = mask_ldr; - uintptr_t faligned = (ins & mask_30) ? 7u : 3u; - if ((ins & mask_ldr) != op_ldr) { - mask = mask_ldrv; - if (faligned != 7u) - faligned = (ins & mask_31) ? 15u : 3u; - if ((ins & mask_ldrv) != op_ldrv) { - if ((ins & mask_ldrsw) != op_ldrsw) { - return false; - } //if - mask = mask_ldrsw; - faligned = 7u; - } //if - } //if - - intptr_t current_idx = ctxp->get_and_set_current_index(*inpp, *outpp); - int64_t absolute_addr = reinterpret_cast(*inpp) + - ((static_cast(ins << msb) >> (msb + lsb - 2u)) & ~3u); - int64_t new_pc_offset = - static_cast(absolute_addr - reinterpret_cast(*outpp)) >> 2; // shifted - bool special_fix_type = ctxp->is_in_fixing_range(absolute_addr); - // special_fix_type may encounter issue when there are mixed data and code - if (special_fix_type || (llabs(new_pc_offset) + (faligned + 1u - 4u)) > (~lmask >> lsb)) { - while ((reinterpret_cast(*outpp + 2) & faligned) != 0u) { - *(*outpp)++ = A64_NOP; - } - ctxp->reset_current_ins(current_idx, *outpp); - - // Note that if memory at absolute_addr is writeable (non-const), we will fail to fetch it. - // And what's worse, we may unexpectedly overwrite something if special_fix_type is true... - uint32_t ns = static_cast((faligned + 1) / sizeof(uint32_t)); - (*outpp)[0] = (((8u >> 2u) << lsb) & ~mask) | (ins & lmask); // LDR #0x8 - (*outpp)[1] = 0x14000001u + ns; // B #0xc - memcpy(*outpp + 2, reinterpret_cast(absolute_addr), faligned + 1); - *outpp += 2 + ns; - } else { - faligned >>= 2; // new_pc_offset is shifted and 4-byte aligned - while ((new_pc_offset & faligned) != 0) { - *(*outpp)++ = A64_NOP; - new_pc_offset = - static_cast(absolute_addr - reinterpret_cast(*outpp)) >> 2; - } - ctxp->reset_current_ins(current_idx, *outpp); - - (*outpp)[0] = (static_cast(new_pc_offset << lsb) & ~mask) | (ins & lmask); - ++(*outpp); - } //if - - ++(*inpp); - return ctxp->process_fix_map(current_idx), true; -} - -//------------------------------------------------------------------------- - -static bool __fix_pcreladdr(instruction inpp, instruction outpp, context *ctxp) { - // Load a PC-relative address into a register - // http://infocenter.arm.com/help/topic/com.arm.doc.100069_0608_00_en/pge1427897645644.html - constexpr uint32_t msb = 8u; - constexpr uint32_t lsb = 5u; - constexpr uint32_t mask = 0x9f000000u; // 0b10011111000000000000000000000000 - constexpr uint32_t rmask = 0x0000001fu; // 0b00000000000000000000000000011111 - constexpr uint32_t lmask = 0xff00001fu; // 0b11111111000000000000000000011111 - constexpr uint32_t fmask = 0x00ffffffu; // 0b00000000111111111111111111111111 - constexpr uint32_t max_val = 0x001fffffu; // 0b00000000000111111111111111111111 - constexpr uint32_t op_adr = 0x10000000u; // "adr" Rd, ADDR_PCREL21 - constexpr uint32_t op_adrp = 0x90000000u; // "adrp" Rd, ADDR_ADRP - - const uint32_t ins = *(*inpp); - intptr_t current_idx; - switch (ins & mask) { - case op_adr: { - current_idx = ctxp->get_and_set_current_index(*inpp, *outpp); - int64_t lsb_bytes = static_cast(ins << 1u) >> 30u; - int64_t absolute_addr = reinterpret_cast(*inpp) + - (((static_cast(ins << msb) >> (msb + lsb - 2u)) & - ~3u) | lsb_bytes); - int64_t new_pc_offset = static_cast(absolute_addr - - reinterpret_cast(*outpp)); - bool special_fix_type = ctxp->is_in_fixing_range(absolute_addr); - if (!special_fix_type && llabs(new_pc_offset) > max_val) { - if ((reinterpret_cast(*outpp + 2) & 7u) != 0u) { - (*outpp)[0] = A64_NOP; - ctxp->reset_current_ins(current_idx, ++(*outpp)); - } //if - - (*outpp)[0] = - 0x58000000u | (((8u >> 2u) << lsb) & ~mask) | (ins & rmask); // LDR #0x8 - (*outpp)[1] = 0x14000003u; // B #0xc - memcpy(*outpp + 2, &absolute_addr, sizeof(absolute_addr)); - *outpp += 4; - } else { - if (special_fix_type) { - intptr_t ref_idx = ctxp->get_ref_ins_index(absolute_addr & ~3ull); - if (ref_idx <= current_idx) { - new_pc_offset = static_cast(ctxp->dat[ref_idx].ins - - reinterpret_cast(*outpp)); - } else { - ctxp->insert_fix_map(ref_idx, *outpp, lsb, fmask); - new_pc_offset = 0; - } //if - } //if - - // the lsb_bytes will never be changed, so we can use lmask to keep it - (*outpp)[0] = (static_cast(new_pc_offset << (lsb - 2u)) & fmask) | - (ins & lmask); - ++(*outpp); - } //if - } - break; - case op_adrp: { - current_idx = ctxp->get_and_set_current_index(*inpp, *outpp); - int32_t lsb_bytes = static_cast(ins << 1u) >> 30u; - int64_t absolute_addr = (reinterpret_cast(*inpp) & ~0xfffll) + - ((((static_cast(ins << msb) >> (msb + lsb - 2u)) & - ~3u) | lsb_bytes) << 12); - A64_LOGI("ins = 0x%.8X, pc = %p, abs_addr = %p", - ins, *inpp, reinterpret_cast(absolute_addr)); - if (ctxp->is_in_fixing_range(absolute_addr)) { - intptr_t ref_idx = ctxp->get_ref_ins_index(absolute_addr/* & ~3ull*/); - if (ref_idx > current_idx) { - // the bottom 12 bits of absolute_addr are masked out, - // so ref_idx must be less than or equal to current_idx! - A64_LOGE("ref_idx must be less than or equal to current_idx!"); - } //if - - // *absolute_addr may be changed due to relocation fixing - A64_LOGI("What is the correct way to fix this?"); - *(*outpp)++ = ins; // 0x90000000u; - } else { - if ((reinterpret_cast(*outpp + 2) & 7u) != 0u) { - (*outpp)[0] = A64_NOP; - ctxp->reset_current_ins(current_idx, ++(*outpp)); - } //if - - (*outpp)[0] = - 0x58000000u | (((8u >> 2u) << lsb) & ~mask) | (ins & rmask); // LDR #0x8 - (*outpp)[1] = 0x14000003u; // B #0xc - memcpy(*outpp + 2, &absolute_addr, sizeof(absolute_addr)); // potential overflow? - *outpp += 4; - } //if - } - break; - default: - return false; - } - - ctxp->process_fix_map(current_idx); - ++(*inpp); - return true; -} - -//------------------------------------------------------------------------- - -static void __fix_instructions(uint32_t *__restrict inp, int32_t count, uint32_t *__restrict outp) { - context ctx; - ctx.basep = reinterpret_cast(inp); - ctx.endp = reinterpret_cast(inp + count); - memset(ctx.dat, 0, sizeof(ctx.dat)); - static_assert(sizeof(ctx.dat) / sizeof(ctx.dat[0]) == A64_MAX_INSTRUCTIONS, - "please use A64_MAX_INSTRUCTIONS!"); -#ifndef NDEBUG - if (count > A64_MAX_INSTRUCTIONS) { - A64_LOGE("too many fixing instructions!"); - } //if -#endif // NDEBUG - - while (--count >= 0) { - if (__fix_branch_imm(&inp, &outp, &ctx)) continue; - if (__fix_cond_comp_test_branch(&inp, &outp, &ctx)) continue; - if (__fix_loadlit(&inp, &outp, &ctx)) continue; - if (__fix_pcreladdr(&inp, &outp, &ctx)) continue; - - // without PC-relative offset - ctx.process_fix_map(ctx.get_and_set_current_index(inp, outp)); - *(outp++) = *(inp++); - } - - constexpr uint_fast64_t mask = 0x03ffffffu; // 0b00000011111111111111111111111111 - auto callback = reinterpret_cast(inp); - auto pc_offset = static_cast(callback - reinterpret_cast(outp)) >> 2; - if (llabs(pc_offset) > mask) { - if ((reinterpret_cast(outp + 2) & 7u) != 0u) { - outp[0] = A64_NOP; - ++outp; - } //if - outp[0] = 0x58000051u; // LDR X17, #0x8 - outp[1] = 0xd61f0220u; // BR X17 - *reinterpret_cast(outp + 2) = callback; -// outp += 4; - } else { - outp[0] = 0x14000000u | (pc_offset & mask); // "B" ADDR_PCREL26 -// ++outp; - } //if -} - -//------------------------------------------------------------------------- - -extern "C" { -#define __attribute __attribute__ -#define aligned(x) __aligned__(x) -#define __intval(p) reinterpret_cast(p) -#define __uintval(p) reinterpret_cast(p) -#define __ptr(p) reinterpret_cast(p) -#define __page_size 4096 -#define __page_align(n) __align_up(static_cast(n), __page_size) -#define __ptr_align(x) __ptr(__align_down(reinterpret_cast(x), __page_size)) -#define __align_up(x, n) (((x) + ((n) - 1)) & ~((n) - 1)) -#define __align_down(x, n) ((x) & -(n)) -#define __countof(x) static_cast(sizeof(x) / sizeof((x)[0])) // must be signed -#define __atomic_increase(p) __sync_add_and_fetch(p, 1) -#define __sync_cmpswap(p, v, n) __sync_bool_compare_and_swap(p, v, n) -#define __predict_true(exp) __builtin_expect((exp) != 0, 1) -#define __flush_cache(c, n) __builtin___clear_cache(reinterpret_cast(c), reinterpret_cast(c) + n) -#define __make_rwx(p, n) ::mprotect(__ptr_align(p), \ - __page_align(__uintval(p) + n) != __page_align(__uintval(p)) ? __page_align(n) + __page_size : __page_align(n), \ - PROT_READ | PROT_WRITE | PROT_EXEC) - -//------------------------------------------------------------------------- - -static __attribute((aligned(__page_size))) uint32_t __insns_pool[A64_MAX_BACKUPS][ - A64_MAX_INSTRUCTIONS * 10]; - -//------------------------------------------------------------------------- - -class A64HookInit { -public: - A64HookInit() { - __make_rwx(__insns_pool, sizeof(__insns_pool)); - A64_LOGI("insns pool initialized."); - } -}; -static A64HookInit __init; - -//------------------------------------------------------------------------- - -static uint32_t *FastAllocateTrampoline() { - static_assert((A64_MAX_INSTRUCTIONS * 10 * sizeof(uint32_t)) % 8 == 0, "8-byte align"); - static volatile int32_t __index = -1; - - int32_t i = __atomic_increase(&__index); - if (__predict_true(i >= 0 && i < __countof(__insns_pool))) { - return __insns_pool[i]; - } //if - - A64_LOGE("failed to allocate trampoline!"); - return NULL; -} - -//------------------------------------------------------------------------- - -A64_JNIEXPORT void *A64HookFunctionV(void *const symbol, void *const replace, - void *const rwx, const uintptr_t rwx_size) { - constexpr uint_fast64_t mask = 0x03ffffffu; // 0b00000011111111111111111111111111 - - uint32_t *trampoline = static_cast(rwx), *original = static_cast(symbol); - - static_assert(A64_MAX_INSTRUCTIONS >= 5, "please fix A64_MAX_INSTRUCTIONS!"); - auto pc_offset = static_cast(__intval(replace) - __intval(symbol)) >> 2; - if (llabs(pc_offset) > mask) { - int32_t count = (reinterpret_cast(original + 2) & 7u) != 0u ? 5 : 4; - if (trampoline) { - if (rwx_size < count * 10u) { - return NULL; - } //if - __fix_instructions(original, count, trampoline); - } //if - - if (__make_rwx(original, 5 * sizeof(uint32_t)) == 0) { - if (count == 5) { - original[0] = A64_NOP; - ++original; - } //if - original[0] = 0x58000051u; // LDR X17, #0x8 - original[1] = 0xd61f0220u; // BR X17 - *reinterpret_cast(original + 2) = __intval(replace); - __flush_cache(symbol, 5 * sizeof(uint32_t)); - - A64_LOGI("inline hook %p->%p successfully! %zu bytes overwritten", - symbol, replace, 5 * sizeof(uint32_t)); - } else { - - trampoline = NULL; - } //if - } else { - if (trampoline) { - if (rwx_size < 1u * 10u) { - return NULL; - } //if - __fix_instructions(original, 1, trampoline); - } //if - - if (__make_rwx(original, 1 * sizeof(uint32_t)) == 0) { - __sync_cmpswap(original, *original, - 0x14000000u | (pc_offset & mask)); // "B" ADDR_PCREL26 - __flush_cache(symbol, 1 * sizeof(uint32_t)); - - A64_LOGI("inline hook %p->%p successfully! %zu bytes overwritten", - symbol, replace, 1 * sizeof(uint32_t)); - } else { - - trampoline = NULL; - } //if - } //if - - return trampoline; -} - -//------------------------------------------------------------------------- - -A64_JNIEXPORT void A64HookFunction(void *const symbol, void *const replace, void **result) { - void *trampoline = NULL; - if (result != NULL) { - trampoline = FastAllocateTrampoline(); - *result = trampoline; - if (trampoline == NULL) return; - } //if - - trampoline = A64HookFunctionV(symbol, replace, trampoline, A64_MAX_INSTRUCTIONS * 10u); - if (trampoline == NULL && result != NULL) { - *result = NULL; - } //if -} -} - -#endif // defined(__aarch64__) \ No newline at end of file diff --git a/xposedmodule/src/main/cpp/And64InlineHook.hpp b/xposedmodule/src/main/cpp/And64InlineHook.hpp deleted file mode 100755 index 2511754..0000000 --- a/xposedmodule/src/main/cpp/And64InlineHook.hpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * @date : 2018/01/24 - * @author : rrrfff@foxmail.com - * https://github.com/rrrfff/And64InlineHook - */ -/* - MIT License - - Copyright (c) 2017 rrrfff@foxmail.com - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. - */ -#pragma once -#define A64_MAX_BACKUPS 256 - -#ifdef __cplusplus -extern "C" { -#endif - - void A64HookFunction(void *const symbol, void *const replace, void **result); - void *A64HookFunctionV(void *const symbol, void *const replace, - void *const rwx, const uintptr_t rwx_size); - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/xposedmodule/src/main/cpp/dlopen.c b/xposedmodule/src/main/cpp/dlopen.c deleted file mode 100644 index c2a9714..0000000 --- a/xposedmodule/src/main/cpp/dlopen.c +++ /dev/null @@ -1,191 +0,0 @@ -/* - * - * @author : rrrfff@foxmail.com - * https://github.com/rrrfff/ndk_dlopen - * - */ -#include "dlopen.h" -#include -#include -#include -#include -#include -#include -#include - -#define LOG_TAG "ndk_dlopen" -#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) - -static volatile int SDK_INT = 0; -static void *quick_on_stack_back; -static union -{ - void *generic_stub; - void *(*quick_on_stack_replace)(const void *param1, const void *param2, - const void *fake_trampoline, const void *called); -} STUBS; - -void JNIEXPORT ndk_init(JNIEnv *env) -{ - if (SDK_INT <= 0) { - char sdk[PROP_VALUE_MAX]; - __system_property_get("ro.build.version.sdk", sdk); - SDK_INT = atoi(sdk); - LOGI("SDK_INT = %d", SDK_INT); - if (SDK_INT >= 24) { - static __attribute__((__aligned__(PAGE_SIZE))) uint8_t __insns[PAGE_SIZE]; - STUBS.generic_stub = __insns; - mprotect(__insns, sizeof(__insns), PROT_READ | PROT_WRITE | PROT_EXEC); - - // we are currently hijacking "FatalError" as a fake system-call trampoline - uintptr_t pv = (uintptr_t)(*env)->FatalError; - uintptr_t pu = (pv | (PAGE_SIZE - 1)) + 1u; - uintptr_t pd = (pv & ~(PAGE_SIZE - 1)); - mprotect((void *)pd, pv + 8u >= pu ? PAGE_SIZE * 2u : PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC); - quick_on_stack_back = (void *)pv; - -#if defined(__i386__) - /* - DEFINE_FUNCTION art_quick_on_stack_replace - movl 12(REG_VAR(esp)), REG_VAR(eax) - movl (REG_VAR(esp)), REG_VAR(edx) - movl REG_VAR(eax), (REG_VAR(esp)) - movl REG_VAR(edx), 12(REG_VAR(esp)) - pushl 16(REG_VAR(esp)) - ret - END_FUNCTION art_quick_on_stack_replace - */ - memcpy(__insns, "\x8B\x44\x24\x0C\x8B\x14\x24\x89\x04\x24\x89\x54\x24\x0C\xFF\x74\x24\x10\xC3", 19); - /* - DEFINE_FUNCTION art_quick_on_stack_back - push 8(REG_VAR(esp)) - ret - END_FUNCTION art_quick_on_stack_back - */ - memcpy(quick_on_stack_back, "\xC3\xFF\x74\x24\x08\xC3", 6); - quick_on_stack_back = (void *)(pv + 1); // inserts `ret` at first -#elif defined(__x86_64__) - // rdi, rsi, rdx, rcx, r8, r9 - /* - 0x0000000000000000: 52 push rdx - 0x0000000000000001: FF E1 jmp rcx - */ - memcpy(__insns, "\x52\xFF\xE1", 3); - /* - 0x0000000000000000: C3 ret - */ - memcpy(quick_on_stack_back, "\xC3", 1); -#elif defined(__aarch64__) - // x0~x7 - /* - 0x0000000000000000: FD 7B BF A9 stp x29, x30, [sp, #-0x10]! - 0x0000000000000004: FD 03 00 91 mov x29, sp - 0x0000000000000008: FE 03 02 AA mov x30, x2 - 0x000000000000000C: 60 00 1F D6 br x3 - */ - memcpy(__insns, "\xFD\x7B\xBF\xA9\xFD\x03\x00\x91\xFE\x03\x02\xAA\x60\x00\x1F\xD6", 16); - /* - 0x0000000000000000: FD 7B C1 A8 ldp x29, x30, [sp], #0x10 - 0x0000000000000004: C0 03 5F D6 ret - */ - memcpy(quick_on_stack_back, "\xFD\x7B\xC1\xA8\xC0\x03\x5F\xD6", 8); -#elif defined(__arm__) - // r0~r3 - /* - 0x0000000000000000: 04 E0 2D E5 str lr, [sp, #-4]! - 0x0000000000000004: 02 E0 A0 E1 mov lr, r2 - 0x0000000000000008: 13 FF 2F E1 bx r3 - */ - memcpy(__insns, "\x04\xE0\x2D\xE5\x02\xE0\xA0\xE1\x13\xFF\x2F\xE1", 12); - if ((pv & 1u) != 0u) { // Thumb - /* - 0x0000000000000000: 08 BC pop {r3} - 0x0000000000000002: 18 47 bx r3 - */ - memcpy((void *)(pv - 1), "\x08\xBC\x18\x47", 4); - } else { - /* - 0x0000000000000000: 04 30 9D E4 pop {r3} - 0x0000000000000004: 13 FF 2F E1 bx r3 - */ - memcpy(quick_on_stack_back, "\x04\x30\x9D\xE4\x13\xFF\x2F\xE1", 8); - } //if -#else -# error "not supported" -#endif - LOGI("init done! quick_on_stack_replace = %p, quick_on_stack_back = %p", - STUBS.generic_stub, quick_on_stack_back); - } //if - } //if -} - -void *JNIEXPORT ndk_dlopen(const char *filename, int flag) -{ - if (SDK_INT >= 24) { -#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || defined(__arm__) - return STUBS.quick_on_stack_replace(filename, (void *)flag, - quick_on_stack_back, dlopen); -#else -# error "not supported" -#endif - } //if - - return dlopen(filename, flag); -} - -int JNIEXPORT ndk_dlclose(void *handle) -{ - if (SDK_INT >= 24) { -#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || defined(__arm__) - return (int)STUBS.quick_on_stack_replace(handle, NULL, - quick_on_stack_back, dlclose); -#else -# error "not supported" -#endif - } //if - - return dlclose(handle); -} - -const char *JNIEXPORT ndk_dlerror(void) -{ - if (SDK_INT >= 24) { -#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || defined(__arm__) - return STUBS.quick_on_stack_replace(NULL, NULL, - quick_on_stack_back, dlerror); -#else -# error "not supported" -#endif - } //if - - return dlerror(); -} - -void *JNIEXPORT ndk_dlsym(void *handle, const char *symbol) -{ - if (SDK_INT >= 24) { -#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || defined(__arm__) - return STUBS.quick_on_stack_replace(handle, symbol, - quick_on_stack_back, dlsym); - -#else -# error "not supported" -#endif - } //if - - return dlsym(handle, symbol); -} - -int JNIEXPORT ndk_dladdr(const void *ddr, Dl_info *info) -{ - if (SDK_INT >= 24) { -#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || defined(__arm__) - return (int)STUBS.quick_on_stack_replace(ddr, info, - quick_on_stack_back, dladdr); -#else -# error "not supported" -#endif - } //if - - return dladdr(ddr, info); -} \ No newline at end of file diff --git a/xposedmodule/src/main/cpp/dlopen.h b/xposedmodule/src/main/cpp/dlopen.h deleted file mode 100644 index 58374ea..0000000 --- a/xposedmodule/src/main/cpp/dlopen.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * - * @author : rrrfff@foxmail.com - * https://github.com/rrrfff/ndk_dlopen - * - */ -#pragma once - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -void ndk_init(JNIEnv *env); -void *ndk_dlopen(const char *filename, int flag); -int ndk_dlclose(void *handle); -const char *ndk_dlerror(void); -void *ndk_dlsym(void *handle, const char *symbol); -int ndk_dladdr(const void *ddr, Dl_info *info); - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/xposedmodule/src/main/cpp/inlineHook.c b/xposedmodule/src/main/cpp/inlineHook.c deleted file mode 100755 index 004f13d..0000000 --- a/xposedmodule/src/main/cpp/inlineHook.c +++ /dev/null @@ -1,416 +0,0 @@ -/* -thumb16 thumb32 arm32 inlineHook -author: ele7enxxh -mail: ele7enxxh@qq.com -website: ele7enxxh.com -modified time: 2015-01-23 -created time: 2015-11-30 -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "relocate.h" -#include "inlineHook.h" - -#ifndef PAGE_SIZE -#define PAGE_SIZE 4096 -#endif -#define TAG "developerhelper.xposed.inlineHook.native-->" - -#define PAGE_START(addr) (~(PAGE_SIZE - 1) & (addr)) -#define SET_BIT0(addr) (addr | 1) -#define CLEAR_BIT0(addr) (addr & 0xFFFFFFFE) -#define TEST_BIT0(addr) (addr & 1) - -#define ACTION_ENABLE 0 -#define ACTION_DISABLE 1 - -enum hook_status { - REGISTERED, - HOOKED, -}; - -struct inlineHookItem { - uint32_t target_addr; - uint32_t new_addr; - uint32_t **proto_addr; - void *orig_instructions; - int orig_boundaries[4]; - int trampoline_boundaries[20]; - int count; - void *trampoline_instructions; - int length; - int status; - int mode; -}; - -struct inlineHookInfo { - struct inlineHookItem item[1024]; - int size; -}; - -static struct inlineHookInfo info = {0}; - -static int getAllTids(pid_t pid, pid_t *tids) { - char dir_path[32]; - DIR *dir; - int i; - struct dirent *entry; - pid_t tid; - - if (pid < 0) { - snprintf(dir_path, sizeof(dir_path), "/proc/self/task"); - } else { - snprintf(dir_path, sizeof(dir_path), "/proc/%d/task", pid); - } - - dir = opendir(dir_path); - if (dir == NULL) { - return 0; - } - - i = 0; - while ((entry = readdir(dir)) != NULL) { - tid = atoi(entry->d_name); - if (tid != 0 && tid != getpid()) { - tids[i++] = tid; - } - } - closedir(dir); - return i; -} - -static bool doProcessThreadPC(struct inlineHookItem *item, struct pt_regs *regs, int action) { - int offset; - int i; - - switch (action) { - case ACTION_ENABLE: - offset = regs->ARM_pc - CLEAR_BIT0(item->target_addr); - for (i = 0; i < item->count; ++i) { - if (offset == item->orig_boundaries[i]) { - regs->ARM_pc = (uint32_t) item->trampoline_instructions + - item->trampoline_boundaries[i]; - return true; - } - } - break; - case ACTION_DISABLE: - offset = regs->ARM_pc - (int) item->trampoline_instructions; - for (i = 0; i < item->count; ++i) { - if (offset == item->trampoline_boundaries[i]) { - regs->ARM_pc = CLEAR_BIT0(item->target_addr) + item->orig_boundaries[i]; - return true; - } - } - break; - } - - return false; -} - -static void processThreadPC(pid_t tid, struct inlineHookItem *item, int action) { - struct pt_regs regs; - - if (ptrace(PTRACE_GETREGS, tid, NULL, ®s) == 0) { - if (item == NULL) { - int pos; - - for (pos = 0; pos < info.size; ++pos) { - if (doProcessThreadPC(&info.item[pos], ®s, action) == true) { - break; - } - } - } else { - doProcessThreadPC(item, ®s, action); - } - - ptrace(PTRACE_SETREGS, tid, NULL, ®s); - } -} - -static pid_t freeze(struct inlineHookItem *item, int action) { - int count; - pid_t tids[1024]; - pid_t pid; - - pid = -1; - count = getAllTids(getpid(), tids); - if (count > 0) { - pid = fork(); - - if (pid == 0) { - int i; - - for (i = 0; i < count; ++i) { - if (ptrace(PTRACE_ATTACH, tids[i], NULL, NULL) == 0) { - waitpid(tids[i], NULL, WUNTRACED); - processThreadPC(tids[i], item, action); - } - } - - raise(SIGSTOP); - - for (i = 0; i < count; ++i) { - ptrace(PTRACE_DETACH, tids[i], NULL, NULL); - } - - raise(SIGKILL); - } else if (pid > 0) { - waitpid(pid, NULL, WUNTRACED); - } - } - - return pid; -} - -static void unFreeze(pid_t pid) { - if (pid < 0) { - return; - } - - kill(pid, SIGCONT); - wait(NULL); -} - -static bool isExecutableAddr(uint32_t addr) { - FILE *fp; - char line[1024]; - uint32_t start; - uint32_t end; - - fp = fopen("/proc/self/maps", "r"); - if (fp == NULL) { - return false; - } - - while (fgets(line, sizeof(line), fp)) { - if (strstr(line, "r-xp") || strstr(line, "rwxp")) { - start = strtoul(strtok(line, "-"), NULL, 16); - end = strtoul(strtok(NULL, " "), NULL, 16); - if (addr >= start && addr <= end) { - fclose(fp); - return true; - } - } - } - - fclose(fp); - - return false; -} - -static struct inlineHookItem *findInlineHookItem(uint32_t target_addr) { - int i; - - for (i = 0; i < info.size; ++i) { - if (info.item[i].target_addr == target_addr) { - return &info.item[i]; - } - } - - return NULL; -} - -static struct inlineHookItem *addInlineHookItem() { - struct inlineHookItem *item; - - if (info.size >= 1024) { - return NULL; - } - - item = &info.item[info.size]; - ++info.size; - - return item; -} - -static void deleteInlineHookItem(int pos) { - info.item[pos] = info.item[info.size - 1]; - --info.size; -} - -enum ele7en_status -registerInlineHook(uint32_t target_addr, uint32_t new_addr, uint32_t **proto_addr) { - struct inlineHookItem *item; - - if (!isExecutableAddr(target_addr) || !isExecutableAddr(new_addr)) { - return ELE7EN_ERROR_NOT_EXECUTABLE; - } - - item = findInlineHookItem(target_addr); - if (item != NULL) { - if (item->status == REGISTERED) { - return ELE7EN_ERROR_ALREADY_REGISTERED; - } else if (item->status == HOOKED) { - return ELE7EN_ERROR_ALREADY_HOOKED; - } else { - return ELE7EN_ERROR_UNKNOWN; - } - } - - item = addInlineHookItem(); - - item->target_addr = target_addr; - item->new_addr = new_addr; - item->proto_addr = proto_addr; - - item->length = TEST_BIT0(item->target_addr) ? 12 : 8; - item->orig_instructions = malloc(item->length); - __android_log_print(ANDROID_LOG_ERROR, TAG, "item->orig_instructions length=%d", item->length); - memcpy(item->orig_instructions, (void *) CLEAR_BIT0(item->target_addr), item->length); - __android_log_print(ANDROID_LOG_ERROR, TAG, "item->orig_instructions address=%p", - item->orig_instructions); - item->trampoline_instructions = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); - relocateInstruction(item->target_addr, item->orig_instructions, item->length, - item->trampoline_instructions, item->orig_boundaries, - item->trampoline_boundaries, &item->count); - - item->status = REGISTERED; - - return ELE7EN_OK; -} - -static void doInlineUnHook(struct inlineHookItem *item, int pos) { - mprotect((void *) PAGE_START(CLEAR_BIT0(item->target_addr)), PAGE_SIZE * 2, - PROT_READ | PROT_WRITE | PROT_EXEC); - memcpy((void *) CLEAR_BIT0(item->target_addr), item->orig_instructions, item->length); - mprotect((void *) PAGE_START(CLEAR_BIT0(item->target_addr)), PAGE_SIZE * 2, - PROT_READ | PROT_EXEC); - munmap(item->trampoline_instructions, PAGE_SIZE); - free(item->orig_instructions); - - deleteInlineHookItem(pos); - - __builtin___clear_cache(CLEAR_BIT0(item->target_addr), - CLEAR_BIT0(item->target_addr) + item->length); -} - -enum ele7en_status inlineUnHook(uint32_t target_addr) { - int i; - - for (i = 0; i < info.size; ++i) { - if (info.item[i].target_addr == target_addr && info.item[i].status == HOOKED) { - pid_t pid; - - pid = freeze(&info.item[i], ACTION_DISABLE); - - doInlineUnHook(&info.item[i], i); - - unFreeze(pid); - - return ELE7EN_OK; - } - } - - return ELE7EN_ERROR_NOT_HOOKED; -} - -void inlineUnHookAll() { - pid_t pid; - int i; - - pid = freeze(NULL, ACTION_DISABLE); - - for (i = 0; i < info.size; ++i) { - if (info.item[i].status == HOOKED) { - doInlineUnHook(&info.item[i], i); - --i; - } - } - - unFreeze(pid); -} - -static void doInlineHook(struct inlineHookItem *item) { - mprotect((void *) PAGE_START(CLEAR_BIT0(item->target_addr)), PAGE_SIZE * 2, - PROT_READ | PROT_WRITE | PROT_EXEC); - - if (item->proto_addr != NULL) { - *(item->proto_addr) = TEST_BIT0(item->target_addr) ? (uint32_t *) SET_BIT0( - (uint32_t) item->trampoline_instructions) : item->trampoline_instructions; - } - - if (TEST_BIT0(item->target_addr)) { - int i; - - i = 0; - if (CLEAR_BIT0(item->target_addr) % 4 != 0) { - ((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = 0xBF00; // NOP - } - ((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = 0xF8DF; - ((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = 0xF000; // LDR.W PC, [PC] - ((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = item->new_addr & 0xFFFF; - ((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = item->new_addr >> 16; - } else { - ((uint32_t *) (item->target_addr))[0] = 0xe51ff004; // LDR PC, [PC, #-4] - ((uint32_t *) (item->target_addr))[1] = item->new_addr; - } - - mprotect((void *) PAGE_START(CLEAR_BIT0(item->target_addr)), PAGE_SIZE * 2, - PROT_READ | PROT_EXEC); - - item->status = HOOKED; - - __builtin___clear_cache(CLEAR_BIT0(item->target_addr), - CLEAR_BIT0(item->target_addr) + item->length); -} - -enum ele7en_status inlineHook(uint32_t target_addr) { - int i; - struct inlineHookItem *item; - - item = NULL; - for (i = 0; i < info.size; ++i) { - if (info.item[i].target_addr == target_addr) { - item = &info.item[i]; - break; - } - } - - if (item == NULL) { - return ELE7EN_ERROR_NOT_REGISTERED; - } - - if (item->status == REGISTERED) { - pid_t pid; - - pid = freeze(item, ACTION_ENABLE); - - doInlineHook(item); - - unFreeze(pid); - - return ELE7EN_OK; - } else if (item->status == HOOKED) { - return ELE7EN_ERROR_ALREADY_HOOKED; - } else { - return ELE7EN_ERROR_UNKNOWN; - } -} - -void inlineHookAll() { - pid_t pid; - int i; - - pid = freeze(NULL, ACTION_ENABLE); - - for (i = 0; i < info.size; ++i) { - if (info.item[i].status == REGISTERED) { - doInlineHook(&info.item[i]); - } - } - - unFreeze(pid); -} diff --git a/xposedmodule/src/main/cpp/inlineHook.h b/xposedmodule/src/main/cpp/inlineHook.h deleted file mode 100755 index 86bc6a8..0000000 --- a/xposedmodule/src/main/cpp/inlineHook.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef _INLINEHOOK_H -#define _INLINEHOOK_H - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -enum ele7en_status { - ELE7EN_ERROR_UNKNOWN = -1, - ELE7EN_OK = 0, - ELE7EN_ERROR_NOT_INITIALIZED, - ELE7EN_ERROR_NOT_EXECUTABLE, - ELE7EN_ERROR_NOT_REGISTERED, - ELE7EN_ERROR_NOT_HOOKED, - ELE7EN_ERROR_ALREADY_REGISTERED, - ELE7EN_ERROR_ALREADY_HOOKED, - ELE7EN_ERROR_SO_NOT_FOUND, - ELE7EN_ERROR_FUNCTION_NOT_FOUND -}; - -enum ele7en_status registerInlineHook(uint32_t target_addr, uint32_t new_addr, uint32_t **proto_addr); -enum ele7en_status inlineUnHook(uint32_t target_addr); -void inlineUnHookAll(); -enum ele7en_status inlineHook(uint32_t target_addr); -void inlineHookAll(); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/xposedmodule/src/main/cpp/native.cpp b/xposedmodule/src/main/cpp/native.cpp deleted file mode 100644 index 1305934..0000000 --- a/xposedmodule/src/main/cpp/native.cpp +++ /dev/null @@ -1,55 +0,0 @@ -// -// Created by WrBug on 2018/3/23. -// -#include "native.h" -#include "inlineHook.h" - -#define TAG "developerhelper.xposed.native.native-->" - - -JNIEXPORT void JNICALL Java_com_wrbug_developerhelper_xposed_dumpdex_Native_dump - (JNIEnv *env, jclass obj, jstring packageName) { - - static bool is_hook = false; - char *p = (char *) env->GetStringUTFChars(packageName, 0); - __android_log_print(ANDROID_LOG_ERROR, TAG, "%s", p); - if (is_hook) { - __android_log_print(ANDROID_LOG_INFO, TAG, "hooked ignore"); - return; - } - init_package_name(p); - env->ReleaseStringChars(packageName, (const jchar *) p); - ndk_init(env); - void *handle = ndk_dlopen("libart.so", RTLD_NOW); - if (handle == NULL) { - __android_log_print(ANDROID_LOG_ERROR, TAG, "Error: unable to find the SO : libart.so"); - return; - } - void *open_common_addr = ndk_dlsym(handle, get_open_function_flag()); - if (open_common_addr == NULL) { - __android_log_print(ANDROID_LOG_ERROR, TAG, - "Error: unable to find the Symbol : "); - return; - } -#if defined(__aarch64__) - A64HookFunction(open_common_addr, get_new_open_function_addr(), get_old_open_function_addr()); - __android_log_print(ANDROID_LOG_DEFAULT, TAG, "loaded so: libart.so"); -#elif defined(__arm__) - if (registerInlineHook((uint32_t) open_common_addr, (uint32_t) get_new_open_function_addr(), - (uint32_t **) get_old_open_function_addr()) != ELE7EN_OK) { - __android_log_print(ANDROID_LOG_ERROR, TAG, "register1 hook failed!"); - return; - } else { - __android_log_print(ANDROID_LOG_ERROR, TAG, "register1 hook success!"); - } - if (inlineHook((uint32_t) open_common_addr) != ELE7EN_OK) { - __android_log_print(ANDROID_LOG_ERROR, TAG, "register2 hook failed!"); - return; - } else { - __android_log_print(ANDROID_LOG_ERROR, TAG, "register2 hook success!"); - } - __android_log_print(ANDROID_LOG_DEFAULT, TAG, "loaded so: libart.so"); -#endif - __android_log_print(ANDROID_LOG_INFO, TAG, "hook init complete"); - is_hook = true; -} diff --git a/xposedmodule/src/main/cpp/native.h b/xposedmodule/src/main/cpp/native.h deleted file mode 100644 index ba727d4..0000000 --- a/xposedmodule/src/main/cpp/native.h +++ /dev/null @@ -1,43 +0,0 @@ -/* DO NOT EDIT THIS FILE - it is machine generated */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "dlopen.h" -#include "util/deviceutils.h" -#include "util/fileutils.h" - -#if defined(__aarch64__) - -#include "And64InlineHook.hpp" - -#elif defined(__arm__) -#include "inlineHook.h" -#endif - -#ifndef _Included_com_wrbug_xposeddemo_Native -#define _Included_com_wrbug_xposeddemo_Native -#ifdef __cplusplus -extern "C" { - -#endif -/* - * Class: com_wrbug_xposeddemo_Native - * Method: test - * Signature: ()Ljava/lang/String; - */ -JNIEXPORT void JNICALL Java_com_wrbug_developerhelper_xposed_dumpdex_Native_dump - (JNIEnv *, jclass, jstring); - - -#ifdef __cplusplus -} -#endif -#endif diff --git a/xposedmodule/src/main/cpp/readme.md b/xposedmodule/src/main/cpp/readme.md deleted file mode 100644 index ff4f8fd..0000000 --- a/xposedmodule/src/main/cpp/readme.md +++ /dev/null @@ -1,10 +0,0 @@ -# ndk说明 - -ndk主要针对android8.0及以上机型使用,通过hook底层的代码,将数据dump出来,目前360加固测试通过,其他正在寻找方案 - - -## 感谢 - -[https://github.com/ele7enxxh/Android-Inline-Hook](https://github.com/ele7enxxh/Android-Inline-Hook) - -[https://github.com/rrrfff/ndk_dlopen](https://github.com/rrrfff/ndk_dlopen) \ No newline at end of file diff --git a/xposedmodule/src/main/cpp/relocate.c b/xposedmodule/src/main/cpp/relocate.c deleted file mode 100755 index 36fd383..0000000 --- a/xposedmodule/src/main/cpp/relocate.c +++ /dev/null @@ -1,612 +0,0 @@ -/* -relocate instruction -author: ele7enxxh -mail: ele7enxxh@qq.com -website: ele7enxxh.com -modified time: 2016-10-17 -created time: 2015-01-17 -*/ - -#include "relocate.h" - -#define ALIGN_PC(pc) (pc & 0xFFFFFFFC) - -enum INSTRUCTION_TYPE { - // B