├── .github └── workflows │ └── android.yml ├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro ├── release │ └── output-metadata.json └── src │ └── main │ ├── AndroidManifest.xml │ ├── aidl │ └── tiiehenry │ │ └── viewcontroller │ │ ├── IGodModeManager.aidl │ │ ├── IObserver.aidl │ │ └── rule │ │ ├── ActRules.aidl │ │ ├── AppRules.aidl │ │ └── ViewRule.aidl │ ├── assets │ └── xposed_init │ ├── java │ ├── com │ │ └── lxj │ │ │ └── androidktx │ │ │ ├── AndroidConfig.java │ │ │ ├── base │ │ │ ├── BaseActivity.kt │ │ │ ├── BaseFragment.kt │ │ │ └── PagerLazyFragment.kt │ │ │ ├── core │ │ │ ├── ActivityExt.kt │ │ │ ├── CommonExt.kt │ │ │ ├── DateTimeExt.kt │ │ │ ├── FragmentExt.kt │ │ │ ├── HashExt.kt │ │ │ ├── LogExt.kt │ │ │ ├── RecyclerViewExt.kt │ │ │ ├── ResourceExt.kt │ │ │ ├── SpanExt.kt │ │ │ ├── StringExt.kt │ │ │ ├── TextViewExt.kt │ │ │ ├── ViewExt.kt │ │ │ └── ViewPagerExt.kt │ │ │ ├── livedata │ │ │ ├── LifecycleHandler.kt │ │ │ ├── NoStickyLiveData.java │ │ │ ├── OnceLiveData.kt │ │ │ └── StateLiveData.kt │ │ │ ├── util │ │ │ ├── EncryptUtils.java │ │ │ ├── HttpsUtils.java │ │ │ └── NetworkUtils.java │ │ │ └── widget │ │ │ └── NoScrollViewPager.kt │ └── tiiehenry │ │ ├── android │ │ ├── fragment │ │ │ ├── adapter │ │ │ │ ├── BaseFragmentAdapter.java │ │ │ │ ├── DynamicFragmentStateAdapter.java │ │ │ │ ├── FragmentNoStatePagerAdapter.java │ │ │ │ └── IFragmentAdapter.java │ │ │ └── fragments │ │ │ │ └── StateFragment.java │ │ ├── text │ │ │ └── SimpleTextWatcher.java │ │ ├── ui │ │ │ └── dialogs │ │ │ │ └── mddialogs │ │ │ │ ├── DialogHelper.kt │ │ │ │ ├── MDDialogs.kt │ │ │ │ ├── base │ │ │ │ ├── ButtonTemp.kt │ │ │ │ ├── MaterialBaseDialogBuilder.kt │ │ │ │ ├── MaterialDialogBaseWrapper.kt │ │ │ │ ├── MaterialDialogInputWrapper.kt │ │ │ │ ├── NegativeButtonTemp.kt │ │ │ │ ├── NeutralButtonTemp.kt │ │ │ │ └── PositiveButtonTemp.kt │ │ │ │ ├── confirm │ │ │ │ ├── MDConfirmDialogBuilder.kt │ │ │ │ └── MDConfirmDialogProvider.kt │ │ │ │ ├── custom │ │ │ │ ├── CustomViewTemp.kt │ │ │ │ ├── MDCustomDialogBuilder.kt │ │ │ │ └── MDCustomDialogProvider.kt │ │ │ │ ├── input │ │ │ │ ├── InputTemp.kt │ │ │ │ ├── MDInputDialogBuilder.kt │ │ │ │ └── MDInputDialogProvider.kt │ │ │ │ ├── list │ │ │ │ ├── MDListDialogBuilder.kt │ │ │ │ ├── MDListDialogProvider.kt │ │ │ │ ├── base │ │ │ │ │ ├── ListTemp.kt │ │ │ │ │ └── MaterialBaseListDialogBuilder.kt │ │ │ │ ├── custom │ │ │ │ │ └── MaterialCustomListDialogBuilder.kt │ │ │ │ ├── multi │ │ │ │ │ ├── MaterialMultiListDialogBuilder.kt │ │ │ │ │ └── MultiListTemp.kt │ │ │ │ ├── regular │ │ │ │ │ ├── MaterialRegularListDialogBuilder.kt │ │ │ │ │ └── RegularListTemp.kt │ │ │ │ └── single │ │ │ │ │ ├── MaterialSingleListDialogBuilder.kt │ │ │ │ │ └── SingleListTemp.kt │ │ │ │ ├── loading │ │ │ │ ├── LoadTemp.kt │ │ │ │ ├── MDLoadingDialogBuilder.kt │ │ │ │ ├── MDLoadingDialogProvider.kt │ │ │ │ └── MaterialLoadingDialogWrapper.kt │ │ │ │ └── progress │ │ │ │ ├── MDProgressDialogBuilder.kt │ │ │ │ └── MDProgressDialogProvider.kt │ │ └── view │ │ │ ├── base │ │ │ ├── adapter │ │ │ │ ├── IAdapter.java │ │ │ │ ├── INotifier.java │ │ │ │ └── wrapped │ │ │ │ │ ├── AllChangedNotifier.java │ │ │ │ │ └── IAllChangedNotifier.java │ │ │ └── holder │ │ │ │ ├── IViewHolder.java │ │ │ │ ├── OnItemClickListener.java │ │ │ │ ├── OnItemLongClickListener.java │ │ │ │ └── OnViewItemClickListener.java │ │ │ ├── loadingview │ │ │ └── LoadingView.kt │ │ │ ├── recyclerview │ │ │ ├── adapter │ │ │ │ ├── AbstractRecyclerAdapter.java │ │ │ │ ├── BaseIdRecyclerAdapter.java │ │ │ │ ├── IRecyclerAdapter.java │ │ │ │ ├── InflateRecyclerAdapter.java │ │ │ │ └── SimpleIdRecyclerAdapter.java │ │ │ └── holder │ │ │ │ ├── IRecyclerViewHolder.java │ │ │ │ └── RecyclerViewHolder.java │ │ │ └── spinner │ │ │ ├── adapter │ │ │ ├── AbstractSpinnerAdapter.java │ │ │ └── ISpinnerAdapter.java │ │ │ └── holder │ │ │ ├── ISpinnerViewHolder.java │ │ │ └── SpinnerViewHolder.java │ │ ├── io │ │ ├── Filej.java │ │ ├── IOExceptionMaker.java │ │ └── Zipl.java │ │ ├── ktx │ │ ├── ViewExt.kt │ │ ├── app │ │ │ └── ActivityExt.kt │ │ ├── content │ │ │ ├── ContextExt.kt │ │ │ └── ThemeExt.kt │ │ ├── io │ │ │ └── FileExt.kt │ │ └── lang │ │ │ ├── CastExt.kt │ │ │ ├── JudgeExt.kt │ │ │ ├── StringExt.kt │ │ │ └── TryExt.kt │ │ └── viewcontroller │ │ ├── CrashHandler.java │ │ ├── GodModeApplication.java │ │ ├── NotificationService.java │ │ ├── QuickSettingsService.java │ │ ├── SettingsActivity.java │ │ ├── backup │ │ ├── RuleExporter.java │ │ └── RuleImporter.java │ │ ├── fragment │ │ ├── GeneralPreferenceFragment.java │ │ ├── ruledetails │ │ │ └── ViewRuleDetailsFragment.java │ │ └── viewrules │ │ │ ├── RuleDetailAdapter.java │ │ │ └── ViewRuleListFragment.java │ │ ├── injection │ │ ├── GodModeInjector.java │ │ ├── bridge │ │ │ ├── GodModeManager.java │ │ │ └── ManagerObserver.java │ │ ├── control │ │ │ ├── ViewCompat.java │ │ │ ├── ViewController.java │ │ │ ├── ViewExtractor.java │ │ │ ├── ViewFinder.java │ │ │ └── ViewHelper.java │ │ ├── hook │ │ │ ├── ActivityLifecycleHook.java │ │ │ ├── DispatchKeyEventHook.java │ │ │ ├── DispatchTouchEventHook.java │ │ │ ├── DisplayPropertiesHook.java │ │ │ ├── EventHandlerHook.java │ │ │ └── SystemPropertiesHook.java │ │ ├── injector │ │ │ ├── InjectorImpl.java │ │ │ ├── InjectorImplAndroid.java │ │ │ ├── InjectorImplApps.java │ │ │ └── InjectorImplManager.java │ │ ├── util │ │ │ ├── FilePermissionUtils.java │ │ │ ├── GmLayoutInflater.java │ │ │ ├── GmResources.java │ │ │ ├── Logger.java │ │ │ ├── PackageManagerUtils.java │ │ │ ├── Property.java │ │ │ └── ViewBitmapUtils.java │ │ └── weiget │ │ │ ├── CancelView.java │ │ │ ├── MaskView.java │ │ │ ├── Particle.java │ │ │ └── ParticleView.java │ │ ├── model │ │ └── SharedViewModel.java │ │ ├── repository │ │ └── RemoteRepository.java │ │ ├── rule │ │ ├── ActRules.java │ │ ├── AppRules.java │ │ └── ViewRule.java │ │ ├── service │ │ ├── GodModeManagerService.java │ │ └── ObserverProxy.java │ │ ├── util │ │ ├── Clipboard.java │ │ ├── CommandUtil.java │ │ ├── DisplayUtils.java │ │ ├── DonateHelper.java │ │ ├── GmGlideModule.java │ │ ├── PermissionHelper.java │ │ ├── Preconditions.java │ │ ├── ShareUtil.java │ │ └── XposedEnvironment.java │ │ └── widget │ │ ├── Snackbar.java │ │ └── preference │ │ └── ImageViewPreference.java │ └── res │ ├── anim │ ├── mddialogs_dialog_enter.xml │ └── mddialogs_dialog_exit.xml │ ├── drawable-anydpi │ ├── ic_donate.xml │ ├── ic_people.xml │ ├── ic_puzzle.xml │ └── ic_setting.xml │ ├── drawable-hdpi │ ├── down.png │ ├── exchange.png │ ├── ic_block.png │ ├── ic_donate.png │ ├── ic_people.png │ ├── ic_puzzle.png │ ├── ic_setting.png │ └── up.png │ ├── drawable-mdpi │ ├── down.png │ ├── exchange.png │ ├── ic_block.png │ ├── ic_donate.png │ ├── ic_people.png │ ├── ic_puzzle.png │ ├── ic_setting.png │ └── up.png │ ├── drawable-xhdpi │ ├── down.png │ ├── exchange.png │ ├── ic_block.png │ ├── ic_donate.png │ ├── ic_people.png │ ├── ic_puzzle.png │ ├── ic_setting.png │ ├── ripple_drawable_20dp.xml │ ├── rounded_bg_bottom_background.xml │ ├── rounded_bg_full.xml │ └── up.png │ ├── drawable-xxhdpi │ ├── down.png │ ├── exchange.png │ ├── ic_block.png │ ├── ic_donate.png │ ├── ic_people.png │ ├── ic_puzzle.png │ ├── ic_setting.png │ └── up.png │ ├── drawable-xxxhdpi │ ├── down.png │ ├── exchange.png │ ├── ic_block.png │ └── up.png │ ├── drawable │ ├── design_snackbar_background.xml │ ├── ic_angel_disable.xml │ ├── ic_angel_normal.xml │ ├── ic_angel_small.xml │ ├── ic_baseline_backup_table_24.xml │ ├── ic_baseline_settings_backup_restore_24.xml │ └── rectange_selector.xml │ ├── layout │ ├── fragment_container_activity.xml │ ├── layout_node_selector.xml │ ├── layout_snackbar.xml │ ├── list_fragment_layout.xml │ ├── mddialogs_loading_layout.xml │ ├── preference_image_preview.xml │ ├── preference_progress.xml │ └── preference_view_pager.xml │ ├── menu │ ├── menu_app_rule.xml │ ├── menu_app_rules.xml │ └── menu_general.xml │ ├── mipmap-nodpi │ ├── qrcode_alipay.jpg │ └── qrcode_wxpay.jpg │ ├── mipmap-xxhdpi │ └── ic_god.png │ ├── mipmap-xxxhdpi │ └── ic_god.png │ ├── values-en │ └── strings.xml │ ├── values-night-v23 │ ├── strings.xml │ └── styles.xml │ ├── values-night │ ├── arrays.xml │ ├── colors.xml │ ├── strings.xml │ └── styles.xml │ ├── values-v23 │ ├── strings.xml │ └── styles.xml │ ├── values │ ├── arrays.xml │ ├── attrs.xml │ ├── colors.xml │ ├── dimens.xml │ ├── ids.xml │ ├── strings.xml │ └── styles.xml │ └── xml │ ├── file_paths.xml │ ├── pref_general.xml │ └── pref_rule_details.xml ├── build.gradle ├── community.json ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── wechat_community.png /.github/workflows/android.yml: -------------------------------------------------------------------------------- 1 | name: Android CI 2 | 3 | on: 4 | push: 5 | branches: [ dev ] 6 | pull_request: 7 | branches: [ dev ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: set up JDK 11 17 | uses: actions/setup-java@v2 18 | with: 19 | java-version: '11' 20 | distribution: 'adopt' 21 | cache: gradle 22 | - name: Install dependencies 23 | run: git submodule update --init --recursive 24 | - name: Grant execute permission for gradlew 25 | run: chmod +x gradlew 26 | - name: Build with Gradle 27 | run: ./gradlew build 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | .DS_Store 5 | /build 6 | /captures 7 | .externalNativeBuild 8 | .idea 9 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libxservicemanager"] 2 | path = libxservicemanager 3 | url = https://github.com/jrsen/XServiceManager.git 4 | [submodule "libhotloader"] 5 | path = libhotloader 6 | url = https://github.com/jrsen/Xposed-HotLoader.git 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Xposed-GodMode 2 | 3 | ## Oh 我的上帝 这是什么鬼东西? 4 | 5 | 借助于```Xposed```你可以在任何app中屏蔽你不喜欢的东西 6 | 7 | 目前功能类似于PC端的AdBlock 8 | 9 | ## 嘿 这看起来真不错 我可以用它干什么? 10 | 11 | - 已支持 12 | - [x] 屏蔽控件比如广告或者巨丑无比的按钮 13 | 14 | - 预计以后支持 15 | - [ ] 支持导出修改后的应用 16 | - [ ] 修改控件属性:大小/位置/文本/图片 17 | 18 | [查看开发计划](https://github.com/jrsen/Xposed-GodMode/projects) 19 | 20 | ## 噢 真糟糕这是目前存在的问题 21 | 22 | - 无法屏蔽悬浮窗中的控件 23 | - 屏蔽列表中的控件可能会导致混乱 24 | 25 | [查看isuse](https://github.com/jrsen/Xposed-GodMode/issues) 26 | 27 | ## 使用的正确姿势 28 | 29 | 30 | 31 | ## 伙计们我想我应该跟你说些什么 32 | 33 | 因工作繁忙(借口)导致项目进展一直很慢未能及时兼容新的Android版本因此出现各种莫名其妙的问题加上之前有很多伙计联系我希望能开源所以在我思考很久之后决定开源独乐不如众乐,各路神仙要什么功能可以提个```issue```我会按实现复杂度列个项目开发计划,闲暇之余我也会来更新更新,希望有能力的伙计们也可以一起维护这个项目。 -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 31 5 | compileOptions { 6 | sourceCompatibility JavaVersion.VERSION_1_8 7 | targetCompatibility JavaVersion.VERSION_1_8 8 | } 9 | defaultConfig { 10 | applicationId "tiiehenry.viewcontroller" 11 | minSdkVersion 21 12 | targetSdkVersion 31 13 | versionCode new Date().format('yyMMdd').toInteger() 14 | versionName new Date().format('yy.MM.dd') 15 | testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' 16 | vectorDrawables.useSupportLibrary = true 17 | } 18 | productFlavors { 19 | } 20 | } 21 | android.applicationVariants.all { variant -> 22 | variant.outputs.each { output -> 23 | def versionName = variant.versionName 24 | def buildType = variant.buildType.name 25 | if (buildType == "release") { 26 | versionName = output.versionNameOverride 27 | } 28 | def fileName = "GodMode+_v${versionName}_${buildType}.apk" 29 | output.outputFileName = fileName 30 | } 31 | } 32 | 33 | dependencies { 34 | implementation fileTree(include: ['*.jar'], dir: 'libs') 35 | implementation project(path: ':libxservicemanager') 36 | // implementation project(path: ':libhotloader') 37 | // implementation 'androidx.constraintlayout:constraintlayout:2.1.3' 38 | androidTestImplementation('androidx.test.espresso:espresso-core:3.3.0', { 39 | exclude group: 'com.android.support', module: 'support-annotations' 40 | }) 41 | implementation 'androidx.appcompat:appcompat:1.4.1' 42 | implementation "androidx.viewpager2:viewpager2:1.0.0" 43 | implementation "androidx.fragment:fragment:1.4.1" 44 | // implementation 'androidx.legacy:legacy-support-v4:1.0.0' 45 | compileOnly 'de.robv.android.xposed:api:82' 46 | compileOnly 'de.robv.android.xposed:api:82:sources' 47 | implementation 'com.google.code.gson:gson:2.8.9' 48 | implementation 'androidx.legacy:legacy-support-v13:1.0.0' 49 | implementation 'androidx.legacy:legacy-preference-v14:1.0.0' 50 | implementation 'com.squareup.retrofit2:retrofit:2.9.0' 51 | implementation 'com.squareup.retrofit2:converter-gson:2.9.0' 52 | implementation 'com.github.bumptech.glide:glide:4.12.0' 53 | annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0' 54 | 55 | 56 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" 57 | implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0' 58 | 59 | implementation 'androidx.core:core-ktx:1.7.0' 60 | 61 | implementation 'com.afollestad.material-dialogs:core:3.3.0' 62 | implementation 'com.afollestad.material-dialogs:input:3.3.0' 63 | } 64 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/software/android-sdk-linux/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | 27 | -keep class tiiehenry.godmode.injection.GodModeInjector{*;} 28 | -------------------------------------------------------------------------------- /app/release/output-metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "artifactType": { 4 | "type": "APK", 5 | "kind": "Directory" 6 | }, 7 | "applicationId": "tiiehenry.viewcontroller", 8 | "variantName": "release", 9 | "elements": [ 10 | { 11 | "type": "SINGLE", 12 | "filters": [], 13 | "attributes": [], 14 | "versionCode": 220206, 15 | "versionName": "22.02.06", 16 | "outputFile": "GodMode+_v22.02.06_release.apk" 17 | } 18 | ], 19 | "elementType": "File" 20 | } -------------------------------------------------------------------------------- /app/src/main/aidl/tiiehenry/viewcontroller/IGodModeManager.aidl: -------------------------------------------------------------------------------- 1 | package tiiehenry.viewcontroller; 2 | 3 | import tiiehenry.viewcontroller.IObserver; 4 | import tiiehenry.viewcontroller.rule.AppRules; 5 | import tiiehenry.viewcontroller.rule.ActRules; 6 | import tiiehenry.viewcontroller.rule.ViewRule; 7 | import android.graphics.Bitmap; 8 | import android.os.ParcelFileDescriptor; 9 | 10 | interface IGodModeManager { 11 | 12 | void setEditMode(boolean enable); 13 | 14 | boolean isInEditMode(); 15 | 16 | void addObserver(String packageName, in IObserver observer); 17 | 18 | void removeObserver(String packageName, in IObserver observer); 19 | 20 | AppRules getAllRules(); 21 | 22 | ActRules getRules(String packageName); 23 | 24 | boolean writeBitmap(String packageName,String fileName,in Bitmap snapshot); 25 | 26 | boolean writeAllRule(String packageName,in ActRules actRules); 27 | 28 | boolean writeRule(String packageName, in ViewRule viewRule, in Bitmap bitmap); 29 | 30 | boolean updateRule(String packageName, in ViewRule viewRule); 31 | 32 | boolean deleteRule(String packageName, in ViewRule viewRule); 33 | 34 | boolean deleteRules(String packageName); 35 | 36 | void setAppEnable(String packageName,boolean enable); 37 | 38 | String[] getDisabledApps(); 39 | 40 | boolean isAppDisabled(String packageName); 41 | 42 | ParcelFileDescriptor openImageFileDescriptor(String filePath); 43 | 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/aidl/tiiehenry/viewcontroller/IObserver.aidl: -------------------------------------------------------------------------------- 1 | package tiiehenry.viewcontroller; 2 | 3 | import tiiehenry.viewcontroller.rule.ActRules; 4 | 5 | interface IObserver { 6 | void onEditModeChanged(boolean enable); 7 | void onAppStatusChanged( boolean enable); 8 | void onViewRuleChanged(String packageName, in ActRules actRules); 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/aidl/tiiehenry/viewcontroller/rule/ActRules.aidl: -------------------------------------------------------------------------------- 1 | // Rulea.aidl 2 | package tiiehenry.viewcontroller.rule; 3 | 4 | parcelable ActRules; -------------------------------------------------------------------------------- /app/src/main/aidl/tiiehenry/viewcontroller/rule/AppRules.aidl: -------------------------------------------------------------------------------- 1 | // Rulea.aidl 2 | package tiiehenry.viewcontroller.rule; 3 | 4 | parcelable AppRules; -------------------------------------------------------------------------------- /app/src/main/aidl/tiiehenry/viewcontroller/rule/ViewRule.aidl: -------------------------------------------------------------------------------- 1 | // Rulea.aidl 2 | package tiiehenry.viewcontroller.rule; 3 | 4 | parcelable ViewRule; 5 | -------------------------------------------------------------------------------- /app/src/main/assets/xposed_init: -------------------------------------------------------------------------------- 1 | tiiehenry.viewcontroller.injection.GodModeInjector -------------------------------------------------------------------------------- /app/src/main/java/com/lxj/androidktx/AndroidConfig.java: -------------------------------------------------------------------------------- 1 | package com.lxj.androidktx; 2 | 3 | import android.accessibilityservice.AccessibilityService; 4 | import android.content.Context; 5 | 6 | public class AndroidConfig { 7 | public static Context context; 8 | public static boolean isDebug = true; 9 | public static String defaultLogTag = "android"; 10 | public static String sharedPrefName = "android"; 11 | 12 | private void init(Context context, 13 | Boolean isDebug, 14 | String defaultLogTag, 15 | String sharedPrefName 16 | ) { 17 | AndroidConfig.context = context; 18 | AndroidConfig.isDebug = isDebug; 19 | AndroidConfig.defaultLogTag = defaultLogTag; 20 | AndroidConfig.sharedPrefName = sharedPrefName; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/src/main/java/com/lxj/androidktx/base/BaseActivity.kt: -------------------------------------------------------------------------------- 1 | package com.lxj.androidktx.base 2 | 3 | import android.os.Bundle 4 | import androidx.appcompat.app.AppCompatActivity 5 | 6 | /** 7 | * Description: 8 | * Create by dance, at 2019/5/16 9 | */ 10 | abstract class BaseActivity: AppCompatActivity(){ 11 | override fun onCreate(savedInstanceState: Bundle?) { 12 | super.onCreate(savedInstanceState) 13 | setContentView(getLayoutId()) 14 | initView() 15 | initData() 16 | } 17 | 18 | protected abstract fun getLayoutId(): Int 19 | protected abstract fun initView() 20 | protected abstract fun initData() 21 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lxj/androidktx/base/BaseFragment.kt: -------------------------------------------------------------------------------- 1 | package com.lxj.androidktx.base 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import androidx.fragment.app.Fragment 8 | 9 | /** 10 | * Description: 11 | * Create by dance, at 2019/5/16 12 | */ 13 | abstract class BaseFragment: Fragment(){ 14 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { 15 | return inflater.inflate(getLayoutId(), container, false) 16 | } 17 | 18 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 19 | super.onViewCreated(view, savedInstanceState) 20 | initView() 21 | initData() 22 | } 23 | 24 | protected abstract fun getLayoutId(): Int 25 | protected abstract fun initView() 26 | protected abstract fun initData() 27 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lxj/androidktx/base/PagerLazyFragment.kt: -------------------------------------------------------------------------------- 1 | package com.lxj.androidktx.base 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import androidx.fragment.app.Fragment 8 | 9 | 10 | /** 11 | * Description: 使用ViewPager中的懒加载Fragment 12 | * Create by dance, at 2019/4/22 13 | */ 14 | abstract class PagerLazyFragment : Fragment() { 15 | protected var cacheView: View? = null 16 | protected var isInit = false 17 | 18 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { 19 | if (cacheView == null) cacheView = inflater.inflate(getLayoutId(), container, false) 20 | return cacheView!! 21 | } 22 | 23 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 24 | super.onViewCreated(view, savedInstanceState) 25 | lazyInit() 26 | } 27 | 28 | private fun lazyInit() { 29 | if (cacheView!=null && userVisibleHint && !isInit ) { 30 | isInit = true 31 | initView() 32 | initData() 33 | } 34 | } 35 | 36 | override fun setUserVisibleHint(isVisibleToUser: Boolean) { 37 | super.setUserVisibleHint(isVisibleToUser) 38 | lazyInit() 39 | } 40 | 41 | //执行初始化,只会执行一次 42 | protected abstract fun getLayoutId(): Int 43 | abstract fun initView() 44 | abstract fun initData() 45 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lxj/androidktx/core/ActivityExt.kt: -------------------------------------------------------------------------------- 1 | package com.lxj.androidktx.core 2 | 3 | import android.app.Activity 4 | import android.content.Context 5 | import android.content.Intent 6 | import android.view.View 7 | import androidx.fragment.app.Fragment 8 | import androidx.fragment.app.FragmentActivity 9 | import androidx.lifecycle.ViewModel 10 | import androidx.lifecycle.ViewModelProviders 11 | import com.lxj.androidktx.livedata.LifecycleHandler 12 | 13 | /** 14 | * Description: Activity相关 15 | * Create by lxj, at 2018/12/7 16 | */ 17 | 18 | inline fun Fragment.startActivity(flag: Int = -1, bundle: Array>? = null) { 19 | activity?.startActivity(flag, bundle) 20 | } 21 | 22 | inline fun Fragment.startActivityForResult(flag: Int = -1, bundle: Array>? = null, requestCode: Int = -1) { 23 | activity?.startActivityForResult(flag, bundle, requestCode) 24 | } 25 | 26 | inline fun Context.startActivity(flag: Int = -1, bundle: Array>? = null, requestCode: Int = -1) { 27 | val intent = Intent(this, T::class.java).apply { 28 | if (flag != -1) { 29 | this.addFlags(flag) 30 | } 31 | if (this !is Activity) { 32 | this.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 33 | } 34 | if (bundle != null) bundle.toBundle()?.let { putExtras(it) } 35 | } 36 | startActivity(intent) 37 | } 38 | 39 | inline fun View.startActivity(flag: Int = -1, bundle: Array>? = null) { 40 | context.startActivity(flag, bundle) 41 | } 42 | 43 | inline fun View.startActivityForResult(flag: Int = -1, bundle: Array>? = null, requestCode: Int = -1) { 44 | (context as Activity).startActivityForResult(flag, bundle, requestCode) 45 | } 46 | 47 | inline fun Activity.startActivityForResult(flag: Int = -1, bundle: Array>? = null, requestCode: Int = -1) { 48 | val intent = Intent(this, T::class.java).apply { 49 | if (flag != -1) { 50 | this.addFlags(flag) 51 | } 52 | if (bundle != null) bundle.toBundle()?.let { putExtras(it) } 53 | } 54 | startActivityForResult(intent, requestCode) 55 | } 56 | 57 | fun FragmentActivity.finishDelay(delay: Long = 1) { 58 | LifecycleHandler(this).postDelayed({ finish() }, delay) 59 | } 60 | 61 | //post, postDelay 62 | fun FragmentActivity.post(action: ()->Unit){ 63 | LifecycleHandler(this).post { action() } 64 | } 65 | 66 | fun FragmentActivity.postDelay(delay:Long = 0, action: ()->Unit){ 67 | LifecycleHandler(this).postDelayed({ action() }, delay) 68 | } 69 | 70 | //view model 71 | fun FragmentActivity.getVM(clazz: Class) = ViewModelProviders.of(this).get(clazz) -------------------------------------------------------------------------------- /app/src/main/java/com/lxj/androidktx/core/DateTimeExt.kt: -------------------------------------------------------------------------------- 1 | package com.lxj.androidktx.core 2 | 3 | import java.text.SimpleDateFormat 4 | import java.util.* 5 | 6 | /** 7 | * Description: 时间日期相关 8 | * Create by lxj, at 2018/12/7 9 | */ 10 | 11 | /** 12 | * 字符串日期格式(比如:2018-4-6)转为毫秒 13 | * @param format 时间的格式,默认是按照yyyy-MM-dd HH:mm:ss来转换,如果您的格式不一样,则需要传入对应的格式 14 | */ 15 | fun String.toDateMills(format: String = "yyyy-MM-dd HH:mm:ss") = SimpleDateFormat(format, Locale.getDefault()).parse(this).time 16 | 17 | 18 | /** 19 | * Long类型时间戳转为字符串的日期格式 20 | * @param format 时间的格式,默认是按照yyyy-MM-dd HH:mm:ss来转换,如果您的格式不一样,则需要传入对应的格式 21 | */ 22 | fun Long.toDateString(format: String = "yyyy-MM-dd HH:mm:ss") = SimpleDateFormat(format, Locale.getDefault()).format(Date(this)) 23 | 24 | fun Int.toDateString(format: String = "yyyy-MM-dd HH:mm:ss") = SimpleDateFormat(format, Locale.getDefault()).format(Date(this.toLong())) -------------------------------------------------------------------------------- /app/src/main/java/com/lxj/androidktx/core/FragmentExt.kt: -------------------------------------------------------------------------------- 1 | package com.lxj.androidktx.core 2 | 3 | import androidx.fragment.app.Fragment 4 | import androidx.fragment.app.FragmentActivity 5 | import androidx.fragment.app.FragmentTransaction 6 | import androidx.lifecycle.ViewModel 7 | import androidx.lifecycle.ViewModelProviders 8 | import com.lxj.androidktx.livedata.LifecycleHandler 9 | 10 | /** 11 | * Description: Fragment相关扩展 12 | * Create by dance, at 2018/12/5 13 | */ 14 | 15 | /** 16 | * fragment批处理,自动commit 17 | */ 18 | fun FragmentActivity.fragmentManager(action: FragmentTransaction.() -> Unit){ 19 | supportFragmentManager.beginTransaction() 20 | .apply { action() } 21 | .commitAllowingStateLoss() 22 | } 23 | 24 | fun FragmentActivity.replace(layoutId: Int, f: Fragment, bundle: Array>? = null){ 25 | if(bundle!=null)f.arguments = bundle.toBundle() 26 | supportFragmentManager.beginTransaction() 27 | .replace(layoutId, f) 28 | .commitAllowingStateLoss() 29 | } 30 | 31 | fun FragmentActivity.add(layoutId: Int, f: Fragment, bundle: Array>? = null){ 32 | if(bundle!=null)f.arguments = bundle.toBundle() 33 | supportFragmentManager.beginTransaction() 34 | .add(layoutId, f) 35 | .commitAllowingStateLoss() 36 | } 37 | 38 | fun FragmentActivity.hide(f: Fragment){ 39 | supportFragmentManager.beginTransaction() 40 | .hide(f) 41 | .commitAllowingStateLoss() 42 | } 43 | 44 | fun FragmentActivity.show(f: Fragment){ 45 | supportFragmentManager.beginTransaction() 46 | .show(f) 47 | .commitAllowingStateLoss() 48 | } 49 | fun FragmentActivity.remove(f: Fragment){ 50 | supportFragmentManager.beginTransaction() 51 | .remove(f) 52 | .commitAllowingStateLoss() 53 | } 54 | 55 | 56 | fun Fragment.replace(layoutId: Int, f: Fragment, bundle: Array>? = null){ 57 | if(bundle!=null)f.arguments = bundle.toBundle() 58 | childFragmentManager.beginTransaction() 59 | .replace(layoutId, f) 60 | .commitAllowingStateLoss() 61 | } 62 | 63 | fun Fragment.add(layoutId: Int, f: Fragment, bundle: Array>? = null){ 64 | if(bundle!=null)f.arguments = bundle.toBundle() 65 | childFragmentManager.beginTransaction() 66 | .add(layoutId, f) 67 | .commitAllowingStateLoss() 68 | } 69 | 70 | fun Fragment.hide( f: Fragment){ 71 | childFragmentManager.beginTransaction() 72 | .hide(f) 73 | .commitAllowingStateLoss() 74 | } 75 | 76 | fun Fragment.show(f: Fragment){ 77 | childFragmentManager.beginTransaction() 78 | .show(f) 79 | .commitAllowingStateLoss() 80 | } 81 | fun Fragment.remove(f: Fragment){ 82 | childFragmentManager.beginTransaction() 83 | .remove(f) 84 | .commitAllowingStateLoss() 85 | } 86 | 87 | //post, postDelay 88 | fun Fragment.post(action: ()->Unit){ 89 | LifecycleHandler(this).post { action() } 90 | } 91 | 92 | fun Fragment.postDelay(delay:Long = 0, action: ()->Unit){ 93 | LifecycleHandler(this).postDelayed({ action() }, delay) 94 | } 95 | 96 | //view model 97 | fun Fragment.getVM(clazz: Class) = ViewModelProviders.of(this).get(clazz) 98 | 99 | fun Fragment.getActivityVM(clazz: Class) = ViewModelProviders.of(activity!!).get(clazz) -------------------------------------------------------------------------------- /app/src/main/java/com/lxj/androidktx/core/HashExt.kt: -------------------------------------------------------------------------------- 1 | package com.lxj.androidktx.core 2 | 3 | import com.lxj.androidktx.util.EncryptUtils 4 | 5 | /** 6 | * Description: 哈希,加密相关 7 | * Create by lxj, at 2018/12/5 8 | */ 9 | fun String.md5() = EncryptUtils.encryptMD5ToString(this) 10 | 11 | fun String.sha1() = EncryptUtils.encryptSHA1ToString(this) 12 | 13 | fun String.sha256() = EncryptUtils.encryptSHA256ToString(this) 14 | 15 | fun String.sha512() = EncryptUtils.encryptSHA512ToString(this) 16 | 17 | /** 18 | * 随机数增强的md5算法 19 | * @param salt 加盐的值 20 | */ 21 | fun String.md5Hmac(salt: String) = EncryptUtils.encryptHmacMD5ToString(this, salt) 22 | 23 | fun String.sha1Hmac(salt: String) = EncryptUtils.encryptHmacSHA1ToString(this, salt) 24 | 25 | fun String.sha256Hmac(salt: String) = EncryptUtils.encryptHmacSHA256ToString(this, salt) 26 | 27 | /** 28 | * DES对称加密 29 | * @param key 长度必须是8位 30 | */ 31 | fun String.encryptDES(key: String) = EncryptUtils.encryptDES(this, key) 32 | 33 | /** 34 | * DES对称解密 35 | * @param key 长度必须是8位 36 | */ 37 | fun String.decryptDES(key: String) = EncryptUtils.decryptDES(this, key) 38 | 39 | /** 40 | * AES对称加密 41 | * @param key 长度必须是16位 42 | */ 43 | fun String.encryptAES(key: String) = EncryptUtils.encryptAES(this, key) 44 | 45 | /** 46 | * AES对称解密 47 | * @param key 长度必须是16位 48 | */ 49 | fun String.decryptAES(key: String) = EncryptUtils.decryptAES(this, key) 50 | 51 | -------------------------------------------------------------------------------- /app/src/main/java/com/lxj/androidktx/core/LogExt.kt: -------------------------------------------------------------------------------- 1 | package com.lxj.androidktx.core 2 | 3 | import android.util.Log 4 | import com.lxj.androidktx.AndroidConfig 5 | 6 | /** 7 | * Description: log相关,日志的开关和默认tag通过AndroidConfig来配置 8 | * Create by lxj, at 2018/12/5 9 | */ 10 | 11 | private enum class LogLevel{ 12 | Verbose, Debug, Info, Warn, Error 13 | } 14 | 15 | @Deprecated(message = "推荐使用logv") 16 | fun String.v(tag: String = AndroidConfig.defaultLogTag){ 17 | intervalLog(LogLevel.Verbose, tag, this ) 18 | } 19 | 20 | fun Any.logv(tag: String, msg: String){ 21 | intervalLog(LogLevel.Verbose, tag, msg ) 22 | } 23 | fun Any.logv(msg: String){ 24 | intervalLog(LogLevel.Verbose, AndroidConfig.defaultLogTag, msg ) 25 | } 26 | 27 | @Deprecated(message = "推荐使用logd") 28 | fun String.d(tag: String = AndroidConfig.defaultLogTag){ 29 | intervalLog(LogLevel.Debug, tag, this ) 30 | } 31 | 32 | fun Any.logd(tag: String, msg: String){ 33 | intervalLog(LogLevel.Debug, tag, msg ) 34 | } 35 | fun Any.logd(msg: String){ 36 | intervalLog(LogLevel.Debug, AndroidConfig.defaultLogTag, msg ) 37 | } 38 | 39 | @Deprecated(message = "推荐使用logi") 40 | fun String.i(tag: String = AndroidConfig.defaultLogTag){ 41 | intervalLog(LogLevel.Info, tag, this ) 42 | } 43 | 44 | fun Any.logi(tag: String, msg: String){ 45 | intervalLog(LogLevel.Info, tag, msg ) 46 | } 47 | fun Any.logi(msg: String){ 48 | intervalLog(LogLevel.Info, AndroidConfig.defaultLogTag, msg ) 49 | } 50 | 51 | @Deprecated(message = "推荐使用logw") 52 | fun String.w(tag: String = AndroidConfig.defaultLogTag){ 53 | intervalLog(LogLevel.Warn, tag, this ) 54 | } 55 | fun Any.logw(tag: String, msg: String){ 56 | intervalLog(LogLevel.Warn, tag, msg ) 57 | } 58 | fun Any.logw(msg: String){ 59 | intervalLog(LogLevel.Warn, AndroidConfig.defaultLogTag, msg ) 60 | } 61 | 62 | @Deprecated(message = "推荐使用loge") 63 | fun String.e(tag: String = AndroidConfig.defaultLogTag){ 64 | intervalLog(LogLevel.Error, tag, this ) 65 | } 66 | 67 | fun Any.loge(tag: String, msg: String){ 68 | intervalLog(LogLevel.Error, tag, msg ) 69 | } 70 | fun Any.loge(msg: String){ 71 | intervalLog(LogLevel.Error, AndroidConfig.defaultLogTag, msg ) 72 | } 73 | fun Any.logea(msg: Any?){ 74 | loge(msg.toString()) 75 | } 76 | 77 | private fun intervalLog(level: LogLevel, tag: String, msg: String){ 78 | if(AndroidConfig.isDebug){ 79 | when (level) { 80 | LogLevel.Verbose -> Log.v(tag, msg) 81 | LogLevel.Debug -> Log.d(tag, msg) 82 | LogLevel.Info -> Log.i(tag, msg) 83 | LogLevel.Warn -> Log.w(tag, msg) 84 | LogLevel.Error -> Log.e(tag, msg) 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /app/src/main/java/com/lxj/androidktx/core/RecyclerViewExt.kt: -------------------------------------------------------------------------------- 1 | package com.lxj.androidktx.core 2 | 3 | import android.graphics.Color 4 | import android.graphics.drawable.GradientDrawable 5 | import android.view.View 6 | import androidx.recyclerview.widget.* 7 | 8 | /** 9 | * Description: RecyclerView扩展 10 | * Create by lxj, at 2018/12/25 11 | */ 12 | 13 | /** 14 | * 设置分割线 15 | * @param color 分割线的颜色,默认是#DEDEDE 16 | * @param size 分割线的大小,默认是1px 17 | * @param isReplace 是否覆盖之前的ItemDecoration,默认是true 18 | * 19 | */ 20 | fun RecyclerView.divider(color: Int = Color.parseColor("#DEDEDE"), size: Int = 1, isReplace: Boolean = true): RecyclerView { 21 | val decoration = DividerItemDecoration(context, orientation) 22 | decoration.setDrawable(GradientDrawable().apply { 23 | setColor(color) 24 | shape = GradientDrawable.RECTANGLE 25 | setSize(size, size) 26 | }) 27 | if(isReplace && itemDecorationCount>0){ 28 | removeItemDecorationAt(0) 29 | } 30 | addItemDecoration(decoration) 31 | return this 32 | } 33 | 34 | 35 | fun RecyclerView.vertical(spanCount: Int = 0, isStaggered: Boolean = false): RecyclerView { 36 | layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) 37 | if (spanCount != 0) { 38 | layoutManager = GridLayoutManager(context, spanCount) 39 | } 40 | if (isStaggered) { 41 | layoutManager = StaggeredGridLayoutManager(spanCount, StaggeredGridLayoutManager.VERTICAL) 42 | } 43 | return this 44 | } 45 | 46 | fun RecyclerView.horizontal(spanCount: Int = 0, isStaggered: Boolean = false): RecyclerView { 47 | layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) 48 | if (spanCount != 0) { 49 | layoutManager = GridLayoutManager(context, spanCount, GridLayoutManager.HORIZONTAL, false) 50 | } 51 | if (isStaggered) { 52 | layoutManager = StaggeredGridLayoutManager(spanCount, StaggeredGridLayoutManager.HORIZONTAL) 53 | } 54 | return this 55 | } 56 | 57 | 58 | inline val RecyclerView.orientation 59 | get() = if (layoutManager == null) -1 else layoutManager.run { 60 | when (this) { 61 | is LinearLayoutManager -> orientation 62 | is GridLayoutManager -> orientation 63 | is StaggeredGridLayoutManager -> orientation 64 | else -> -1 65 | } 66 | } 67 | 68 | 69 | -------------------------------------------------------------------------------- /app/src/main/java/com/lxj/androidktx/core/ResourceExt.kt: -------------------------------------------------------------------------------- 1 | package com.lxj.androidktx.core 2 | 3 | import android.content.Context 4 | import android.view.View 5 | import androidx.fragment.app.Fragment 6 | import androidx.recyclerview.widget.RecyclerView 7 | 8 | /** 9 | * Description: 资源操作相关 10 | * Create by dance, at 2018/12/11 11 | */ 12 | 13 | fun Context.color(id: Int) = resources.getColor(id) 14 | 15 | fun Context.string(id: Int) = resources.getString(id) 16 | 17 | fun Context.stringArray(id: Int) = resources.getStringArray(id) 18 | 19 | fun Context.drawable(id: Int) = resources.getDrawable(id) 20 | 21 | fun Context.dimenPx(id: Int) = resources.getDimensionPixelSize(id) 22 | 23 | 24 | fun View.color(id: Int) = context.color(id) 25 | 26 | fun View.string(id: Int) = context.string(id) 27 | 28 | fun View.stringArray(id: Int) = context.stringArray(id) 29 | 30 | fun View.drawable(id: Int) = context.drawable(id) 31 | 32 | fun View.dimenPx(id: Int) = context.dimenPx(id) 33 | 34 | 35 | fun Fragment.color(id: Int) = context!!.color(id) 36 | 37 | fun Fragment.string(id: Int) = context!!.string(id) 38 | 39 | fun Fragment.stringArray(id: Int) = context!!.stringArray(id) 40 | 41 | fun Fragment.drawable(id: Int) = context!!.drawable(id) 42 | 43 | fun Fragment.dimenPx(id: Int) = context!!.dimenPx(id) 44 | 45 | 46 | fun RecyclerView.ViewHolder.color(id: Int) = itemView.color(id) 47 | 48 | fun RecyclerView.ViewHolder.string(id: Int) = itemView.string(id) 49 | 50 | fun RecyclerView.ViewHolder.stringArray(id: Int) = itemView.stringArray(id) 51 | 52 | fun RecyclerView.ViewHolder.drawable(id: Int) = itemView.drawable(id) 53 | 54 | fun RecyclerView.ViewHolder.dimenPx(id: Int) = itemView.dimenPx(id) -------------------------------------------------------------------------------- /app/src/main/java/com/lxj/androidktx/core/StringExt.kt: -------------------------------------------------------------------------------- 1 | package com.lxj.androidktx.core 2 | 3 | 4 | /** 5 | * Description: 字符串处理相关 6 | * Create by lxj, at 2018/12/7 7 | */ 8 | 9 | /** 10 | * 是否是手机号 11 | */ 12 | fun String.isPhone() = "^[1](([3][0-9])|([4][5-9])|([5][0-3,5-9])|([6][5,6])|([7][0-8])|([8][0-9])|([9][1,8,9]))[0-9]{8}$".toRegex().matches(this) 13 | 14 | /** 15 | * 是否是邮箱地址 16 | */ 17 | fun String.isEmail() = "\\w+@\\w+\\.[a-z]+(\\.[a-z]+)?".toRegex().matches(this) 18 | 19 | /** 20 | * 是否是身份证号码 21 | */ 22 | fun String.isIDCard() = "[1-9]\\d{16}[a-zA-Z0-9]".toRegex().matches(this) 23 | 24 | /** 25 | * 是否是中文字符 26 | */ 27 | fun String.isChinese() = "^[\u4E00-\u9FA5]+$".toRegex().matches(this) 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/lxj/androidktx/core/TextViewExt.kt: -------------------------------------------------------------------------------- 1 | package com.lxj.androidktx.core 2 | 3 | import android.graphics.Rect 4 | import android.graphics.drawable.Drawable 5 | import android.widget.TextView 6 | 7 | /** 8 | * Description: 9 | * Create by lxj, at 2019/2/21 10 | */ 11 | 12 | /** 13 | * 给TextView的drawable设置大小,Drawable如果不传的话会尝试使用TextView自己的Drawable 14 | * @param width Drawable的宽度 15 | * @param height Drawable的高度 16 | */ 17 | fun TextView.sizeDrawable(width: Int, height: Int, leftDrawable: Int = 0, topDrawable: Int = 0, 18 | rightDrawable: Int = 0, bottomDrawable: Int = 0): TextView { 19 | val rect = Rect(0, 0, width, height) 20 | setCompoundDrawables( 21 | findDrawable(leftDrawable, 0, this)?.apply { bounds = rect }, 22 | findDrawable(topDrawable, 1, this)?.apply { bounds = rect }, 23 | findDrawable(rightDrawable, 2, this)?.apply { bounds = rect }, 24 | findDrawable(bottomDrawable, 3, this)?.apply { bounds = rect } 25 | ) 26 | return this 27 | } 28 | 29 | /** 30 | * 优先使用传入的,如果不传则尝试使用TextView自己的 31 | */ 32 | private fun findDrawable(drawableRes: Int, index:Int, textView: TextView): Drawable?{ 33 | if(drawableRes!=0)return textView.drawable(drawableRes) 34 | if(textView.compoundDrawables.isNotEmpty())return textView.compoundDrawables[index] 35 | return null 36 | } 37 | 38 | /** 39 | * 给TextView的drawable设置大小,Drawable如果不传的话会尝试使用TextView自己的Drawable 40 | * @param size 会同时作用于Drawable宽高 41 | */ 42 | fun TextView.sizeDrawable(size: Int, leftDrawable: Int = 0, topDrawable: Int = 0, 43 | rightDrawable: Int = 0, bottomDrawable: Int = 0): TextView { 44 | sizeDrawable(size, size, leftDrawable, topDrawable, rightDrawable, bottomDrawable) 45 | return this 46 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lxj/androidktx/core/ViewPagerExt.kt: -------------------------------------------------------------------------------- 1 | package com.lxj.androidktx.core 2 | 3 | import android.content.Context 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import androidx.fragment.app.Fragment 7 | import androidx.fragment.app.FragmentManager 8 | import androidx.fragment.app.FragmentPagerAdapter 9 | import androidx.viewpager.widget.PagerAdapter 10 | import androidx.viewpager.widget.ViewPager 11 | 12 | /** 13 | * Description: ViewPager相关 14 | * Create by dance, at 2019/5/23 15 | */ 16 | 17 | /** 18 | * 给ViewPager绑定数据 19 | */ 20 | fun ViewPager.bind(count: Int, bindView: (container: ViewGroup, position: Int) -> View): ViewPager { 21 | adapter = object : PagerAdapter() { 22 | override fun isViewFromObject(v: View, p: Any) = v == p 23 | override fun getCount() = count 24 | override fun instantiateItem(container: ViewGroup, position: Int): Any { 25 | val view = bindView(container, position) 26 | container.addView(view) 27 | return view 28 | } 29 | override fun destroyItem(container: ViewGroup, position: Int, obj: Any) { 30 | container.removeView(obj as View) 31 | } 32 | } 33 | return this 34 | } 35 | 36 | /** 37 | * 给ViewPager绑定Fragment 38 | */ 39 | fun ViewPager.bindFragment(fm: FragmentManager, fragments: List, pageTitles: List? = null): ViewPager { 40 | adapter = object : FragmentPagerAdapter(fm) { 41 | override fun getItem(p: Int) = fragments[p] 42 | override fun getCount() = fragments.size 43 | override fun getPageTitle(p: Int) = if(pageTitles==null) null else pageTitles[p] 44 | } 45 | return this 46 | } 47 | 48 | /** 49 | * 让ViewPager展示卡片效果 50 | * @param pageMargin 用来调节卡片之间的距离 51 | * @param padding 用来调节ViewPager的padding 52 | */ 53 | fun ViewPager.asCard(pageMargin: Int = dp2px(30.toFloat()), padding: Int = dp2px(45.toFloat())): ViewPager { 54 | setPageTransformer(false, CardPagerTransformer(context)) 55 | setPageMargin(pageMargin) 56 | clipToPadding = false 57 | setPadding(padding, padding, padding, padding) 58 | return this 59 | } 60 | 61 | class CardPagerTransformer(context: Context) : ViewPager.PageTransformer { 62 | private val maxTranslateOffsetX: Int = context.dp2px(180f) 63 | private var viewPager: ViewPager? = null 64 | 65 | override fun transformPage(view: View, position: Float) { 66 | if (viewPager == null) { 67 | viewPager = view.parent as ViewPager 68 | } 69 | val leftInScreen = view.left - viewPager!!.scrollX 70 | val centerXInViewPager = leftInScreen + view.measuredWidth / 2 71 | val offsetX = centerXInViewPager - viewPager!!.measuredWidth / 2 72 | val offsetRate = offsetX.toFloat() * 0.38f / viewPager!!.measuredWidth 73 | val scaleFactor = 1 - Math.abs(offsetRate) 74 | if (scaleFactor > 0) { 75 | view.scaleX = scaleFactor 76 | view.scaleY = scaleFactor 77 | view.translationX = -maxTranslateOffsetX * offsetRate 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /app/src/main/java/com/lxj/androidktx/livedata/LifecycleHandler.kt: -------------------------------------------------------------------------------- 1 | package com.lxj.androidktx.livedata 2 | 3 | import android.os.Handler 4 | import android.os.Looper 5 | import androidx.lifecycle.Lifecycle 6 | import androidx.lifecycle.LifecycleObserver 7 | import androidx.lifecycle.LifecycleOwner 8 | import androidx.lifecycle.OnLifecycleEvent 9 | 10 | /** 11 | * Description: 自动在UI销毁时移除msg和任务的Handler,不会有内存泄露 12 | * Create by dance, at 2019/5/21 13 | */ 14 | class LifecycleHandler(private val lifecycleOwner: LifecycleOwner?, 15 | looper: Looper = Looper.getMainLooper()) 16 | : Handler(looper), LifecycleObserver { 17 | init { 18 | lifecycleOwner?.lifecycle?.addObserver(this) 19 | } 20 | 21 | @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) 22 | fun onDestroy() { 23 | removeCallbacksAndMessages(null) 24 | lifecycleOwner?.lifecycle?.removeObserver(this) 25 | } 26 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lxj/androidktx/livedata/OnceLiveData.kt: -------------------------------------------------------------------------------- 1 | package com.lxj.androidktx.livedata 2 | 3 | import androidx.lifecycle.LifecycleOwner 4 | import androidx.lifecycle.MutableLiveData 5 | import androidx.lifecycle.Observer 6 | import java.util.concurrent.atomic.AtomicBoolean 7 | 8 | /** 9 | * Description: 只执行一次的LiveData 10 | * Create by lxj, at 2019/3/6 11 | */ 12 | class OnceLiveData : MutableLiveData() { 13 | 14 | private var isRead: AtomicBoolean = AtomicBoolean(false) 15 | 16 | /** 17 | * ensure the event is non-null and can only been seen once 18 | */ 19 | 20 | override fun observe(owner: LifecycleOwner, observer: Observer) { 21 | super.observe(owner, Observer { 22 | if (isRead.compareAndSet(false, true)) { 23 | observer.onChanged(it) 24 | } 25 | }) 26 | } 27 | override fun postValue(value: T) { 28 | isRead.set(false) 29 | super.postValue(value) 30 | } 31 | 32 | override fun setValue(value: T) { 33 | isRead.set(false) 34 | super.setValue(value) 35 | } 36 | } -------------------------------------------------------------------------------- /app/src/main/java/com/lxj/androidktx/livedata/StateLiveData.kt: -------------------------------------------------------------------------------- 1 | package com.lxj.androidktx.livedata 2 | 3 | import androidx.lifecycle.LifecycleOwner 4 | import androidx.lifecycle.MutableLiveData 5 | import androidx.lifecycle.Observer 6 | import kotlinx.coroutines.CoroutineScope 7 | import kotlinx.coroutines.GlobalScope 8 | import kotlinx.coroutines.Job 9 | import kotlinx.coroutines.launch 10 | 11 | /** 12 | * Description: 携带状态的LiveData 13 | * Create by lxj, at 2019/3/6 14 | */ 15 | class StateLiveData : NoStickyLiveData() { 16 | 17 | enum class State { 18 | Idle, Loading, Success, Error, Empty 19 | } 20 | 21 | val state = MutableLiveData() 22 | var errMsg = "" 23 | 24 | init { 25 | clearState() 26 | } 27 | 28 | fun postValueAndSuccess(value: T) { 29 | super.postValue(value) 30 | postSuccess() 31 | } 32 | 33 | fun clearState() { 34 | this.errMsg = "" 35 | state.postValue(State.Idle) 36 | } 37 | 38 | fun postLoading() { 39 | state.postValue(State.Loading) 40 | } 41 | 42 | fun postSuccess() { 43 | state.postValue(State.Success) 44 | } 45 | 46 | fun postError() { 47 | state.postValue(State.Error) 48 | } 49 | 50 | fun postEmpty() { 51 | state.postValue(State.Empty) 52 | } 53 | 54 | fun changeState(s: State) { 55 | state.postValue(s) 56 | } 57 | 58 | 59 | /** 60 | * 智能post值,能根据值进行智能的设置自己的状态,无需手工编写代码 61 | * @param dataValue 目标值,根据目标值去设置对应的state 62 | */ 63 | fun smartPost(dataValue: T?){ 64 | if(dataValue==null){ 65 | postError() 66 | }else if(dataValue is Collection<*> && dataValue.isEmpty()){ 67 | postEmpty() 68 | }else{ 69 | postValueAndSuccess(dataValue) 70 | } 71 | } 72 | 73 | /** 74 | * 强大而实用的封装,启动协程执行逻辑(比如网络请求),并对逻辑结果进行智能post。示例如下: 75 | * launchAndSmartPost { 76 | * "https://iandroid.xyz/api".http().get().await() 77 | * } 78 | */ 79 | fun launchAndSmartPost(block: suspend CoroutineScope.() -> T?): Job { 80 | postLoading() 81 | return GlobalScope.launch { smartPost(block()) } 82 | } 83 | 84 | } 85 | 86 | -------------------------------------------------------------------------------- /app/src/main/java/com/lxj/androidktx/widget/NoScrollViewPager.kt: -------------------------------------------------------------------------------- 1 | package com.lxj.androidktx.widget 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.view.MotionEvent 6 | import androidx.viewpager.widget.ViewPager 7 | 8 | class NoScrollViewPager @JvmOverloads constructor( 9 | context: Context, 10 | attributeSet: AttributeSet? = null 11 | ) : ViewPager(context, attributeSet) { 12 | 13 | override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean { 14 | return false 15 | } 16 | 17 | override fun onTouchEvent(ev: MotionEvent?): Boolean { 18 | return false 19 | } 20 | } -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/fragment/adapter/DynamicFragmentStateAdapter.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.fragment.adapter; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.fragment.app.Fragment; 5 | import androidx.fragment.app.FragmentManager; 6 | 7 | import java.util.ArrayList; 8 | import java.util.Arrays; 9 | import java.util.List; 10 | 11 | /** 12 | * 动态删改,不重建 13 | * 14 | * @param 15 | */ 16 | 17 | public abstract class DynamicFragmentStateAdapter extends FragmentNoStatePagerAdapter implements IFragmentAdapter { 19 | 20 | private List dataList = new ArrayList<>(); 21 | 22 | 23 | public DynamicFragmentStateAdapter(@NonNull FragmentManager fm) { 24 | super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT); 25 | } 26 | 27 | @Override 28 | public List getDataList() { 29 | return dataList; 30 | } 31 | 32 | 33 | public DynamicFragmentStateAdapter(@NonNull FragmentManager fm, @NonNull List items) { 34 | super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT); 35 | dataList.addAll(items); 36 | } 37 | 38 | public DynamicFragmentStateAdapter(@NonNull FragmentManager fm, @NonNull DATATYPE[] items) { 39 | super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT); 40 | dataList.addAll(Arrays.asList(items)); 41 | } 42 | 43 | public boolean contains(Fragment item) { 44 | for (int i = 0; i < dataList.size(); i++) { 45 | if (getData(i) == item) 46 | return true; 47 | } 48 | return false; 49 | } 50 | 51 | public int getPosition(@NonNull Fragment item) { 52 | for (int i = 0; i < dataList.size(); i++) { 53 | if (getData(i) == item) 54 | return i; 55 | } 56 | return -1; 57 | } 58 | 59 | @Override 60 | public int getCount() { 61 | return getDataCount(); 62 | } 63 | 64 | @NonNull 65 | abstract public Fragment getItem(int position); 66 | 67 | @NonNull 68 | abstract public CharSequence getPageTitle(int position); 69 | 70 | @Override 71 | public int getItemPosition(@NonNull Object object) { 72 | if (dataList != null && dataList.size() == 0) { 73 | return POSITION_NONE; 74 | } 75 | return POSITION_NONE; 76 | } 77 | 78 | 79 | // @Override 80 | // public boolean isViewFromObject(@NonNull View view, @NonNull Object object) { 81 | // return view==object; 82 | // } 83 | } 84 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/fragment/adapter/IFragmentAdapter.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.fragment.adapter; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import tiiehenry.android.view.base.adapter.IAdapter; 6 | import tiiehenry.android.view.base.adapter.INotifier; 7 | import tiiehenry.android.view.base.adapter.wrapped.IAllChangedNotifier; 8 | 9 | public interface IFragmentAdapter 11 | extends IAdapter, IAllChangedNotifier { 12 | 13 | @NonNull 14 | @Override 15 | default INotifier getNotifier() { 16 | return getInstance(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/fragment/fragments/StateFragment.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.fragment.fragments; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.fragment.app.Fragment; 5 | import androidx.lifecycle.Lifecycle; 6 | import androidx.lifecycle.LifecycleObserver; 7 | import androidx.lifecycle.LifecycleOwner; 8 | 9 | public class StateFragment extends Fragment implements LifecycleOwner { 10 | 11 | @Override 12 | public Lifecycle getLifecycle() { 13 | return new Lifecycle() { 14 | @Override 15 | public void addObserver(@NonNull LifecycleObserver observer) { 16 | 17 | } 18 | 19 | @Override 20 | public void removeObserver(@NonNull LifecycleObserver observer) { 21 | 22 | } 23 | 24 | @NonNull 25 | @Override 26 | public State getCurrentState() { 27 | return null; 28 | } 29 | }; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/text/SimpleTextWatcher.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.text; 2 | 3 | import android.text.Editable; 4 | import android.text.TextWatcher; 5 | 6 | public class SimpleTextWatcher implements TextWatcher { 7 | public BeforeWatcher beforeWatcher; 8 | public AfterWatcher afterWatcher; 9 | public OnWatcher onWatcher; 10 | 11 | public static SimpleTextWatcher newBeforeWatcher(BeforeWatcher beforeWatcher) { 12 | return new SimpleTextWatcher(beforeWatcher); 13 | } 14 | 15 | public static SimpleTextWatcher newOnWatcher(OnWatcher onWatcher) { 16 | return new SimpleTextWatcher(onWatcher); 17 | } 18 | 19 | public static SimpleTextWatcher newAfterWatcher(AfterWatcher afterWatcher) { 20 | return new SimpleTextWatcher(afterWatcher); 21 | } 22 | 23 | public SimpleTextWatcher(BeforeWatcher beforeWatcher) { 24 | this.beforeWatcher = beforeWatcher; 25 | } 26 | 27 | public SimpleTextWatcher(OnWatcher onWatcher) { 28 | this.onWatcher = onWatcher; 29 | } 30 | 31 | public SimpleTextWatcher(AfterWatcher afterWatcher) { 32 | this.afterWatcher = afterWatcher; 33 | } 34 | 35 | @Override 36 | public void beforeTextChanged(CharSequence s, int start, int count, int after) { 37 | if (beforeWatcher != null) 38 | beforeWatcher.beforeTextChanged(s, start, count, after); 39 | } 40 | 41 | @Override 42 | public void onTextChanged(CharSequence s, int start, int before, int count) { 43 | if (onWatcher != null) 44 | onWatcher.onTextChanged(s, start, before, count); 45 | } 46 | 47 | @Override 48 | public void afterTextChanged(Editable s) { 49 | if (afterWatcher != null) 50 | afterWatcher.afterTextChanged(s.toString()); 51 | } 52 | 53 | public interface BeforeWatcher { 54 | void beforeTextChanged(CharSequence s, int start, int count, int after); 55 | } 56 | 57 | public interface OnWatcher { 58 | void onTextChanged(CharSequence s, int start, int before, int count); 59 | } 60 | 61 | public interface AfterWatcher { 62 | void afterTextChanged(String s); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/ui/dialogs/mddialogs/DialogHelper.kt: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.ui.dialogs.mddialogs 2 | 3 | import android.os.Handler 4 | import android.os.Looper 5 | import android.text.Editable 6 | import android.text.InputType 7 | import android.text.TextWatcher 8 | import com.afollestad.materialdialogs.MaterialDialog 9 | import tiiehenry.android.ui.dialogs.api.strategy.input.IInputDialog 10 | import tiiehenry.android.ui.dialogs.api.strategy.input.IInputDialogBuilder 11 | import java.io.File 12 | 13 | 14 | fun MaterialDialog.applyTheme(): MaterialDialog { 15 | // titleColorAttr(R.attr.main_content_foreground_color_hint) 16 | // contentColorAttr(R.attr.main_content_foreground_color) 17 | 18 | /*backgroundColorAttr(R.attr.main_content_background_color) 19 | */ 20 | // itemsColorAttr(R.attr.main_content_foreground_color) 21 | // 22 | // positiveColorAttr(R.attr.main_content_foreground_color) 23 | // negativeColorAttr(R.attr.main_content_foreground_color) 24 | // neutralColorAttr(R.attr.main_content_foreground_color) 25 | // buttonRippleColorAttr(R.attr.colorAccent) 26 | // btnSelector(R.drawable.main_btn_selector) 27 | return this 28 | } 29 | 30 | 31 | fun IInputDialogBuilder.inputTypeFile(): IInputDialogBuilder { 32 | inputType(InputType.TYPE_TEXT_VARIATION_URI) 33 | return this 34 | } 35 | 36 | fun IInputDialogBuilder.inputRangeFile(): IInputDialogBuilder { 37 | inputRange(1, 255) 38 | return this 39 | } 40 | 41 | fun Any.runOnUIThread(action: ()->Unit){ 42 | Handler(Looper.getMainLooper()).post { action() } 43 | } 44 | 45 | fun MaterialDialog.showInAnim(): MaterialDialog { 46 | context.runOnUIThread { 47 | window?.setWindowAnimations(R.style.MDDialogs_Animation_MaterialDialog) 48 | show() 49 | } 50 | 51 | // show() 52 | return this 53 | } 54 | 55 | fun isValidFileName(fileName: CharSequence): Boolean { 56 | if (fileName.length > 255) 57 | return false 58 | if (fileName.isEmpty()) 59 | return false 60 | // return fileName.matches("[^\\s\\\\/:*?\"<>|](\\x20|[^\\s\\\\/:*?\"<>|])*[^\\s\\\\/:*?\"<>|.]$".toRegex()) 61 | return fileName.matches("[^\\s\\\\/:*?\"<>|]?(\\x20|[^\\s\\\\/:*?\"<>|])*[^\\s\\\\/:*?\"<>|.]?$".toRegex()) 62 | } 63 | 64 | fun getNameError(parent: File?, name: String): String? { 65 | if (!isValidFileName(name)) { 66 | return "非法文件名!" 67 | } 68 | if (parent == null) { 69 | return null 70 | } 71 | val newFile = File(parent, name) 72 | if (newFile.exists()) { 73 | return "文件已存在!" 74 | } 75 | return null 76 | } 77 | 78 | fun IInputDialog.addFileNameChecker(parent: File): IInputDialog { 79 | inputField.apply { 80 | addTextChangedListener(object :TextWatcher{ 81 | override fun afterTextChanged(s: Editable) { 82 | getNameError(parent, s.toString())?.let { 83 | error = it 84 | } 85 | } 86 | 87 | override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { 88 | } 89 | 90 | override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { 91 | } 92 | }) 93 | } 94 | return this 95 | } 96 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/ui/dialogs/mddialogs/MDDialogs.kt: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.ui.dialogs.mddialogs 2 | 3 | import android.app.Activity 4 | import tiiehenry.android.ui.dialogs.api.strategy.Dialogs 5 | import tiiehenry.android.ui.dialogs.mddialogs.confirm.MDConfirmDialogProvider 6 | import tiiehenry.android.ui.dialogs.mddialogs.custom.MDCustomDialogProvider 7 | import tiiehenry.android.ui.dialogs.mddialogs.input.MDInputDialogProvider 8 | import tiiehenry.android.ui.dialogs.mddialogs.list.MDListDialogProvider 9 | import tiiehenry.android.ui.dialogs.mddialogs.loading.MDLoadingDialogProvider 10 | import tiiehenry.android.ui.dialogs.mddialogs.progress.MDProgressDialogProvider 11 | 12 | class MDDialogs(val activity: Activity) : Dialogs() { 13 | 14 | init { 15 | confirmDialogProvider = MDConfirmDialogProvider(activity) 16 | inputDialogProvider = MDInputDialogProvider(activity) 17 | loadingDialogProvider = MDLoadingDialogProvider(activity) 18 | listDialogProvider = MDListDialogProvider(activity) 19 | customDialogProvider = MDCustomDialogProvider(activity) 20 | progressDialogProvider = MDProgressDialogProvider(activity) 21 | } 22 | 23 | fun initGlobal() { 24 | Dialogs.newInstance() 25 | Dialogs.setConfirmDialogProvider(MDConfirmDialogProvider(activity)) 26 | Dialogs.setInputDialogProvider(MDInputDialogProvider(activity)) 27 | Dialogs.setLoadingDialogProvider(MDLoadingDialogProvider(activity)) 28 | Dialogs.setListDialogProvider(MDListDialogProvider(activity)) 29 | Dialogs.setCustomDialogProvider(MDCustomDialogProvider(activity)) 30 | Dialogs.setProgressDialogProvider(MDProgressDialogProvider(activity)) 31 | } 32 | } -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/ui/dialogs/mddialogs/base/ButtonTemp.kt: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.ui.dialogs.mddialogs.base 2 | 3 | import com.afollestad.materialdialogs.MaterialDialog 4 | import tiiehenry.android.ui.dialogs.api.IDialog 5 | import tiiehenry.android.ui.dialogs.api.callback.button.ButtonCallback 6 | 7 | abstract class ButtonTemp { 8 | var res: Int? = null 9 | var text: CharSequence? = null 10 | var click: ButtonCallback? = null 11 | 12 | fun exists(): Boolean { 13 | return res != null || text != null 14 | } 15 | 16 | abstract fun apply(builder: MaterialDialog, dialog: IDialog) 17 | 18 | } -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/ui/dialogs/mddialogs/base/MaterialDialogBaseWrapper.kt: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.ui.dialogs.mddialogs.base 2 | 3 | import com.afollestad.materialdialogs.MaterialDialog 4 | import tiiehenry.android.ui.dialogs.api.IDialog 5 | import tiiehenry.android.ui.dialogs.mddialogs.runOnUIThread 6 | import tiiehenry.android.ui.dialogs.mddialogs.showInAnim 7 | 8 | open class MaterialDialogBaseWrapper(val dialog: MaterialDialog) : IDialog { 9 | override fun dismiss() { 10 | runOnUIThread { 11 | dialog.hide() 12 | } 13 | } 14 | 15 | override fun show() { 16 | dialog.showInAnim() 17 | } 18 | 19 | } -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/ui/dialogs/mddialogs/base/MaterialDialogInputWrapper.kt: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.ui.dialogs.mddialogs.base 2 | 3 | import android.widget.EditText 4 | import com.afollestad.materialdialogs.MaterialDialog 5 | import com.afollestad.materialdialogs.input.getInputField 6 | import tiiehenry.android.ui.dialogs.api.strategy.input.IInputDialog 7 | 8 | class MaterialDialogInputWrapper(dialog: MaterialDialog) : MaterialDialogBaseWrapper(dialog), IInputDialog { 9 | override fun setInputError(error: String?) { 10 | inputField.error = error 11 | } 12 | 13 | override fun setInputError(error: Int) { 14 | setInputError(dialog.context.getString(error)) 15 | } 16 | 17 | override fun getInputField(): EditText { 18 | return dialog.getInputField() 19 | } 20 | } -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/ui/dialogs/mddialogs/base/NegativeButtonTemp.kt: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.ui.dialogs.mddialogs.base 2 | 3 | import com.afollestad.materialdialogs.MaterialDialog 4 | import tiiehenry.android.ui.dialogs.api.IDialog 5 | 6 | class NegativeButtonTemp : ButtonTemp() { 7 | override fun apply(builder: MaterialDialog, dialog: IDialog) { 8 | if (exists()) 9 | builder.negativeButton(res, text) { click?.onClick(dialog) } 10 | } 11 | } -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/ui/dialogs/mddialogs/base/NeutralButtonTemp.kt: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.ui.dialogs.mddialogs.base 2 | 3 | import com.afollestad.materialdialogs.MaterialDialog 4 | import tiiehenry.android.ui.dialogs.api.IDialog 5 | 6 | class NeutralButtonTemp : ButtonTemp() { 7 | override fun apply(builder: MaterialDialog, dialog: IDialog) { 8 | if (exists()) 9 | builder.neutralButton(res, text) { click?.onClick(dialog) } 10 | } 11 | } -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/ui/dialogs/mddialogs/base/PositiveButtonTemp.kt: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.ui.dialogs.mddialogs.base 2 | 3 | import com.afollestad.materialdialogs.MaterialDialog 4 | import tiiehenry.android.ui.dialogs.api.IDialog 5 | 6 | class PositiveButtonTemp : ButtonTemp() { 7 | override fun apply(builder: MaterialDialog, dialog: IDialog) { 8 | if (exists()) 9 | builder.positiveButton(res, text) { click?.onClick(dialog) } 10 | } 11 | } -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/ui/dialogs/mddialogs/confirm/MDConfirmDialogBuilder.kt: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.ui.dialogs.mddialogs.confirm 2 | 3 | import android.content.Context 4 | import com.afollestad.materialdialogs.MaterialDialog 5 | import tiiehenry.android.ui.dialogs.mddialogs.base.MaterialBaseDialogBuilder 6 | import tiiehenry.android.ui.dialogs.mddialogs.base.NegativeButtonTemp 7 | import tiiehenry.android.ui.dialogs.mddialogs.base.NeutralButtonTemp 8 | import tiiehenry.android.ui.dialogs.mddialogs.base.PositiveButtonTemp 9 | import tiiehenry.android.ui.dialogs.api.IDialog 10 | import tiiehenry.android.ui.dialogs.api.strategy.confirm.IConfirmDialogBuilder 11 | 12 | class MDConfirmDialogBuilder(val context: Context) : MaterialBaseDialogBuilder, IConfirmDialogBuilder { 13 | override val builder: MaterialDialog = MaterialDialog(context) 14 | 15 | override val positiveTemp: PositiveButtonTemp = PositiveButtonTemp() 16 | override val negativeTemp: NegativeButtonTemp = NegativeButtonTemp() 17 | override val neutralTemp: NeutralButtonTemp = NeutralButtonTemp() 18 | 19 | override lateinit var dialog: IDialog 20 | 21 | override fun content(contentRes: Int): IConfirmDialogBuilder { 22 | builder.message(contentRes) 23 | return builder() 24 | } 25 | // 26 | // override fun content(contentRes: Int, html: Boolean): IConfirmDialogBuilder { 27 | // builder.message(contentRes, html) 28 | // return builder() 29 | // } 30 | 31 | override fun content(content: CharSequence): IConfirmDialogBuilder { 32 | builder.message(text = content) 33 | return builder() 34 | } 35 | 36 | // override fun content(contentRes: Int, vararg formatArgs: Any?): IConfirmDialogBuilder { 37 | // builder.content(contentRes, formatArgs) 38 | // return builder() 39 | // } 40 | 41 | override fun builder(): IConfirmDialogBuilder { 42 | return this 43 | } 44 | 45 | } -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/ui/dialogs/mddialogs/confirm/MDConfirmDialogProvider.kt: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.ui.dialogs.mddialogs.confirm 2 | 3 | import android.content.Context 4 | import tiiehenry.android.ui.dialogs.api.strategy.confirm.IConfirmDialogBuilder 5 | import tiiehenry.android.ui.dialogs.api.strategy.confirm.IConfirmDialogProvider 6 | 7 | class MDConfirmDialogProvider(val context: Context) : IConfirmDialogProvider { 8 | override fun builder(): IConfirmDialogBuilder { 9 | return MDConfirmDialogBuilder(context) 10 | } 11 | } -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/ui/dialogs/mddialogs/custom/CustomViewTemp.kt: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.ui.dialogs.mddialogs.custom 2 | 3 | import android.view.View 4 | import com.afollestad.materialdialogs.MaterialDialog 5 | import com.afollestad.materialdialogs.customview.customView 6 | import tiiehenry.android.ui.dialogs.api.IDialog 7 | 8 | class CustomViewTemp { 9 | var scrollable: Boolean = false 10 | var dialogWrapContent: Boolean = true 11 | var view: View? = null 12 | fun apply(builder: MaterialDialog, dialog: IDialog, margin: Boolean) { 13 | builder.customView(view = view, scrollable = dialogWrapContent, dialogWrapContent = dialogWrapContent, horizontalPadding = margin) 14 | } 15 | } -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/ui/dialogs/mddialogs/custom/MDCustomDialogBuilder.kt: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.ui.dialogs.mddialogs.custom 2 | 3 | import android.content.Context 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import com.afollestad.materialdialogs.MaterialDialog 7 | import tiiehenry.android.ui.dialogs.mddialogs.base.MaterialBaseDialogBuilder 8 | import tiiehenry.android.ui.dialogs.mddialogs.base.NegativeButtonTemp 9 | import tiiehenry.android.ui.dialogs.mddialogs.base.NeutralButtonTemp 10 | import tiiehenry.android.ui.dialogs.mddialogs.base.PositiveButtonTemp 11 | import tiiehenry.android.ui.dialogs.api.IDialog 12 | import tiiehenry.android.ui.dialogs.api.callback.OnViewCreatedCallback 13 | import tiiehenry.android.ui.dialogs.api.strategy.custom.ICustomDialogBuilder 14 | 15 | class MDCustomDialogBuilder(val context: Context) : MaterialBaseDialogBuilder, ICustomDialogBuilder { 16 | override val builder: MaterialDialog = MaterialDialog(context) 17 | 18 | override val positiveTemp: PositiveButtonTemp = PositiveButtonTemp() 19 | override val negativeTemp: NegativeButtonTemp = NegativeButtonTemp() 20 | override val neutralTemp: NeutralButtonTemp = NeutralButtonTemp() 21 | private val customViewTemp: CustomViewTemp = CustomViewTemp() 22 | 23 | override lateinit var dialog: IDialog 24 | 25 | override fun customView(layoutRes: Int, dialogWrapContent: Boolean, onViewCreatedCallback: OnViewCreatedCallback?): ICustomDialogBuilder { 26 | val layout = LayoutInflater.from(context).inflate(layoutRes, null) 27 | onViewCreatedCallback?.onViewCreated(layout) 28 | return customView(layout, dialogWrapContent) 29 | } 30 | 31 | override fun customView(view: View, dialogWrapContent: Boolean): ICustomDialogBuilder { 32 | // val layoutContainer = LinearLayout(context) 33 | // layoutContainer.setPadding(context.dp2px(16f),0,context.dp2px(16f),0) 34 | // layoutContainer.addView(view, -1, -1) 35 | customViewTemp.view = view 36 | customViewTemp.scrollable = dialogWrapContent 37 | customViewTemp.dialogWrapContent = dialogWrapContent 38 | return builder() 39 | } 40 | 41 | override fun builder(): ICustomDialogBuilder { 42 | return this 43 | } 44 | 45 | override fun build(): IDialog { 46 | val dialog = super.build() 47 | 48 | val margin = positiveTemp.exists() || negativeTemp.exists() || neutralTemp.exists() 49 | // if (positiveTemp.exists()||negativeTemp.exists()||neutralTemp.exists()) { 50 | // val marginHorizontal = context.dp2px(24f) 51 | // customViewTemp.view?.layoutParams = FrameLayout.LayoutParams(-1, -2) 52 | // customViewTemp.view?.margin(leftMargin = marginHorizontal, rightMargin = marginHorizontal) 53 | // } 54 | customViewTemp.apply(builder, dialog, margin) 55 | return dialog 56 | } 57 | } -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/ui/dialogs/mddialogs/custom/MDCustomDialogProvider.kt: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.ui.dialogs.mddialogs.custom 2 | 3 | import android.content.Context 4 | import tiiehenry.android.ui.dialogs.api.strategy.custom.ICustomDialogBuilder 5 | import tiiehenry.android.ui.dialogs.api.strategy.custom.ICustomDialogProvider 6 | 7 | 8 | class MDCustomDialogProvider(val context: Context) : ICustomDialogProvider { 9 | override fun builder(): ICustomDialogBuilder { 10 | return MDCustomDialogBuilder(context) 11 | } 12 | } -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/ui/dialogs/mddialogs/input/InputTemp.kt: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.ui.dialogs.mddialogs.input 2 | 3 | import android.text.InputType 4 | import tiiehenry.android.ui.dialogs.api.callback.InputCallback 5 | import tiiehenry.android.ui.dialogs.api.callback.button.ButtonCallback 6 | 7 | class InputTemp { 8 | var hint: CharSequence? = null 9 | var preFill: CharSequence? = null 10 | var allowEmptyInput: Boolean? = null 11 | var alwaysCallInputCallback: Boolean = false 12 | var inputType: Int = InputType.TYPE_CLASS_TEXT 13 | var inputRange: Pair? = null 14 | var callback: InputCallback? = null 15 | 16 | var res: Int? = null 17 | var text: CharSequence? = null 18 | var click: ButtonCallback? = null 19 | 20 | 21 | } -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/ui/dialogs/mddialogs/input/MDInputDialogProvider.kt: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.ui.dialogs.mddialogs.input 2 | 3 | import android.content.Context 4 | import tiiehenry.android.ui.dialogs.api.strategy.input.IInputDialogBuilder 5 | import tiiehenry.android.ui.dialogs.api.strategy.input.IInputDialogProvider 6 | 7 | class MDInputDialogProvider(val context: Context) : IInputDialogProvider { 8 | override fun builder(): IInputDialogBuilder { 9 | return MDInputDialogBuilder(context) 10 | } 11 | } -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/ui/dialogs/mddialogs/list/MDListDialogBuilder.kt: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.ui.dialogs.mddialogs.list 2 | 3 | import android.content.Context 4 | import tiiehenry.android.ui.dialogs.mddialogs.base.NegativeButtonTemp 5 | import tiiehenry.android.ui.dialogs.mddialogs.base.NeutralButtonTemp 6 | import tiiehenry.android.ui.dialogs.mddialogs.base.PositiveButtonTemp 7 | import tiiehenry.android.ui.dialogs.mddialogs.list.custom.MaterialCustomListDialogBuilder 8 | import tiiehenry.android.ui.dialogs.mddialogs.list.multi.MaterialMultiListDialogBuilder 9 | import tiiehenry.android.ui.dialogs.mddialogs.list.regular.MaterialRegularListDialogBuilder 10 | import tiiehenry.android.ui.dialogs.mddialogs.list.single.MaterialSingleListDialogBuilder 11 | import tiiehenry.android.ui.dialogs.api.strategy.list.IListDialogBuilder 12 | import tiiehenry.android.ui.dialogs.api.strategy.list.custom.ICustomListDialogBuilder 13 | import tiiehenry.android.ui.dialogs.api.strategy.list.multi.IMultiListDialogBuilder 14 | import tiiehenry.android.ui.dialogs.api.strategy.list.regular.IRegularListDialogBuilder 15 | import tiiehenry.android.ui.dialogs.api.strategy.list.single.ISingleListDialogBuilder 16 | 17 | class MDListDialogBuilder(context: Context) : MaterialRegularListDialogBuilder(context), IListDialogBuilder { 18 | 19 | override val positiveTemp: PositiveButtonTemp = PositiveButtonTemp() 20 | override val negativeTemp: NegativeButtonTemp = NegativeButtonTemp() 21 | override val neutralTemp: NeutralButtonTemp = NeutralButtonTemp() 22 | 23 | override fun typeRegular(): IRegularListDialogBuilder { 24 | return this 25 | 26 | } 27 | 28 | override fun typeMultiChoice(): IMultiListDialogBuilder { 29 | return MaterialMultiListDialogBuilder(context) 30 | } 31 | 32 | override fun typeSingleChoice(): ISingleListDialogBuilder { 33 | return MaterialSingleListDialogBuilder(context) 34 | } 35 | 36 | override fun typeCustom(): ICustomListDialogBuilder { 37 | return MaterialCustomListDialogBuilder(context) 38 | } 39 | } -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/ui/dialogs/mddialogs/list/MDListDialogProvider.kt: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.ui.dialogs.mddialogs.list 2 | 3 | import android.content.Context 4 | import tiiehenry.android.ui.dialogs.api.strategy.list.IListDialogBuilder 5 | import tiiehenry.android.ui.dialogs.api.strategy.list.IListDialogProvider 6 | 7 | class MDListDialogProvider(val context: Context) : IListDialogProvider { 8 | override fun builder(): IListDialogBuilder { 9 | return MDListDialogBuilder(context) 10 | } 11 | } -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/ui/dialogs/mddialogs/list/base/ListTemp.kt: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.ui.dialogs.mddialogs.list.base 2 | 3 | import com.afollestad.materialdialogs.MaterialDialog 4 | import tiiehenry.android.ui.dialogs.api.IDialog 5 | 6 | abstract class ListTemp { 7 | 8 | var itemsRes: Int? = null 9 | var itemList: List? = null 10 | var disabledIndices: IntArray? = null 11 | var itemsIdsRes: Int? = null 12 | var itemsIds: IntArray? = null 13 | 14 | abstract fun apply(builder: MaterialDialog, idialog: IDialog) 15 | 16 | } -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/ui/dialogs/mddialogs/list/base/MaterialBaseListDialogBuilder.kt: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.ui.dialogs.mddialogs.list.base 2 | 3 | import tiiehenry.android.ui.dialogs.mddialogs.base.MaterialBaseDialogBuilder 4 | import tiiehenry.android.ui.dialogs.api.IDialog 5 | import tiiehenry.android.ui.dialogs.api.base.content.IDialogBaseItems 6 | 7 | interface MaterialBaseListDialogBuilder : MaterialBaseDialogBuilder, IDialogBaseItems { 8 | val listTemp: ListTemp 9 | 10 | override fun itemsIds(idsArray: IntArray): T { 11 | listTemp.itemsIds = idsArray 12 | return builder() 13 | } 14 | 15 | override fun itemsIds(idsArrayRes: Int): T { 16 | listTemp.itemsIdsRes = idsArrayRes 17 | return builder() 18 | } 19 | //md_item_selector 20 | // override fun listSelector(selectorRes: Int): T { 21 | // builder.getItemSelector() 22 | // builder.listSelector(selectorRes) 23 | // return builder() 24 | // } 25 | 26 | override fun itemsDisabledIndices(vararg disabledIndices: Int): T { 27 | listTemp.disabledIndices = intArrayOf(*disabledIndices) 28 | return builder() 29 | } 30 | 31 | // 32 | // override fun itemsGravity(gravity: GravityEnum): T { 33 | // builder.itemsGravity(translateGravityEnum(gravity.gravityInt)) 34 | // return builder() 35 | // } 36 | 37 | override fun items(itemCollection: MutableCollection): T { 38 | return items(itemCollection.toList()) 39 | } 40 | 41 | override fun items(itemList: List): T { 42 | listTemp.itemList = itemList 43 | return builder() 44 | } 45 | 46 | override fun items(itemsRes: Int): T { 47 | listTemp.itemsRes = itemsRes 48 | return builder() 49 | } 50 | 51 | override fun items(vararg items: CharSequence): T { 52 | return items(items.toList()) 53 | } 54 | 55 | override fun build(): IDialog { 56 | val dialog = super.build() 57 | listTemp.apply(builder, dialog) 58 | return dialog 59 | } 60 | } -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/ui/dialogs/mddialogs/list/custom/MaterialCustomListDialogBuilder.kt: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.ui.dialogs.mddialogs.list.custom 2 | 3 | 4 | import android.content.Context 5 | import android.widget.FrameLayout 6 | import androidx.recyclerview.widget.LinearLayoutManager 7 | import androidx.recyclerview.widget.RecyclerView 8 | import com.afollestad.materialdialogs.MaterialDialog 9 | import com.afollestad.materialdialogs.customview.customView 10 | import tiiehenry.android.ui.dialogs.mddialogs.base.MaterialBaseDialogBuilder 11 | import tiiehenry.android.ui.dialogs.mddialogs.base.NegativeButtonTemp 12 | import tiiehenry.android.ui.dialogs.mddialogs.base.NeutralButtonTemp 13 | import tiiehenry.android.ui.dialogs.mddialogs.base.PositiveButtonTemp 14 | import tiiehenry.android.ui.dialogs.api.IDialog 15 | import tiiehenry.android.ui.dialogs.api.strategy.list.custom.ICustomListDialogBuilder 16 | 17 | class MaterialCustomListDialogBuilder(val context: Context) : MaterialBaseDialogBuilder, ICustomListDialogBuilder { 18 | override val builder: MaterialDialog = MaterialDialog(context) 19 | 20 | override val positiveTemp: PositiveButtonTemp = PositiveButtonTemp() 21 | override val negativeTemp: NegativeButtonTemp = NegativeButtonTemp() 22 | override val neutralTemp: NeutralButtonTemp = NeutralButtonTemp() 23 | 24 | override lateinit var dialog: IDialog 25 | 26 | override fun adapter(adapter: RecyclerView.Adapter<*>, layoutManager: RecyclerView.LayoutManager?): ICustomListDialogBuilder { 27 | // builder.customListAdapter(adapter, layoutManager) 28 | // return builder() 29 | val rv = RecyclerView(context) 30 | rv.let { 31 | it.adapter = adapter 32 | it.layoutManager = layoutManager ?: LinearLayoutManager(context) 33 | } 34 | // val marginHorizontal=context.dp2px(24f) 35 | rv.layoutParams = FrameLayout.LayoutParams(-1, -2) 36 | // rv.margin(leftMargin = marginHorizontal,rightMargin = marginHorizontal) 37 | builder.customView(view = rv, scrollable = true, horizontalPadding = true, dialogWrapContent = true) 38 | return builder() 39 | } 40 | 41 | override fun adapter(adapter: RecyclerView.Adapter<*>): ICustomListDialogBuilder { 42 | return adapter(adapter, LinearLayoutManager(context, RecyclerView.VERTICAL, false)) 43 | } 44 | 45 | override fun builder(): ICustomListDialogBuilder { 46 | return this 47 | } 48 | } -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/ui/dialogs/mddialogs/list/multi/MaterialMultiListDialogBuilder.kt: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.ui.dialogs.mddialogs.list.multi 2 | 3 | import android.content.Context 4 | import com.afollestad.materialdialogs.MaterialDialog 5 | import tiiehenry.android.ui.dialogs.mddialogs.base.NegativeButtonTemp 6 | import tiiehenry.android.ui.dialogs.mddialogs.base.NeutralButtonTemp 7 | import tiiehenry.android.ui.dialogs.mddialogs.base.PositiveButtonTemp 8 | import tiiehenry.android.ui.dialogs.mddialogs.list.base.MaterialBaseListDialogBuilder 9 | import tiiehenry.android.ui.dialogs.api.IDialog 10 | import tiiehenry.android.ui.dialogs.api.callback.ListCallbackMultiChoice 11 | import tiiehenry.android.ui.dialogs.api.strategy.list.multi.IMultiListDialogBuilder 12 | 13 | class MaterialMultiListDialogBuilder(val context: Context) : MaterialBaseListDialogBuilder, IMultiListDialogBuilder { 14 | override val builder: MaterialDialog = MaterialDialog(context) 15 | 16 | override val positiveTemp: PositiveButtonTemp = PositiveButtonTemp() 17 | override val negativeTemp: NegativeButtonTemp = NegativeButtonTemp() 18 | override val neutralTemp: NeutralButtonTemp = NeutralButtonTemp() 19 | override val listTemp: MultiListTemp = MultiListTemp() 20 | 21 | override lateinit var dialog: IDialog 22 | 23 | override fun itemsCallbackMultiChoice(selectedIndices: IntArray?, callback: ListCallbackMultiChoice): IMultiListDialogBuilder { 24 | if (selectedIndices != null) { 25 | listTemp.selectedIndices = selectedIndices 26 | } 27 | listTemp.itemCallback = callback 28 | return builder() 29 | } 30 | 31 | override fun alwaysCallMultiChoiceCallback(): IMultiListDialogBuilder { 32 | listTemp.alwaysCallMultiChoiceCallback = true 33 | return builder() 34 | } 35 | 36 | override fun builder(): IMultiListDialogBuilder { 37 | return this 38 | } 39 | 40 | 41 | } -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/ui/dialogs/mddialogs/list/multi/MultiListTemp.kt: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.ui.dialogs.mddialogs.list.multi 2 | 3 | import com.afollestad.materialdialogs.MaterialDialog 4 | import com.afollestad.materialdialogs.list.listItemsMultiChoice 5 | import tiiehenry.android.ui.dialogs.mddialogs.list.base.ListTemp 6 | import tiiehenry.android.ui.dialogs.api.IDialog 7 | import tiiehenry.android.ui.dialogs.api.callback.ListCallbackMultiChoice 8 | 9 | class MultiListTemp : ListTemp() { 10 | var itemCallback: ListCallbackMultiChoice? = null 11 | var selectedIndices: IntArray = IntArray(0) 12 | var alwaysCallMultiChoiceCallback: Boolean = false 13 | 14 | override fun apply(builder: MaterialDialog, idialog: IDialog) { 15 | builder.listItemsMultiChoice(res = itemsRes, items = itemList, 16 | disabledIndices = disabledIndices, 17 | initialSelection = selectedIndices, 18 | waitForPositiveButton = !alwaysCallMultiChoiceCallback) { dialog, indices, items -> 19 | itemCallback?.onSelection(idialog, indices, items.toTypedArray()) 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/ui/dialogs/mddialogs/list/regular/MaterialRegularListDialogBuilder.kt: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.ui.dialogs.mddialogs.list.regular 2 | 3 | import android.content.Context 4 | import com.afollestad.materialdialogs.MaterialDialog 5 | import tiiehenry.android.ui.dialogs.mddialogs.base.NegativeButtonTemp 6 | import tiiehenry.android.ui.dialogs.mddialogs.base.NeutralButtonTemp 7 | import tiiehenry.android.ui.dialogs.mddialogs.base.PositiveButtonTemp 8 | import tiiehenry.android.ui.dialogs.mddialogs.list.base.MaterialBaseListDialogBuilder 9 | import tiiehenry.android.ui.dialogs.api.IDialog 10 | import tiiehenry.android.ui.dialogs.api.callback.ListCallback 11 | import tiiehenry.android.ui.dialogs.api.strategy.list.regular.IRegularListDialogBuilder 12 | 13 | open class MaterialRegularListDialogBuilder(val context: Context) : MaterialBaseListDialogBuilder, IRegularListDialogBuilder { 14 | override val builder: MaterialDialog = MaterialDialog(context) 15 | 16 | override val positiveTemp: PositiveButtonTemp = PositiveButtonTemp() 17 | override val negativeTemp: NegativeButtonTemp = NegativeButtonTemp() 18 | override val neutralTemp: NeutralButtonTemp = NeutralButtonTemp() 19 | override val listTemp: RegularListTemp = RegularListTemp() 20 | 21 | override lateinit var dialog: IDialog 22 | 23 | override fun itemsCallback(callback: ListCallback): IRegularListDialogBuilder { 24 | listTemp.itemCallback = callback 25 | return builder() 26 | } 27 | // 28 | // override fun itemsLongCallback(callback: ListLongCallback): IRegularListDialogBuilder { 29 | // builder.itemsLongCallback { _, itemView, position, text -> 30 | // callback.onLongSelection(dialog, itemView, position, text) 31 | // } 32 | // return builder() 33 | // } 34 | 35 | override fun builder(): IRegularListDialogBuilder { 36 | return this 37 | } 38 | 39 | } -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/ui/dialogs/mddialogs/list/regular/RegularListTemp.kt: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.ui.dialogs.mddialogs.list.regular 2 | 3 | import com.afollestad.materialdialogs.MaterialDialog 4 | import com.afollestad.materialdialogs.list.listItems 5 | import tiiehenry.android.ui.dialogs.mddialogs.list.base.ListTemp 6 | import tiiehenry.android.ui.dialogs.api.IDialog 7 | import tiiehenry.android.ui.dialogs.api.callback.ListCallback 8 | 9 | class RegularListTemp : ListTemp() { 10 | var itemCallback: ListCallback? = null 11 | 12 | override fun apply(builder: MaterialDialog, idialog: IDialog) { 13 | builder.listItems(res = itemsRes, items = itemList, 14 | disabledIndices = disabledIndices, 15 | waitForPositiveButton = false) { dialog, index, text -> 16 | itemCallback?.onSelection(idialog, index, text.toString()) 17 | } 18 | } 19 | 20 | } -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/ui/dialogs/mddialogs/list/single/MaterialSingleListDialogBuilder.kt: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.ui.dialogs.mddialogs.list.single 2 | 3 | import android.content.Context 4 | import com.afollestad.materialdialogs.MaterialDialog 5 | import tiiehenry.android.ui.dialogs.mddialogs.base.NegativeButtonTemp 6 | import tiiehenry.android.ui.dialogs.mddialogs.base.NeutralButtonTemp 7 | import tiiehenry.android.ui.dialogs.mddialogs.base.PositiveButtonTemp 8 | import tiiehenry.android.ui.dialogs.mddialogs.list.base.MaterialBaseListDialogBuilder 9 | import tiiehenry.android.ui.dialogs.api.IDialog 10 | import tiiehenry.android.ui.dialogs.api.callback.ListCallbackSingleChoice 11 | import tiiehenry.android.ui.dialogs.api.strategy.list.single.ISingleListDialogBuilder 12 | 13 | class MaterialSingleListDialogBuilder(val context: Context) : MaterialBaseListDialogBuilder, ISingleListDialogBuilder { 14 | override val builder: MaterialDialog = MaterialDialog(context) 15 | 16 | override val positiveTemp: PositiveButtonTemp = PositiveButtonTemp() 17 | override val negativeTemp: NegativeButtonTemp = NegativeButtonTemp() 18 | override val neutralTemp: NeutralButtonTemp = NeutralButtonTemp() 19 | override val listTemp: SingleListTemp = SingleListTemp() 20 | 21 | 22 | override lateinit var dialog: IDialog 23 | 24 | override fun alwaysCallSingleChoiceCallback(): ISingleListDialogBuilder { 25 | listTemp.alwaysCallSingleChoiceCallback = true 26 | return builder() 27 | } 28 | 29 | 30 | override fun itemsCallbackSingleChoice(selectedIndex: Int, callback: ListCallbackSingleChoice): ISingleListDialogBuilder { 31 | listTemp.selectedIndex = selectedIndex 32 | listTemp.itemCallback = callback 33 | return builder() 34 | } 35 | 36 | override fun builder(): ISingleListDialogBuilder { 37 | return this 38 | } 39 | 40 | } -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/ui/dialogs/mddialogs/list/single/SingleListTemp.kt: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.ui.dialogs.mddialogs.list.single 2 | 3 | import com.afollestad.materialdialogs.MaterialDialog 4 | import com.afollestad.materialdialogs.list.listItemsSingleChoice 5 | import tiiehenry.android.ui.dialogs.mddialogs.list.base.ListTemp 6 | import tiiehenry.android.ui.dialogs.api.IDialog 7 | import tiiehenry.android.ui.dialogs.api.callback.ListCallbackSingleChoice 8 | 9 | class SingleListTemp : ListTemp() { 10 | var selectedIndex: Int = -1 11 | var alwaysCallSingleChoiceCallback: Boolean = false 12 | var itemCallback: ListCallbackSingleChoice? = null 13 | 14 | override fun apply(builder: MaterialDialog, idialog: IDialog) { 15 | builder.listItemsSingleChoice(res = itemsRes, items = itemList, 16 | disabledIndices = disabledIndices, 17 | initialSelection = selectedIndex, 18 | waitForPositiveButton = !alwaysCallSingleChoiceCallback) { dialog, index, text -> 19 | itemCallback?.onSelection(idialog, index, text) 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/ui/dialogs/mddialogs/loading/LoadTemp.kt: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.ui.dialogs.mddialogs.loading 2 | 3 | import tiiehenry.android.ui.dialogs.api.callback.OnShowListener 4 | import tiiehenry.android.ui.dialogs.api.strategy.loading.ILoadingTask 5 | 6 | class LoadTemp { 7 | 8 | val loadingTaskList = mutableListOf>() 9 | 10 | var autoExecute: Boolean = true 11 | var autoDismiss: Boolean = true 12 | var showListener: OnShowListener? = null 13 | var minDisplayTime: Long = 0 14 | 15 | } -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/ui/dialogs/mddialogs/loading/MDLoadingDialogBuilder.kt: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.ui.dialogs.mddialogs.loading 2 | 3 | import android.content.Context 4 | import android.view.LayoutInflater 5 | import com.afollestad.materialdialogs.MaterialDialog 6 | import com.afollestad.materialdialogs.customview.customView 7 | import tiiehenry.android.ui.dialogs.mddialogs.base.MaterialBaseDialogBuilder 8 | import tiiehenry.android.ui.dialogs.mddialogs.base.NegativeButtonTemp 9 | import tiiehenry.android.ui.dialogs.mddialogs.base.NeutralButtonTemp 10 | import tiiehenry.android.ui.dialogs.mddialogs.base.PositiveButtonTemp 11 | import tiiehenry.android.ui.dialogs.api.IDialog 12 | import tiiehenry.android.ui.dialogs.api.callback.OnShowListener 13 | import tiiehenry.android.ui.dialogs.api.strategy.loading.ILoadingDialogBuilder 14 | import tiiehenry.android.ui.dialogs.api.strategy.loading.ILoadingTask 15 | import tiiehenry.android.ui.dialogs.mddialogs.R 16 | 17 | class MDLoadingDialogBuilder(val context: Context) : MaterialBaseDialogBuilder, ILoadingDialogBuilder { 18 | override val builder: MaterialDialog = MaterialDialog(context) 19 | 20 | override val positiveTemp: PositiveButtonTemp = PositiveButtonTemp() 21 | override val negativeTemp: NegativeButtonTemp = NegativeButtonTemp() 22 | override val neutralTemp: NeutralButtonTemp = NeutralButtonTemp() 23 | private val loadTemp: LoadTemp = LoadTemp() 24 | 25 | override lateinit var dialog: IDialog 26 | 27 | override fun minDisplayTime(delay: Long): ILoadingDialogBuilder { 28 | loadTemp.minDisplayTime = delay 29 | return builder() 30 | } 31 | 32 | override fun showListener(listener: OnShowListener): ILoadingDialogBuilder { 33 | loadTemp.showListener = listener 34 | return super.showListener(listener) 35 | } 36 | 37 | override fun autoDismiss(enable: Boolean): ILoadingDialogBuilder { 38 | loadTemp.autoDismiss = enable 39 | return super.autoDismiss(enable) 40 | } 41 | 42 | override fun autoExecuteTask(auto: Boolean): ILoadingDialogBuilder { 43 | loadTemp.autoExecute = auto 44 | return builder() 45 | } 46 | 47 | override fun addLoadingTask(text: CharSequence, task: ILoadingTask): ILoadingDialogBuilder { 48 | loadTemp.loadingTaskList.add(text to task) 49 | return builder() 50 | } 51 | 52 | override fun addLoadingTask(text: Int, task: ILoadingTask): ILoadingDialogBuilder { 53 | return addLoadingTask(context.getString(text), task) 54 | } 55 | 56 | override fun build(): IDialog { 57 | val layout = LayoutInflater.from(context).inflate(R.layout.mddialogs_loading_layout, null) 58 | builder.customView(view = layout, dialogWrapContent = false) 59 | dialog = MaterialLoadingDialogWrapper(this, layout, loadTemp) 60 | return dialog 61 | } 62 | 63 | override fun builder(): ILoadingDialogBuilder { 64 | return this 65 | } 66 | 67 | } 68 | 69 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/ui/dialogs/mddialogs/loading/MDLoadingDialogProvider.kt: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.ui.dialogs.mddialogs.loading 2 | 3 | import android.content.Context 4 | import tiiehenry.android.ui.dialogs.api.strategy.loading.ILoadingDialogBuilder 5 | import tiiehenry.android.ui.dialogs.api.strategy.loading.ILoadingDialogProvider 6 | 7 | class MDLoadingDialogProvider(val context: Context) : ILoadingDialogProvider { 8 | override fun builder(): ILoadingDialogBuilder { 9 | return MDLoadingDialogBuilder(context) 10 | } 11 | } -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/ui/dialogs/mddialogs/loading/MaterialLoadingDialogWrapper.kt: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.ui.dialogs.mddialogs.loading 2 | 3 | import android.view.View 4 | import android.widget.TextView 5 | import org.jetbrains.anko.doAsync 6 | import tiiehenry.android.ui.dialogs.mddialogs.base.MaterialDialogBaseWrapper 7 | import tiiehenry.android.ui.dialogs.api.strategy.loading.ILoadingDialog 8 | import tiiehenry.android.ui.dialogs.api.strategy.loading.OnLoadingException 9 | import tiiehenry.android.ui.dialogs.mddialogs.R 10 | import tiiehenry.android.ui.dialogs.mddialogs.applyTheme 11 | import tiiehenry.android.ui.dialogs.mddialogs.runOnUIThread 12 | import kotlin.math.max 13 | 14 | class MaterialLoadingDialogWrapper(builder: MDLoadingDialogBuilder, val layout: View, val loadTemp: LoadTemp) : MaterialDialogBaseWrapper(builder.builder.applyTheme()), ILoadingDialog { 15 | 16 | override fun setLoadingText(text: CharSequence?) { 17 | runOnUIThread { 18 | layout.findViewById(R.id.mddialogs_loading_text_tv).text = text 19 | } 20 | } 21 | 22 | override fun executeLoadingTask() { 23 | try { 24 | doAsync { 25 | val oldTime = System.currentTimeMillis() 26 | for (taskItem in loadTemp.loadingTaskList) { 27 | setLoadingText(taskItem.first) 28 | // Thread.sleep(10000) 29 | taskItem.second.onLoading(this@MaterialLoadingDialogWrapper) 30 | } 31 | val newTime = System.currentTimeMillis() 32 | // loge((builder._minDisplayTime - (newTime - oldTime)).toString()) 33 | Thread.sleep(max(loadTemp.minDisplayTime - (newTime - oldTime), 0)) 34 | if (loadTemp.autoDismiss) { 35 | dialog.dismiss() 36 | } 37 | } 38 | } catch (e: OnLoadingException) { 39 | setLoadingText(e.message) 40 | dialog.cancelable(true) 41 | } catch (e: Exception) { 42 | e.printStackTrace() 43 | setLoadingText(e.message) 44 | dialog.cancelable(true) 45 | } 46 | } 47 | 48 | override fun show() { 49 | super.show() 50 | dialog.setOnShowListener { 51 | if (loadTemp.autoExecute) { 52 | executeLoadingTask() 53 | } 54 | loadTemp.showListener?.onShow(this) 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/ui/dialogs/mddialogs/progress/MDProgressDialogBuilder.kt: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.ui.dialogs.mddialogs.progress 2 | 3 | import android.content.Context 4 | import com.afollestad.materialdialogs.MaterialDialog 5 | import tiiehenry.android.ui.dialogs.mddialogs.base.MaterialBaseDialogBuilder 6 | import tiiehenry.android.ui.dialogs.mddialogs.base.NegativeButtonTemp 7 | import tiiehenry.android.ui.dialogs.mddialogs.base.NeutralButtonTemp 8 | import tiiehenry.android.ui.dialogs.mddialogs.base.PositiveButtonTemp 9 | import tiiehenry.android.ui.dialogs.api.IDialog 10 | import tiiehenry.android.ui.dialogs.api.strategy.progress.IProgressDialogBuilder 11 | import java.text.NumberFormat 12 | 13 | class MDProgressDialogBuilder(val context: Context) : MaterialBaseDialogBuilder, IProgressDialogBuilder { 14 | override val builder: MaterialDialog = MaterialDialog(context) 15 | 16 | override val positiveTemp: PositiveButtonTemp = PositiveButtonTemp() 17 | override val negativeTemp: NegativeButtonTemp = NegativeButtonTemp() 18 | override val neutralTemp: NeutralButtonTemp = NeutralButtonTemp() 19 | 20 | override lateinit var dialog: IDialog 21 | override fun progressPercentFormat(format: NumberFormat): IProgressDialogBuilder { 22 | // builder.progressPercentFormat(format) 23 | return builder() 24 | } 25 | 26 | override fun progressIndeterminateStyle(horizontal: Boolean): IProgressDialogBuilder { 27 | // builder.progressIndeterminateStyle(horizontal) 28 | return builder() 29 | } 30 | 31 | override fun progressNumberFormat(format: String): IProgressDialogBuilder { 32 | // builder.progressNumberFormat(format) 33 | return builder() 34 | } 35 | 36 | override fun progress(indeterminate: Boolean, max: Int): IProgressDialogBuilder { 37 | // builder.progress(indeterminate, max) 38 | return builder() 39 | } 40 | 41 | override fun progress(indeterminate: Boolean, max: Int, showMinMax: Boolean): IProgressDialogBuilder { 42 | // builder.progress(indeterminate, max, showMinMax) 43 | return builder() 44 | } 45 | 46 | 47 | override fun builder(): IProgressDialogBuilder { 48 | return this 49 | } 50 | 51 | } -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/ui/dialogs/mddialogs/progress/MDProgressDialogProvider.kt: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.ui.dialogs.mddialogs.progress 2 | 3 | import android.content.Context 4 | import tiiehenry.android.ui.dialogs.api.strategy.progress.IProgressDialogBuilder 5 | import tiiehenry.android.ui.dialogs.api.strategy.progress.IProgressDialogProvider 6 | 7 | class MDProgressDialogProvider(val context: Context) : IProgressDialogProvider { 8 | override fun builder(): IProgressDialogBuilder { 9 | return MDProgressDialogBuilder(context) 10 | } 11 | } -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/view/base/adapter/INotifier.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.view.base.adapter; 2 | 3 | public interface INotifier { 4 | 5 | void notifyDataSetChanged(); 6 | 7 | void notifyItemChanged(int position); 8 | 9 | void notifyItemRangeChanged(int positionStart, int itemCount); 10 | 11 | void notifyItemInserted(int position); 12 | 13 | void notifyItemRangeInserted(int positionStart, int itemCount); 14 | 15 | void notifyItemMoved(int fromPosition, int toPosition); 16 | 17 | void notifyItemRemoved(int position); 18 | 19 | void notifyItemRangeRemoved(int positionStart, int itemCount); 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/view/base/adapter/wrapped/AllChangedNotifier.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.view.base.adapter.wrapped; 2 | 3 | import tiiehenry.android.view.base.adapter.INotifier; 4 | 5 | public abstract class AllChangedNotifier implements IAllChangedNotifier { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/view/base/adapter/wrapped/IAllChangedNotifier.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.view.base.adapter.wrapped; 2 | 3 | import tiiehenry.android.view.base.adapter.INotifier; 4 | 5 | public interface IAllChangedNotifier extends INotifier { 6 | 7 | 8 | @Override 9 | default void notifyItemChanged(int position) { 10 | notifyDataSetChanged(); 11 | } 12 | 13 | @Override 14 | default void notifyItemRangeChanged(int positionStart, int itemCount) { 15 | notifyDataSetChanged(); 16 | } 17 | 18 | @Override 19 | default void notifyItemInserted(int position) { 20 | notifyDataSetChanged(); 21 | } 22 | 23 | @Override 24 | default void notifyItemRangeInserted(int positionStart, int itemCount) { 25 | notifyDataSetChanged(); 26 | } 27 | 28 | @Override 29 | default void notifyItemMoved(int fromPosition, int toPosition) { 30 | notifyDataSetChanged(); 31 | } 32 | 33 | @Override 34 | default void notifyItemRemoved(int position) { 35 | notifyDataSetChanged(); 36 | } 37 | 38 | @Override 39 | default void notifyItemRangeRemoved(int positionStart, int itemCount) { 40 | notifyDataSetChanged(); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/view/base/holder/OnItemClickListener.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.view.base.holder; 2 | 3 | import android.view.View; 4 | 5 | import androidx.annotation.NonNull; 6 | 7 | /** 8 | * 列表条目点击监听 9 | */ 10 | public interface OnItemClickListener { 11 | /** 12 | * 条目点击 13 | * 14 | * @param itemView 条目 15 | * @param item 数据 16 | * @param position 索引 17 | */ 18 | void onItemClick(@NonNull View itemView, @NonNull DATATYPE item, int position); 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/view/base/holder/OnItemLongClickListener.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.view.base.holder; 2 | 3 | import android.view.View; 4 | 5 | import androidx.annotation.NonNull; 6 | 7 | /** 8 | * 列表条目长按监听 9 | */ 10 | public interface OnItemLongClickListener { 11 | /** 12 | * 条目长按 13 | * 14 | * @param itemView 条目 15 | * @param item 数据 16 | * @param position 索引 17 | */ 18 | void onItemLongClick(@NonNull View itemView, @NonNull DATATYPE item, int position); 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/view/base/holder/OnViewItemClickListener.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.view.base.holder; 2 | 3 | import android.view.View; 4 | 5 | import androidx.annotation.NonNull; 6 | 7 | /** 8 | * 布局内控件点击事件 9 | */ 10 | public interface OnViewItemClickListener { 11 | /** 12 | * 控件被点击 13 | * 14 | * @param view 被点击的控件 15 | * @param item 数据 16 | * @param position 索引 17 | */ 18 | void onViewItemClick(@NonNull View view, @NonNull DATATYPE item, int position); 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/view/recyclerview/adapter/BaseIdRecyclerAdapter.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.view.recyclerview.adapter; 2 | 3 | import android.view.View; 4 | import android.view.ViewGroup; 5 | 6 | import androidx.annotation.NonNull; 7 | 8 | import java.util.Collection; 9 | 10 | public abstract class BaseIdRecyclerAdapter extends InflateRecyclerAdapter { 11 | 12 | public BaseIdRecyclerAdapter() { 13 | super(); 14 | } 15 | 16 | public BaseIdRecyclerAdapter(@NonNull Collection list) { 17 | super(list); 18 | } 19 | 20 | public BaseIdRecyclerAdapter(@NonNull T[] data) { 21 | super(data); 22 | } 23 | 24 | /** 25 | * 适配的布局 26 | * 27 | * @param viewType 28 | * @return 29 | */ 30 | protected abstract int getItemLayoutId(int viewType); 31 | 32 | @Override 33 | protected View inflateItemLayout(ViewGroup parent, int viewType) { 34 | return inflateView(parent, getItemLayoutId(viewType)); 35 | } 36 | 37 | @NonNull 38 | @Override 39 | public InflateRecyclerAdapter getInstance() { 40 | return this; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/view/recyclerview/adapter/IRecyclerAdapter.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.view.recyclerview.adapter; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.annotation.Nullable; 5 | 6 | import tiiehenry.android.view.base.adapter.IAdapter; 7 | import tiiehenry.android.view.base.holder.OnItemClickListener; 8 | import tiiehenry.android.view.base.holder.OnItemLongClickListener; 9 | import tiiehenry.android.view.recyclerview.holder.IRecyclerViewHolder; 10 | 11 | public interface IRecyclerAdapter extends IAdapter { 12 | 13 | @NonNull 14 | IADAPTER setOnItemClickListener(@Nullable OnItemClickListener listener); 15 | 16 | @NonNull 17 | IADAPTER setOnItemLongClickListener(@Nullable OnItemLongClickListener listener); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/view/recyclerview/adapter/InflateRecyclerAdapter.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.view.recyclerview.adapter; 2 | 3 | import android.view.View; 4 | import android.view.ViewGroup; 5 | 6 | import androidx.annotation.NonNull; 7 | 8 | import java.util.Collection; 9 | 10 | import tiiehenry.android.view.recyclerview.holder.RecyclerViewHolder; 11 | 12 | 13 | public abstract class InflateRecyclerAdapter extends AbstractRecyclerAdapter, T> { 14 | 15 | public InflateRecyclerAdapter() { 16 | super(); 17 | } 18 | 19 | public InflateRecyclerAdapter(Collection list) { 20 | super(list); 21 | } 22 | 23 | public InflateRecyclerAdapter(T[] data) { 24 | super(data); 25 | } 26 | 27 | /** 28 | * 适配的布局 29 | * 30 | * @param parent 31 | * @param viewType 32 | * @return layout 33 | */ 34 | protected abstract View inflateItemLayout(ViewGroup parent, int viewType); 35 | 36 | @NonNull 37 | @Override 38 | protected RecyclerViewHolder getViewHolder(@NonNull ViewGroup parent, int viewType) { 39 | return new RecyclerViewHolder(inflateItemLayout(parent, viewType)); 40 | } 41 | 42 | @NonNull 43 | @Override 44 | public InflateRecyclerAdapter getInstance() { 45 | return this; 46 | } 47 | } -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/view/recyclerview/adapter/SimpleIdRecyclerAdapter.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.view.recyclerview.adapter; 2 | 3 | import android.view.View; 4 | import android.view.ViewGroup; 5 | 6 | import androidx.annotation.LayoutRes; 7 | import androidx.annotation.NonNull; 8 | 9 | import java.util.Collection; 10 | 11 | import tiiehenry.android.view.recyclerview.adapter.InflateRecyclerAdapter; 12 | 13 | public abstract class SimpleIdRecyclerAdapter extends InflateRecyclerAdapter { 14 | 15 | private final int layoutId; 16 | 17 | public SimpleIdRecyclerAdapter(@LayoutRes int layoutId) { 18 | super(); 19 | this.layoutId = layoutId; 20 | } 21 | 22 | public SimpleIdRecyclerAdapter(@LayoutRes int layoutId, @NonNull Collection list) { 23 | super(list); 24 | this.layoutId = layoutId; 25 | } 26 | 27 | public SimpleIdRecyclerAdapter(@LayoutRes int layoutId,@NonNull T[] data) { 28 | super(data); 29 | this.layoutId = layoutId; 30 | } 31 | 32 | /** 33 | * 适配的布局 34 | * 35 | * @param parent 36 | * @param viewType 37 | * @return layout 38 | */ 39 | protected View inflateItemLayout(ViewGroup parent, int viewType) { 40 | return inflateView(parent, layoutId); 41 | } 42 | 43 | @NonNull 44 | @Override 45 | public InflateRecyclerAdapter getInstance() { 46 | return this; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/view/recyclerview/holder/IRecyclerViewHolder.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.view.recyclerview.holder; 2 | 3 | import tiiehenry.android.view.base.holder.IViewHolder; 4 | 5 | public interface IRecyclerViewHolder extends IViewHolder { 6 | int getLayoutPosition(); 7 | 8 | int getPosition(); 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/view/recyclerview/holder/RecyclerViewHolder.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.view.recyclerview.holder; 2 | 3 | 4 | import android.util.SparseArray; 5 | import android.view.View; 6 | 7 | import androidx.annotation.NonNull; 8 | import androidx.recyclerview.widget.RecyclerView; 9 | 10 | 11 | public class RecyclerViewHolder extends RecyclerView.ViewHolder implements IRecyclerViewHolder { 12 | 13 | private SparseArray mViews; 14 | 15 | public RecyclerViewHolder(View itemView) { 16 | super(itemView); 17 | mViews = new SparseArray<>(); 18 | } 19 | 20 | @NonNull 21 | @Override 22 | public View getItemView() { 23 | return itemView; 24 | } 25 | 26 | @Override 27 | public T findView(int id) { 28 | View view = mViews.get(id); 29 | if (view == null) { 30 | view = itemView.findViewById(id); 31 | mViews.put(id, view); 32 | } 33 | return (T) view; 34 | } 35 | 36 | @Override 37 | public void clearViewCache() { 38 | if (mViews != null) { 39 | mViews.clear(); 40 | } 41 | } 42 | 43 | @NonNull 44 | @Override 45 | public RecyclerViewHolder getInstance() { 46 | return this; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/view/spinner/adapter/AbstractSpinnerAdapter.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.view.spinner.adapter; 2 | 3 | import android.view.View; 4 | import android.view.ViewGroup; 5 | import android.widget.BaseAdapter; 6 | 7 | import androidx.annotation.NonNull; 8 | 9 | import java.util.ArrayList; 10 | import java.util.Arrays; 11 | import java.util.Collection; 12 | import java.util.List; 13 | 14 | import tiiehenry.android.view.spinner.holder.SpinnerViewHolder; 15 | 16 | /** 17 | * @author TIIEHenry 18 | */ 19 | public abstract class AbstractSpinnerAdapter extends BaseAdapter 21 | implements ISpinnerAdapter { 22 | private List mData = new ArrayList<>(); 23 | 24 | public AbstractSpinnerAdapter() { 25 | super(); 26 | } 27 | 28 | public AbstractSpinnerAdapter(@NonNull Collection list) { 29 | this(); 30 | mData.addAll(list); 31 | } 32 | 33 | public AbstractSpinnerAdapter(@NonNull DATATYPE[] data) { 34 | this(); 35 | if (data.length > 0) { 36 | mData.addAll(Arrays.asList(data)); 37 | } 38 | } 39 | 40 | @NonNull 41 | public abstract SpinnerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int position); 42 | 43 | @NonNull 44 | public abstract SpinnerViewHolder onCreateDropDownViewHolder(@NonNull ViewGroup parent, int position); 45 | 46 | public abstract void bindData(@NonNull SpinnerViewHolder holder, @NonNull DATATYPE item, int position); 47 | 48 | public abstract void bindDropDownData(@NonNull SpinnerViewHolder holder, @NonNull DATATYPE item, int position); 49 | 50 | @Override 51 | public int getCount() { 52 | return getDataCount(); 53 | } 54 | 55 | @Override 56 | public long getItemId(int position) { 57 | return 0; 58 | } 59 | 60 | @Override 61 | public View getView(int position, View convertView, ViewGroup parent) { 62 | SpinnerViewHolder holder = onCreateViewHolder(parent, position); 63 | DATATYPE data = getData(position); 64 | // holder.setData(data); 65 | bindData(holder, data, position); 66 | holder.getItemView().setTag(holder); 67 | return holder.getItemView(); 68 | } 69 | 70 | @Override 71 | public View getDropDownView(int position, View convertView, ViewGroup parent) { 72 | SpinnerViewHolder holder = onCreateDropDownViewHolder(parent, position); 73 | DATATYPE data = getData(position); 74 | // holder.setData(data); 75 | bindDropDownData(holder, data, position); 76 | holder.getItemView().setTag(holder); 77 | return holder.getItemView(); 78 | } 79 | 80 | @Override 81 | public List getDataList() { 82 | return mData; 83 | } 84 | 85 | @Override 86 | public Object getItem(int position) { 87 | return getData(position); 88 | } 89 | 90 | @Override 91 | public DATATYPE getData(int position) { 92 | return getDataList().get(position); 93 | } 94 | } 95 | 96 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/view/spinner/adapter/ISpinnerAdapter.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.view.spinner.adapter; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.annotation.Nullable; 5 | 6 | import tiiehenry.android.view.base.adapter.IAdapter; 7 | import tiiehenry.android.view.base.adapter.INotifier; 8 | import tiiehenry.android.view.base.adapter.wrapped.IAllChangedNotifier; 9 | import tiiehenry.android.view.base.holder.OnItemClickListener; 10 | import tiiehenry.android.view.base.holder.OnItemLongClickListener; 11 | import tiiehenry.android.view.spinner.holder.ISpinnerViewHolder; 12 | 13 | public interface ISpinnerAdapter 16 | extends IAdapter, IAllChangedNotifier { 17 | 18 | 19 | @NonNull 20 | @Override 21 | default INotifier getNotifier() { 22 | return getInstance(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/view/spinner/holder/ISpinnerViewHolder.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.view.spinner.holder; 2 | 3 | import tiiehenry.android.view.base.holder.IViewHolder; 4 | 5 | public interface ISpinnerViewHolder extends IViewHolder { 6 | 7 | } -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/android/view/spinner/holder/SpinnerViewHolder.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.android.view.spinner.holder; 2 | 3 | import android.util.SparseArray; 4 | import android.view.View; 5 | 6 | import androidx.annotation.NonNull; 7 | 8 | public class SpinnerViewHolder implements ISpinnerViewHolder { 9 | @NonNull 10 | private final View itemView; 11 | 12 | private SparseArray mViews; 13 | 14 | public SpinnerViewHolder(View itemView) { 15 | this.itemView = itemView; 16 | mViews = new SparseArray<>(); 17 | } 18 | 19 | @NonNull 20 | @Override 21 | public View getItemView() { 22 | return itemView; 23 | } 24 | 25 | @Override 26 | public T findView(int id) { 27 | View view = mViews.get(id); 28 | if (view == null) { 29 | view = itemView.findViewById(id); 30 | mViews.put(id, view); 31 | } 32 | return (T) view; 33 | } 34 | 35 | @Override 36 | public void clearViewCache() { 37 | if (mViews != null) { 38 | mViews.clear(); 39 | } 40 | } 41 | 42 | @NonNull 43 | @Override 44 | public SpinnerViewHolder getInstance() { 45 | return this; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/io/IOExceptionMaker.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.io; 2 | 3 | import java.io.File; 4 | import java.io.FileNotFoundException; 5 | import java.io.IOException; 6 | 7 | /** 8 | * @author TIIEHenry 9 | */ 10 | public class IOExceptionMaker { 11 | 12 | public static void exists(File f) throws IOException { 13 | exists(f.toString()); 14 | } 15 | 16 | public static void exists(String path) throws IOException { 17 | throw new IOException("File already exists: " + path); 18 | } 19 | 20 | public static void mkdir(File f) throws IOException { 21 | mkdir(f.toString()); 22 | } 23 | 24 | public static void mkdir(String path) throws IOException { 25 | throw new IOException("Can not create directory: " + path); 26 | } 27 | 28 | public static void notFound(File f) throws FileNotFoundException { 29 | notFound(f.toString()); 30 | } 31 | 32 | public static void notFound(String path) throws FileNotFoundException { 33 | throw new FileNotFoundException("File not found: " + path); 34 | } 35 | 36 | public static void notFile(File f) throws IOException { 37 | notFile(f.toString()); 38 | } 39 | 40 | public static void notFile(String path) throws IOException { 41 | throw new IOException("Is not a file: " + path); 42 | } 43 | 44 | public static void notDir(File f) throws IOException { 45 | notDir(f.toString()); 46 | } 47 | 48 | public static void notDir(String path) throws IOException { 49 | throw new IOException("Is not a directory: " + path); 50 | } 51 | 52 | public static void notFileOrDir(File f) throws IOException { 53 | notFileOrDir(f.toString()); 54 | } 55 | 56 | public static void notFileOrDir(String path) throws IOException { 57 | throw new IOException("Is not a file or directory: " + path); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/ktx/ViewExt.kt: -------------------------------------------------------------------------------- 1 | package tiiehenry.ktx 2 | 3 | import android.view.LayoutInflater 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import androidx.annotation.LayoutRes 7 | 8 | fun View.inflate(@LayoutRes resource: Int, root: ViewGroup?): View? { 9 | return LayoutInflater.from(context).inflate(resource, root) 10 | } 11 | 12 | fun View.inflate(@LayoutRes resource: Int, root: ViewGroup?, attachToRoot: Boolean): View? { 13 | return LayoutInflater.from(context).inflate(resource, root, attachToRoot) 14 | } 15 | 16 | fun View.visible() { 17 | visibility = View.VISIBLE 18 | } 19 | 20 | fun View.visibility(visible: Boolean) { 21 | visibility = if (visible) { 22 | View.VISIBLE 23 | } else { 24 | View.GONE 25 | } 26 | } 27 | 28 | fun View.invisible() { 29 | visibility = View.INVISIBLE 30 | } 31 | 32 | fun View.gone() { 33 | visibility = View.GONE 34 | } 35 | 36 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/ktx/app/ActivityExt.kt: -------------------------------------------------------------------------------- 1 | package tiiehenry.ktx.app 2 | 3 | import android.app.Activity 4 | import android.content.Context 5 | import android.util.DisplayMetrics 6 | import android.view.WindowManager 7 | 8 | 9 | 10 | fun Activity.getStatusBarHeight(): Int { 11 | var result = 0 12 | val resourceId = resources.getIdentifier("status_bar_height", "dimen", "android") 13 | if (resourceId > 0) { 14 | result = resources.getDimensionPixelSize(resourceId) 15 | } 16 | return result 17 | } -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/ktx/content/ContextExt.kt: -------------------------------------------------------------------------------- 1 | package tiiehenry.ktx.content 2 | 3 | import android.content.Context 4 | import android.util.DisplayMetrics 5 | import android.util.TypedValue 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.view.ViewGroup 9 | import android.view.WindowManager 10 | import android.widget.Toast 11 | import androidx.annotation.LayoutRes 12 | import androidx.annotation.Nullable 13 | import androidx.core.content.ContextCompat 14 | 15 | fun Context.getWidth(): Int { 16 | val wm = getSystemService(Context.WINDOW_SERVICE) as WindowManager 17 | val outMetrics = DisplayMetrics() 18 | wm.defaultDisplay.getMetrics(outMetrics) 19 | return outMetrics.widthPixels 20 | } 21 | 22 | fun Context.getHeight(): Int { 23 | val wm = getSystemService(Context.WINDOW_SERVICE) as WindowManager 24 | val outMetrics = DisplayMetrics() 25 | wm.defaultDisplay.getMetrics(outMetrics) 26 | return outMetrics.heightPixels 27 | } 28 | 29 | fun Context.getAttrColor(id: Int): Int { 30 | val typedValue = TypedValue() 31 | theme.resolveAttribute(id, typedValue, true) 32 | return typedValue.data 33 | } 34 | 35 | fun Context.getColorCompat(id: Int): Int { 36 | return ContextCompat.getColor(this, id) 37 | } 38 | 39 | fun Context.getDimen(id: Int): Int { 40 | return resources.getDimensionPixelSize(id) 41 | } 42 | 43 | 44 | fun Context.toast(id: Int) { 45 | toast(getString(id)) 46 | } 47 | 48 | fun Context.toast(a: Any) { 49 | toast(a.toString()) 50 | } 51 | 52 | fun Context.toast(text: String) { 53 | Toast.makeText(this, text, Toast.LENGTH_SHORT).show() 54 | } 55 | 56 | fun Context.inflate(@LayoutRes resource: Int, root: ViewGroup?): View { 57 | return LayoutInflater.from(this).inflate(resource, root) 58 | } 59 | 60 | fun Context.inflate(@LayoutRes resource: Int,root: ViewGroup?, attachToRoot: Boolean): View { 61 | return LayoutInflater.from(this).inflate(resource, root, attachToRoot) 62 | } -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/ktx/content/ThemeExt.kt: -------------------------------------------------------------------------------- 1 | package tiiehenry.ktx.content 2 | 3 | import android.content.Context 4 | import android.util.TypedValue 5 | import androidx.core.content.ContextCompat 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/ktx/io/FileExt.kt: -------------------------------------------------------------------------------- 1 | package tiiehenry.ktx.io 2 | 3 | import java.io.File 4 | import java.math.BigInteger 5 | import java.security.MessageDigest 6 | 7 | fun File.md5(): String { 8 | val digest = MessageDigest.getInstance("MD5") 9 | val bigInt = BigInteger(1, digest.digest(readBytes())) 10 | return bigInt.toString(16) 11 | } 12 | 13 | fun File.suffix(): String { 14 | return path.substring(path.lastIndexOf(".") + 1) 15 | } 16 | fun File.child(name:String): File { 17 | return File(this,name) 18 | } -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/ktx/lang/CastExt.kt: -------------------------------------------------------------------------------- 1 | package tiiehenry.ktx.lang 2 | 3 | fun Any?.asString(): String? { 4 | return this as? String? 5 | } 6 | 7 | fun Any?.asString(f: (String) -> Unit) { 8 | asString()?.let { f.invoke(it) } 9 | } 10 | 11 | fun Any?.asInt(): Int? { 12 | return this as? Int? 13 | } 14 | 15 | fun Any?.asInt(f: (Int) -> Unit) { 16 | asInt()?.let { f.invoke(it) } 17 | } 18 | 19 | fun Any?.asInteger(): Int? { 20 | return this as? Int? 21 | } 22 | 23 | fun Any?.asInteger(f: (Int) -> Unit) { 24 | asInteger()?.let { f.invoke(it) } 25 | } 26 | 27 | fun Any?.asBoolean(): Boolean? { 28 | return this as? Boolean? 29 | } 30 | 31 | fun Any?.asBoolean(f: (Boolean) -> Unit) { 32 | asBoolean()?.let { f.invoke(it) } 33 | } 34 | 35 | fun Any?.asArray(): Array<*>? { 36 | return this as? Array<*>? 37 | } 38 | 39 | fun Any?.asArray(f: (Array<*>) -> Unit) { 40 | asArray()?.let { f.invoke(it) } 41 | } -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/ktx/lang/JudgeExt.kt: -------------------------------------------------------------------------------- 1 | package tiiehenry.ktx.lang 2 | 3 | 4 | inline fun CharSequence.ifBlank(block: (CharSequence) -> R) { 5 | if (this.isBlank()) 6 | block.invoke(this) 7 | } 8 | 9 | inline fun CharSequence.ifNotBlank(block: (CharSequence) -> R) { 10 | if (this.isNotBlank()) 11 | block.invoke(this) 12 | } 13 | 14 | inline fun CharSequence.ifEmpty(block: (CharSequence) -> R) { 15 | if (this.isEmpty()) 16 | block.invoke(this) 17 | } 18 | 19 | inline fun CharSequence.ifNotEmpty(block: (CharSequence) -> R) { 20 | if (this.isNotEmpty()) 21 | block.invoke(this) 22 | } 23 | 24 | inline fun Boolean.ifFalse(block: (Boolean) -> R) { 25 | if (this == false) 26 | block.invoke(this) 27 | } 28 | 29 | inline fun Boolean.ifTrue(block: (Boolean) -> R) { 30 | if (this) 31 | block.invoke(this) 32 | } 33 | 34 | //@Deprecated("no auto infer", ReplaceWith("if (v == null) block")) 35 | inline fun T.ifNull(v: Any?, block: (T) -> R) { 36 | if (v == null) 37 | block.invoke(this) 38 | } 39 | 40 | //@Deprecated("no auto infer", ReplaceWith("if (v != null) block")) 41 | inline fun T.ifNonNull(v: Any?, block: (T) -> R) { 42 | if (v != null) 43 | block.invoke(this) 44 | } -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/ktx/lang/StringExt.kt: -------------------------------------------------------------------------------- 1 | package tiiehenry.ktx.lang 2 | 3 | import java.io.File 4 | 5 | fun String.fileExtension(): String { 6 | return File(this).extension 7 | } -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/ktx/lang/TryExt.kt: -------------------------------------------------------------------------------- 1 | package tiiehenry.ktx.lang 2 | 3 | 4 | fun Any?.trySafe(f: (Any) -> Unit) { 5 | if (this != null) 6 | try { 7 | f.invoke(this) 8 | } catch (e: Exception) { 9 | e.printStackTrace() 10 | } 11 | } 12 | inline fun T.tryApply(block: T.() -> Unit): T { 13 | if (this != null) 14 | try { 15 | block() 16 | } catch (e: Exception) { 17 | e.printStackTrace() 18 | } 19 | return this 20 | } 21 | 22 | inline fun T.tryLet(block: (T) -> R): R? { 23 | if (this != null) 24 | try { 25 | return block(this) 26 | } catch (e: Exception) { 27 | e.printStackTrace() 28 | } 29 | return null 30 | } -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/viewcontroller/GodModeApplication.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.viewcontroller; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | 6 | /** 7 | * Created by jrsen on 17-10-16. 8 | */ 9 | 10 | public final class GodModeApplication extends Application { 11 | 12 | public static final String TAG = "GodMode"; 13 | private static GodModeApplication sApplication; 14 | 15 | public GodModeApplication() { 16 | sApplication = this; 17 | } 18 | 19 | @Override 20 | protected void attachBaseContext(Context base) { 21 | CrashHandler.install(base); 22 | super.attachBaseContext(base); 23 | } 24 | 25 | public static GodModeApplication getApplication() { 26 | return sApplication; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/viewcontroller/QuickSettingsService.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.viewcontroller; 2 | 3 | import android.annotation.TargetApi; 4 | import android.content.SharedPreferences; 5 | import android.graphics.drawable.Icon; 6 | import android.os.Build; 7 | import android.preference.PreferenceManager; 8 | import android.service.quicksettings.Tile; 9 | import android.service.quicksettings.TileService; 10 | import android.text.TextUtils; 11 | 12 | import tiiehenry.viewcontroller.injection.bridge.GodModeManager; 13 | 14 | /** 15 | */ 16 | 17 | @TargetApi(Build.VERSION_CODES.N) 18 | public final class QuickSettingsService extends TileService implements SharedPreferences.OnSharedPreferenceChangeListener { 19 | 20 | @Override 21 | public void onStartListening() { 22 | super.onStartListening(); 23 | PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(this); 24 | updateTile(); 25 | } 26 | 27 | @Override 28 | public void onStopListening() { 29 | super.onStopListening(); 30 | PreferenceManager.getDefaultSharedPreferences(this).unregisterOnSharedPreferenceChangeListener(this); 31 | } 32 | 33 | @Override 34 | public void onClick() { 35 | setEditModeEnable(!isEditMode()); 36 | updateTile(); 37 | } 38 | 39 | private void updateTile() { 40 | Tile tile = this.getQsTile(); 41 | if (tile != null) { 42 | boolean isActive = isEditMode(); 43 | 44 | // Change the tile to match the service status. 45 | Icon newIcon = Icon.createWithResource(this, isActive ? R.drawable.ic_angel_normal : R.drawable.ic_angel_disable); 46 | int newState = isActive ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; 47 | 48 | // Change the UI of the tile. 49 | tile.setIcon(newIcon); 50 | tile.setState(newState); 51 | 52 | // Need to call updateTile for the tile to pick up changes. 53 | tile.updateTile(); 54 | } 55 | } 56 | 57 | private void setEditModeEnable(boolean enable) { 58 | SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); 59 | sp.edit().putBoolean("editor_switch", enable).apply(); 60 | GodModeManager.getDefault().setEditMode(enable); 61 | } 62 | 63 | public boolean isEditMode() { 64 | SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); 65 | return sp.getBoolean("editor_switch", false); 66 | } 67 | 68 | @Override 69 | public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { 70 | if (TextUtils.equals(getString(R.string.pref_key_editor), key)) { 71 | updateTile(); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/viewcontroller/SettingsActivity.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.viewcontroller; 2 | 3 | 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | 7 | import androidx.appcompat.app.AppCompatActivity; 8 | import androidx.fragment.app.Fragment; 9 | import androidx.lifecycle.ViewModelProvider; 10 | 11 | 12 | import tiiehenry.viewcontroller.model.SharedViewModel; 13 | 14 | public class SettingsActivity extends AppCompatActivity { 15 | 16 | @Override 17 | protected void onCreate(Bundle savedInstanceState) { 18 | super.onCreate(savedInstanceState); 19 | setContentView(R.layout.fragment_container_activity); 20 | SharedViewModel sharedViewModel = new ViewModelProvider(this).get(SharedViewModel.class); 21 | sharedViewModel.mTitle.observe(this, this::setTitle); 22 | } 23 | 24 | @Override 25 | protected void onResume() { 26 | super.onResume(); 27 | startNotificationService(); 28 | } 29 | 30 | public void startPreferenceFragment(Fragment fragment) { 31 | getSupportFragmentManager().beginTransaction() 32 | .replace(R.id.fragment_container_view, fragment, null) 33 | .setReorderingAllowed(true) 34 | .addToBackStack(fragment.getClass().getSimpleName()) 35 | .commit(); 36 | } 37 | 38 | 39 | private void startNotificationService() { 40 | Intent notificationService = new Intent(this, NotificationService.class); 41 | startService(notificationService); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/viewcontroller/injection/GodModeInjector.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.viewcontroller.injection; 2 | 3 | import de.robv.android.xposed.IXposedHookLoadPackage; 4 | import de.robv.android.xposed.callbacks.XC_LoadPackage; 5 | import tiiehenry.viewcontroller.BuildConfig; 6 | import tiiehenry.viewcontroller.injection.injector.InjectorImpl; 7 | import tiiehenry.viewcontroller.injection.injector.InjectorImplAndroid; 8 | import tiiehenry.viewcontroller.injection.injector.InjectorImplApps; 9 | import tiiehenry.viewcontroller.injection.injector.InjectorImplManager; 10 | 11 | public final class GodModeInjector implements IXposedHookLoadPackage { 12 | 13 | public static InjectorImpl injectorImpl; 14 | 15 | @Override 16 | public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable { 17 | if (!loadPackageParam.isFirstApplication) { 18 | return; 19 | } 20 | 21 | switch (loadPackageParam.packageName) { 22 | case "android"://Run in system process 23 | GodModeInjector.injectorImpl = new InjectorImplAndroid(loadPackageParam); 24 | break; 25 | case BuildConfig.APPLICATION_ID://Run in God's management process 26 | GodModeInjector.injectorImpl = new InjectorImplManager(loadPackageParam); 27 | break; 28 | default://Run in other application processes 29 | GodModeInjector.injectorImpl = new InjectorImplApps(loadPackageParam); 30 | } 31 | GodModeInjector.injectorImpl.handleLoadPackage(); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/viewcontroller/injection/bridge/ManagerObserver.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.viewcontroller.injection.bridge; 2 | 3 | import android.os.Handler; 4 | import android.os.Looper; 5 | import android.os.Message; 6 | 7 | import tiiehenry.viewcontroller.IObserver; 8 | import tiiehenry.viewcontroller.injection.injector.InjectorImplApps; 9 | import tiiehenry.viewcontroller.rule.ActRules; 10 | 11 | 12 | /** 13 | * Created by jrsen on 17-10-18. 14 | */ 15 | 16 | public final class ManagerObserver extends IObserver.Stub implements Handler.Callback { 17 | 18 | private final Handler mHandler = new Handler(Looper.getMainLooper(), this); 19 | private static final int ACTION_EDIT_MODE_CHANGED = 0; 20 | private static final int ACTION_VIEW_RULES_CHANGED = 1; 21 | private static final int ACTION_APP_STATUS_CHANGED = 2; 22 | private final InjectorImplApps injectorImplApps; 23 | 24 | public ManagerObserver(InjectorImplApps injectorImplApps) { 25 | this.injectorImplApps = injectorImplApps; 26 | } 27 | 28 | @Override 29 | public void onEditModeChanged(boolean enable) { 30 | mHandler.obtainMessage(ACTION_EDIT_MODE_CHANGED, enable).sendToTarget(); 31 | } 32 | 33 | @Override 34 | public void onViewRuleChanged(String packageName, ActRules actRules) { 35 | mHandler.obtainMessage(ACTION_VIEW_RULES_CHANGED,actRules).sendToTarget(); 36 | } 37 | 38 | @Override 39 | public void onAppStatusChanged(boolean enable) { 40 | mHandler.obtainMessage(ACTION_APP_STATUS_CHANGED, enable).sendToTarget(); 41 | } 42 | 43 | @Override 44 | public boolean handleMessage(Message msg) { 45 | if (msg.what == ACTION_EDIT_MODE_CHANGED) { 46 | injectorImplApps.notifyEditModeChanged((Boolean) msg.obj); 47 | } else if (msg.what == ACTION_VIEW_RULES_CHANGED) { 48 | injectorImplApps.notifyViewRulesChanged((ActRules) msg.obj); 49 | } else if (msg.what == ACTION_APP_STATUS_CHANGED) { 50 | injectorImplApps.notifyAppStatusChanged((Boolean) msg.obj); 51 | } 52 | return true; 53 | } 54 | 55 | } 56 | 57 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/viewcontroller/injection/control/ViewCompat.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.viewcontroller.injection.control; 2 | 3 | import android.graphics.drawable.Drawable; 4 | import android.os.Build; 5 | import android.view.View; 6 | 7 | import de.robv.android.xposed.XposedHelpers; 8 | 9 | public final class ViewCompat { 10 | 11 | public static void setVisibility(View view, int visibility) { 12 | try { 13 | view.setVisibility(visibility); 14 | } catch (Exception e) { 15 | // 已知有些控件重写该方法禁止设置控件的显示 16 | // eg:https://cs.android.com/android/platform/superproject/+/master:packages/apps/Dialer/java/com/android/dialer/widget/DialerFloatingActionButton.java;l=74?q=DialerFloatingActionButton 17 | XposedHelpers.callMethod(view, "setFlags", visibility, 0x0000000C); 18 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { 19 | Drawable background = view.getBackground(); 20 | if (background != null) background.setVisible(visibility == View.VISIBLE, false); 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/viewcontroller/injection/control/ViewHelper.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.viewcontroller.injection.control; 2 | 3 | import static tiiehenry.viewcontroller.GodModeApplication.TAG; 4 | 5 | import android.app.Activity; 6 | import android.content.Context; 7 | import android.content.ContextWrapper; 8 | import android.content.pm.PackageInfo; 9 | import android.content.pm.PackageManager; 10 | import android.content.res.Resources; 11 | import android.graphics.Rect; 12 | import android.text.TextUtils; 13 | import android.view.View; 14 | import android.view.ViewGroup; 15 | import android.view.ViewParent; 16 | import android.widget.TextView; 17 | 18 | import tiiehenry.viewcontroller.BuildConfig; 19 | import tiiehenry.viewcontroller.injection.util.Logger; 20 | import tiiehenry.viewcontroller.rule.ViewRule; 21 | import tiiehenry.viewcontroller.util.DisplayUtils; 22 | import tiiehenry.viewcontroller.util.Preconditions; 23 | 24 | import java.lang.ref.WeakReference; 25 | import java.util.ArrayList; 26 | import java.util.List; 27 | import java.util.Objects; 28 | 29 | import de.robv.android.xposed.XposedHelpers; 30 | 31 | /** 32 | * Created by jrsen on 17-10-13. 33 | */ 34 | 35 | public final class ViewHelper { 36 | 37 | public static final String TAG_GM_CMP = "gm_cmp"; 38 | 39 | public static List> buildViewNodes(View view) { 40 | ArrayList> views = new ArrayList<>(); 41 | if (view.getVisibility() == View.VISIBLE && !TAG_GM_CMP.equals(view.getTag())) { 42 | views.add(new WeakReference<>(view)); 43 | if (view instanceof ViewGroup) { 44 | final int N = ((ViewGroup) view).getChildCount(); 45 | for (int i = 0; i < N; i++) { 46 | View childView = ((ViewGroup) view).getChildAt(i); 47 | views.addAll(buildViewNodes(childView)); 48 | } 49 | } 50 | } 51 | return views; 52 | } 53 | 54 | public static Rect getLocationInWindow(View v) { 55 | int[] out = new int[2]; 56 | v.getLocationInWindow(out); 57 | int l = out[0]; 58 | int t = out[1]; 59 | int r = l + v.getWidth(); 60 | int b = t + v.getHeight(); 61 | return new Rect(l, t, r, b); 62 | } 63 | 64 | public static Rect getLocationOnScreen(View v) { 65 | int[] out = new int[2]; 66 | v.getLocationOnScreen(out); 67 | int l = out[0]; 68 | int t = out[1]; 69 | int r = l + v.getWidth(); 70 | int b = t + v.getHeight(); 71 | return new Rect(l, t, r, b); 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/viewcontroller/injection/hook/DisplayPropertiesHook.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.viewcontroller.injection.hook; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.os.Build; 5 | 6 | import androidx.annotation.RequiresApi; 7 | 8 | import tiiehenry.viewcontroller.injection.util.Logger; 9 | import tiiehenry.viewcontroller.injection.util.Property; 10 | 11 | import java.util.Optional; 12 | 13 | import de.robv.android.xposed.XC_MethodHook; 14 | import de.robv.android.xposed.XposedHelpers; 15 | 16 | import static tiiehenry.viewcontroller.GodModeApplication.TAG; 17 | 18 | public final class DisplayPropertiesHook extends XC_MethodHook implements Property.OnPropertyChangeListener { 19 | 20 | private boolean mDebugLayout; 21 | 22 | @RequiresApi(api = Build.VERSION_CODES.N) 23 | @Override 24 | protected void beforeHookedMethod(MethodHookParam param) { 25 | if (mDebugLayout) { 26 | param.setResult(Optional.of(true)); 27 | } 28 | } 29 | 30 | @Override 31 | public void onPropertyChange(Boolean debugLayout) { 32 | mDebugLayout = debugLayout; 33 | try { 34 | // @SuppressLint("PrivateApi") Class DisplayPropertiesClass = Class.forName("android.sysprop.DisplayProperties"); 35 | // XposedHelpers.callStaticMethod(DisplayPropertiesClass, "debug_layout",debugLayout); 36 | @SuppressLint("PrivateApi") Class SystemPropertiesClass = Class.forName("android.os.SystemProperties"); 37 | XposedHelpers.callStaticMethod(SystemPropertiesClass, "callChangeCallbacks"); 38 | } catch (ClassNotFoundException e) { 39 | Logger.e(TAG, "invoke callChangeCallbacks fail", e); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/viewcontroller/injection/hook/SystemPropertiesHook.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.viewcontroller.injection.hook; 2 | 3 | 4 | import android.annotation.SuppressLint; 5 | 6 | import tiiehenry.viewcontroller.injection.util.Logger; 7 | import tiiehenry.viewcontroller.injection.util.Property; 8 | 9 | import de.robv.android.xposed.XC_MethodHook; 10 | import de.robv.android.xposed.XposedHelpers; 11 | 12 | import static tiiehenry.viewcontroller.GodModeApplication.TAG; 13 | 14 | public final class SystemPropertiesHook extends XC_MethodHook implements Property.OnPropertyChangeListener { 15 | 16 | private boolean mDebugLayout; 17 | 18 | @Override 19 | protected void beforeHookedMethod(MethodHookParam param) { 20 | if (mDebugLayout && "debug.layout".equals(param.args[0])) { 21 | param.setResult(true); 22 | } 23 | } 24 | 25 | @Override 26 | public void onPropertyChange(Boolean debugLayout) { 27 | mDebugLayout = debugLayout; 28 | try { 29 | @SuppressLint("PrivateApi") Class SystemPropertiesClass = Class.forName("android.os.SystemProperties"); 30 | XposedHelpers.callStaticMethod(SystemPropertiesClass, "callChangeCallbacks"); 31 | } catch (ClassNotFoundException e) { 32 | Logger.e(TAG, "invoke callChangeCallbacks fail", e); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/viewcontroller/injection/injector/InjectorImpl.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.viewcontroller.injection.injector; 2 | 3 | import de.robv.android.xposed.callbacks.XC_LoadPackage; 4 | 5 | public abstract class InjectorImpl { 6 | public final XC_LoadPackage.LoadPackageParam loadPackageParam; 7 | 8 | public InjectorImpl(XC_LoadPackage.LoadPackageParam loadPackageParam) { 9 | this.loadPackageParam = loadPackageParam; 10 | } 11 | 12 | public abstract void handleLoadPackage() throws Throwable; 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/viewcontroller/injection/injector/InjectorImplAndroid.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.viewcontroller.injection.injector; 2 | 3 | import static tiiehenry.viewcontroller.GodModeApplication.TAG; 4 | 5 | import android.content.Context; 6 | import android.os.Binder; 7 | 8 | import tiiehenry.viewcontroller.injection.util.Logger; 9 | import tiiehenry.viewcontroller.service.GodModeManagerService; 10 | import com.kaisar.xservicemanager.XServiceManager; 11 | 12 | import de.robv.android.xposed.callbacks.XC_LoadPackage; 13 | 14 | //Run in system process 15 | public class InjectorImplAndroid extends InjectorImpl { 16 | public InjectorImplAndroid(XC_LoadPackage.LoadPackageParam loadPackageParam) { 17 | super(loadPackageParam); 18 | } 19 | 20 | @Override 21 | public void handleLoadPackage() { 22 | Logger.d(TAG, "inject GodModeManagerService as system service."); 23 | XServiceManager.initForSystemServer(); 24 | XServiceManager.registerService("godmode", new XServiceManager.ServiceFetcher() { 25 | @Override 26 | public Binder createService(Context ctx) { 27 | return new GodModeManagerService(ctx); 28 | } 29 | }); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/viewcontroller/injection/injector/InjectorImplManager.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.viewcontroller.injection.injector; 2 | 3 | import android.content.Context; 4 | 5 | import tiiehenry.viewcontroller.util.XposedEnvironment; 6 | 7 | import de.robv.android.xposed.XC_MethodReplacement; 8 | import de.robv.android.xposed.XSharedPreferences; 9 | import de.robv.android.xposed.XposedHelpers; 10 | import de.robv.android.xposed.callbacks.XC_LoadPackage; 11 | 12 | // Run in God's management process 13 | public class InjectorImplManager extends InjectorImpl { 14 | public InjectorImplManager(XC_LoadPackage.LoadPackageParam loadPackageParam) { 15 | super(loadPackageParam); 16 | } 17 | 18 | @Override 19 | public void handleLoadPackage() { 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/viewcontroller/injection/util/FilePermissionUtils.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.viewcontroller.injection.util; 2 | 3 | import java.io.File; 4 | import java.lang.reflect.InvocationTargetException; 5 | import java.lang.reflect.Method; 6 | 7 | 8 | public final class FilePermissionUtils { 9 | 10 | public static final int S_IRWXU = 00700; 11 | public static final int S_IRUSR = 00400; 12 | public static final int S_IWUSR = 00200; 13 | public static final int S_IXUSR = 00100; 14 | 15 | public static final int S_IRWXG = 00070; 16 | public static final int S_IRGRP = 00040; 17 | public static final int S_IWGRP = 00020; 18 | public static final int S_IXGRP = 00010; 19 | 20 | public static final int S_IRWXO = 00007; 21 | public static final int S_IROTH = 00004; 22 | public static final int S_IWOTH = 00002; 23 | public static final int S_IXOTH = 00001; 24 | 25 | public static boolean canRead(String filepath) { 26 | return new File(filepath).canRead(); 27 | } 28 | 29 | 30 | /** 31 | * Set owner and mode of of given {@link File}. 32 | * 33 | * @param mode to apply through {@code chmod} 34 | * @param uid to apply through {@code chown}, or -1 to leave unchanged 35 | * @param gid to apply through {@code chown}, or -1 to leave unchanged 36 | * @return 0 on success, otherwise errno. 37 | */ 38 | public static int setPermissions(File path, int mode, int uid, int gid) { 39 | return setPermissions(path.getAbsolutePath(), mode, uid, gid); 40 | } 41 | 42 | /** 43 | * Set owner and mode of of given path. 44 | * 45 | * @param mode to apply through {@code chmod} 46 | * @param uid to apply through {@code chown}, or -1 to leave unchanged 47 | * @param gid to apply through {@code chown}, or -1 to leave unchanged 48 | * @return 0 on success, otherwise errno. 49 | */ 50 | public static int setPermissions(String path, int mode, int uid, int gid) { 51 | try { 52 | Class FileUtilsClass = Class.forName("android.os.FileUtils"); 53 | Method setPermissionsMethod = FileUtilsClass.getDeclaredMethod("setPermissions", String.class, int.class, int.class, int.class); 54 | return (int) setPermissionsMethod.invoke(null, path, mode, uid, gid); 55 | } catch (ClassNotFoundException e) { 56 | e.printStackTrace(); 57 | } catch (NoSuchMethodException e) { 58 | e.printStackTrace(); 59 | } catch (IllegalAccessException e) { 60 | e.printStackTrace(); 61 | } catch (InvocationTargetException e) { 62 | e.printStackTrace(); 63 | } 64 | return -1; 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/viewcontroller/injection/util/GmLayoutInflater.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.viewcontroller.injection.util; 2 | 3 | import android.content.Context; 4 | import android.content.pm.PackageManager; 5 | import android.util.AttributeSet; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | 9 | import androidx.annotation.NonNull; 10 | import androidx.annotation.Nullable; 11 | 12 | import tiiehenry.viewcontroller.BuildConfig; 13 | 14 | import java.lang.reflect.InvocationTargetException; 15 | 16 | public class GmLayoutInflater implements LayoutInflater.Factory2 { 17 | 18 | public static LayoutInflater from(Context context) throws PackageManager.NameNotFoundException { 19 | Context gmContext = context.createPackageContext(BuildConfig.APPLICATION_ID, 0); 20 | LayoutInflater layoutInflater = LayoutInflater.from(gmContext); 21 | GmLayoutInflater factory = new GmLayoutInflater(); 22 | layoutInflater.setFactory2(factory); 23 | return layoutInflater; 24 | } 25 | 26 | @Nullable 27 | @Override 28 | public View onCreateView(@Nullable View parent, @NonNull String name, @NonNull Context context, @NonNull AttributeSet attrs) { 29 | try { 30 | return (View) Class.forName(name).getConstructor(Context.class, AttributeSet.class).newInstance(context, attrs); 31 | } catch (IllegalAccessException e) { 32 | e.printStackTrace(); 33 | } catch (InstantiationException e) { 34 | e.printStackTrace(); 35 | } catch (InvocationTargetException e) { 36 | e.printStackTrace(); 37 | } catch (NoSuchMethodException e) { 38 | e.printStackTrace(); 39 | } catch (ClassNotFoundException e) { 40 | e.printStackTrace(); 41 | } 42 | return null; 43 | } 44 | 45 | @Nullable 46 | @Override 47 | public View onCreateView(@NonNull String name, @NonNull Context context, @NonNull AttributeSet attrs) { 48 | try { 49 | return (View) Class.forName(name).getConstructor(Context.class, AttributeSet.class).newInstance(context, attrs); 50 | } catch (IllegalAccessException e) { 51 | e.printStackTrace(); 52 | } catch (InstantiationException e) { 53 | e.printStackTrace(); 54 | } catch (InvocationTargetException e) { 55 | e.printStackTrace(); 56 | } catch (NoSuchMethodException e) { 57 | e.printStackTrace(); 58 | } catch (ClassNotFoundException e) { 59 | e.printStackTrace(); 60 | } 61 | return null; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/viewcontroller/injection/util/GmResources.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.viewcontroller.injection.util; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.content.pm.PackageManager; 6 | import android.content.res.Resources; 7 | import android.graphics.drawable.Drawable; 8 | 9 | import tiiehenry.viewcontroller.BuildConfig; 10 | 11 | public class GmResources { 12 | 13 | private static Resources GMResources; 14 | 15 | private static Resources getGmResource(Context context) throws PackageManager.NameNotFoundException { 16 | if (GMResources == null) { 17 | GMResources = context.createPackageContext(BuildConfig.APPLICATION_ID, 0).getResources(); 18 | } 19 | return GMResources; 20 | } 21 | 22 | public static int getColor(Context context, int id) throws Resources.NotFoundException { 23 | try { 24 | return getGmResource(context).getColor(id); 25 | } catch (PackageManager.NameNotFoundException e) { 26 | throw new Resources.NotFoundException("get resources fail GodMode package may be not installed?"); 27 | } 28 | } 29 | 30 | @SuppressLint("UseCompatLoadingForDrawables") 31 | public static Drawable getDrawable(Context context, int id) throws Resources.NotFoundException { 32 | try { 33 | return getGmResource(context).getDrawable(id); 34 | } catch (PackageManager.NameNotFoundException e) { 35 | throw new Resources.NotFoundException("get resources fail GodMode package may be not installed?"); 36 | } 37 | } 38 | 39 | public static CharSequence getText(Context context, int id) throws Resources.NotFoundException { 40 | try { 41 | return getGmResource(context).getText(id); 42 | } catch (PackageManager.NameNotFoundException e) { 43 | throw new Resources.NotFoundException("get resources fail GodMode package may be not installed?"); 44 | } 45 | } 46 | 47 | public static String getString(Context context, int id) throws Resources.NotFoundException { 48 | try { 49 | return getGmResource(context).getString(id); 50 | } catch (PackageManager.NameNotFoundException e) { 51 | throw new Resources.NotFoundException("get resources fail GodMode package may be not installed?"); 52 | } 53 | } 54 | 55 | public static String getString(Context context, int id, Object... formatArgs) throws Resources.NotFoundException { 56 | try { 57 | return getGmResource(context).getString(id, formatArgs); 58 | } catch (PackageManager.NameNotFoundException e) { 59 | throw new Resources.NotFoundException("get resources fail GodMode package may be not installed?"); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/viewcontroller/injection/util/Logger.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.viewcontroller.injection.util; 2 | 3 | import android.util.Log; 4 | 5 | import androidx.annotation.Keep; 6 | 7 | /** 8 | * Created by jrsen on 17-10-21. 9 | */ 10 | 11 | @Keep 12 | public final class Logger { 13 | 14 | private static final String TAG = "GodMode"; 15 | 16 | public static int v(String tag, String msg) { 17 | return isLoggable(tag, Log.VERBOSE) ? Log.v(tag, msg) : 0; 18 | } 19 | 20 | public static int v(String tag, String msg, Throwable tr) { 21 | return isLoggable(tag, Log.VERBOSE) ? Log.v(tag, msg, tr) : 0; 22 | } 23 | 24 | public static int d(String tag, String msg) { 25 | return isLoggable(tag, Log.DEBUG) ? Log.d(tag, msg) : 0; 26 | } 27 | 28 | public static int d(String tag, String msg, Throwable tr) { 29 | return isLoggable(tag, Log.DEBUG) ? Log.d(tag, msg, tr) : 0; 30 | } 31 | 32 | public static int i(String tag, String msg) { 33 | return isLoggable(tag, Log.INFO) ? Log.i(tag, msg) : 0; 34 | } 35 | 36 | public static int i(String tag, String msg, Throwable tr) { 37 | return isLoggable(tag, Log.INFO) ? Log.i(tag, msg, tr) : 0; 38 | } 39 | 40 | public static int w(String tag, String msg) { 41 | return isLoggable(tag, Log.WARN) ? Log.w(tag, msg) : 0; 42 | } 43 | 44 | public static int w(String tag, String msg, Throwable tr) { 45 | return isLoggable(tag, Log.WARN) ? Log.w(tag, msg, tr) : 0; 46 | } 47 | 48 | public static int e(String tag, String msg) { 49 | return isLoggable(tag, Log.ERROR) ? Log.e(tag, msg) : 0; 50 | } 51 | 52 | public static int e(String tag, String msg, Throwable tr) { 53 | return isLoggable(tag, Log.ERROR) ? Log.e(tag, msg, tr) : 0; 54 | } 55 | 56 | public static String getStackTraceString(Throwable tr) { 57 | return Log.getStackTraceString(tr); 58 | } 59 | 60 | public static boolean isLoggable(String tag, int level) { 61 | return Log.isLoggable(TAG, level) || Log.isLoggable(tag, level); 62 | } 63 | 64 | private final String mName; 65 | 66 | private Logger(String tag) { 67 | this.mName = tag; 68 | } 69 | 70 | public void d(String message) { 71 | if (isLoggable(TAG, Log.DEBUG)) d(TAG, String.format("[%s] %s", mName, message)); 72 | } 73 | 74 | public void i(String message) { 75 | if (isLoggable(TAG, Log.INFO)) i(TAG, String.format("[%s] %s", mName, message)); 76 | } 77 | 78 | public void w(String message) { 79 | if (isLoggable(TAG, Log.WARN)) w(TAG, String.format("[%s] %s", mName, message)); 80 | } 81 | 82 | public void w(String message, Throwable tr) { 83 | if (isLoggable(TAG, Log.WARN)) w(TAG, String.format("[%s] %s", mName, message), tr); 84 | } 85 | 86 | public void e(String message) { 87 | if (isLoggable(TAG, Log.ERROR)) e(mName, String.format("[%s] %s", mName, message)); 88 | } 89 | 90 | public void e(String message, Throwable tr) { 91 | if (isLoggable(TAG, Log.ERROR)) e(mName, String.format("[%s] %s", mName, message), tr); 92 | } 93 | 94 | public static Logger getLogger(String name) { 95 | return new Logger(name); 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/viewcontroller/injection/util/PackageManagerUtils.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.viewcontroller.injection.util; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Intent; 5 | import android.content.pm.PackageInfo; 6 | import android.content.pm.ResolveInfo; 7 | import android.os.Build; 8 | import android.os.IBinder; 9 | import android.os.IInterface; 10 | 11 | import java.util.Collections; 12 | import java.util.List; 13 | 14 | import de.robv.android.xposed.XposedHelpers; 15 | 16 | public final class PackageManagerUtils { 17 | 18 | private static IInterface packageService; 19 | 20 | private static Object getIPackageManager() { 21 | if (packageService == null) { 22 | try { 23 | @SuppressLint("PrivateApi") Class ServiceManagerClass = Class.forName("android.os.ServiceManager"); 24 | IBinder binder = (IBinder) XposedHelpers.callStaticMethod(ServiceManagerClass, "checkService", "package"); 25 | @SuppressLint("PrivateApi") Class IPackageManager$StubClass = Class.forName("android.content.pm.IPackageManager$Stub"); 26 | packageService = (IInterface) XposedHelpers.callStaticMethod(IPackageManager$StubClass, "asInterface", binder); 27 | } catch (ClassNotFoundException e) { 28 | throw new RuntimeException(e); 29 | } 30 | } 31 | return packageService; 32 | } 33 | 34 | @SuppressWarnings("unchecked") 35 | public static List queryIntentActivities(Intent intent, String type, int flags, int userId) { 36 | Object list = XposedHelpers.callMethod(getIPackageManager(), "queryIntentActivities", intent, type, flags, userId); 37 | if (list == null) { 38 | return Collections.emptyList(); 39 | } 40 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { 41 | return (List) list; 42 | } else { 43 | return (List) XposedHelpers.callMethod(list, "getList"); 44 | } 45 | } 46 | 47 | @SuppressWarnings("unchecked") 48 | public static List queryIntentServices(Intent intent, String type, int flags, int userId) { 49 | Object list = XposedHelpers.callMethod(getIPackageManager(), "queryIntentServices", intent, type, flags, userId); 50 | if (list == null) { 51 | return Collections.emptyList(); 52 | } 53 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { 54 | return (List) list; 55 | } else { 56 | return (List) XposedHelpers.callMethod(list, "getList"); 57 | } 58 | } 59 | 60 | public static ResolveInfo resolveIntent(Intent intent, String type, int flags, int userId) { 61 | return (ResolveInfo) XposedHelpers.callMethod(getIPackageManager(), "resolveIntent", intent, type, flags, userId); 62 | } 63 | 64 | public static ResolveInfo resolveService(Intent intent, String type, int flags, int userId) { 65 | return (ResolveInfo) XposedHelpers.callMethod(getIPackageManager(), "resolveService", intent, type, flags, userId); 66 | } 67 | 68 | public static PackageInfo getPackageInfo(String packageName, int flags, int userId) { 69 | return (PackageInfo) XposedHelpers.callMethod(getIPackageManager(), "getPackageInfo", packageName, flags, userId); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/viewcontroller/injection/util/Property.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.viewcontroller.injection.util; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * Created by jrsen on 17-10-18. 8 | */ 9 | 10 | public final class Property { 11 | 12 | private V v; 13 | private final List> listeners; 14 | 15 | { 16 | listeners = new ArrayList<>(); 17 | } 18 | 19 | public Property() { 20 | } 21 | 22 | public Property(V v) { 23 | this.v = v; 24 | } 25 | 26 | public void set(V v) { 27 | if (this.v != v) { 28 | this.v = v; 29 | notifyPropertyHasChanged(v); 30 | } 31 | } 32 | 33 | public V get() { 34 | return v; 35 | } 36 | 37 | private void notifyPropertyHasChanged(V v) { 38 | //不知道为什么synchronized老是出问题 39 | ArrayList> listeners = new ArrayList<>(this.listeners); 40 | final int N = listeners.size(); 41 | for (int i = 0; i < N; i++) { 42 | listeners.get(i).onPropertyChange(v); 43 | } 44 | } 45 | 46 | public void addOnPropertyChangeListener(OnPropertyChangeListener listener) { 47 | listeners.add(listener); 48 | } 49 | 50 | public void removeOnPropertyChangeListener(OnPropertyChangeListener listener) { 51 | listeners.remove(listener); 52 | } 53 | 54 | public interface OnPropertyChangeListener { 55 | void onPropertyChange(V v); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/viewcontroller/injection/weiget/Particle.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.viewcontroller.injection.weiget; 2 | 3 | import android.graphics.Bitmap; 4 | import android.graphics.Point; 5 | import android.graphics.Rect; 6 | 7 | import java.util.Random; 8 | 9 | /** 10 | * Created by jrsen on 17-10-13. 11 | */ 12 | 13 | public final class Particle { 14 | //默认小球宽高 15 | public static final int PART_WH = 8; 16 | //x值 17 | public float cx; 18 | //y值 19 | public float cy; 20 | //绘制圆的半径 21 | public float radius; 22 | //颜色 23 | public int color; 24 | //透明度 25 | public float alpha; 26 | //用于生成随机数 27 | static Random random = new Random(); 28 | //粒子所在的矩形区域 29 | public Rect mBound; 30 | 31 | public static Particle generateParticle(int color, Rect bound, Point point) { 32 | int row = point.y; //行是高 33 | int column = point.x; //列是宽 34 | 35 | Particle particle = new Particle(); 36 | particle.mBound = bound; 37 | particle.color = color; 38 | particle.alpha = 1f; 39 | 40 | particle.radius = PART_WH; 41 | particle.cx = bound.left + PART_WH * column; 42 | particle.cy = bound.top + PART_WH * row; 43 | 44 | return particle; 45 | } 46 | 47 | public void update(float factor) { 48 | cx = cx + factor * random.nextInt(mBound.width()) * (random.nextFloat() - 0.5f); 49 | 50 | cy = cy + factor * (mBound.height() / (random.nextInt(4) + 1)); 51 | 52 | radius = radius - factor * random.nextInt(3); 53 | 54 | if (radius <= 0) 55 | radius = 0; 56 | alpha = 1f - factor; 57 | } 58 | 59 | public static Particle[][] generateParticles(Bitmap bitmap, Rect bound) { 60 | int w = bound.width(); 61 | int h = bound.height(); 62 | 63 | int partW_Count = w / Particle.PART_WH; 64 | int partH_Count = h / Particle.PART_WH; 65 | 66 | Particle[][] particles = new Particle[partH_Count][partW_Count]; 67 | Point point = null; 68 | for (int row = 0; row < partH_Count; row++) { //行 69 | for (int column = 0; column < partW_Count; column++) { //列 70 | //取得当前粒子所在位置的颜色 71 | int color = bitmap.getPixel(column * Particle.PART_WH, row * Particle.PART_WH); 72 | 73 | point = new Point(column, row); //x是列,y是行 74 | 75 | particles[row][column] = Particle.generateParticle(color, bound, point); 76 | } 77 | } 78 | return particles; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/viewcontroller/repository/RemoteRepository.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.viewcontroller.repository; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.GsonBuilder; 5 | 6 | import java.util.Map; 7 | 8 | import retrofit2.Call; 9 | import retrofit2.Callback; 10 | import retrofit2.Retrofit; 11 | import retrofit2.converter.gson.GsonConverterFactory; 12 | import retrofit2.http.GET; 13 | 14 | public class RemoteRepository { 15 | 16 | public static void fetchGroupInfo(Callback[]> cb) { 17 | Gson gson = new GsonBuilder() 18 | .setLenient() 19 | .create(); 20 | Retrofit retrofit = new Retrofit.Builder() 21 | .addConverterFactory(GsonConverterFactory.create(gson)) 22 | .baseUrl("https://gitee.com/TIIEHenry/xposed-god_mode_plus/raw/master/") 23 | .build(); 24 | RemoteService service = retrofit.create(RemoteService.class); 25 | Call[]> fetchGroupInfo = service.fetchGroupInfo(); 26 | fetchGroupInfo.enqueue(cb); 27 | } 28 | 29 | interface RemoteService { 30 | 31 | @GET("community.json") 32 | Call[]> fetchGroupInfo(); 33 | 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/viewcontroller/rule/AppRules.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.viewcontroller.rule; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | import androidx.annotation.Keep; 7 | 8 | import java.util.HashMap; 9 | import java.util.List; 10 | 11 | /** 12 | * Created by jrsen on 17-10-14. 13 | * package:[activityclass:viewrule] 14 | */ 15 | @Keep 16 | public final class AppRules extends HashMap implements Parcelable { 17 | 18 | public AppRules() { 19 | } 20 | 21 | 22 | protected AppRules(Parcel in) { 23 | HashMap hashMap = in.readHashMap(getClass().getClassLoader()); 24 | for (Entry entry : hashMap.entrySet()) { 25 | String key = (String) entry.getKey(); 26 | @SuppressWarnings("unchecked") HashMap> value = (HashMap>) entry.getValue(); 27 | ActRules actRules = new ActRules(value.size()); 28 | actRules.putAll(value); 29 | put(key, actRules); 30 | } 31 | } 32 | 33 | @Override 34 | public void writeToParcel(Parcel dest, int flags) { 35 | dest.writeMap(this); 36 | } 37 | 38 | @Override 39 | public int describeContents() { 40 | return 0; 41 | } 42 | 43 | public static final Creator CREATOR = new Creator() { 44 | @Override 45 | public AppRules createFromParcel(Parcel in) { 46 | return new AppRules(in); 47 | } 48 | 49 | @Override 50 | public AppRules[] newArray(int size) { 51 | return new AppRules[size]; 52 | } 53 | }; 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/viewcontroller/service/ObserverProxy.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.viewcontroller.service; 2 | 3 | import android.os.IBinder; 4 | import android.os.RemoteException; 5 | 6 | import tiiehenry.viewcontroller.IObserver; 7 | import tiiehenry.viewcontroller.rule.ActRules; 8 | 9 | public class ObserverProxy implements IObserver { 10 | 11 | private final String packageName; 12 | private final IObserver observer; 13 | 14 | public ObserverProxy(String packageName, IObserver observer) { 15 | this.packageName = packageName; 16 | this.observer = observer; 17 | } 18 | 19 | public String getPackageName() { 20 | return packageName; 21 | } 22 | 23 | @Override 24 | public void onEditModeChanged(boolean enable) throws RemoteException { 25 | observer.onEditModeChanged(enable); 26 | } 27 | 28 | @Override 29 | public void onAppStatusChanged(boolean enable) throws RemoteException { 30 | observer.onAppStatusChanged(enable); 31 | } 32 | 33 | @Override 34 | public void onViewRuleChanged(String packageName, ActRules actRules) throws RemoteException { 35 | observer.onViewRuleChanged(packageName, actRules); 36 | } 37 | 38 | @Override 39 | public IBinder asBinder() { 40 | return observer.asBinder(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/viewcontroller/util/Clipboard.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.viewcontroller.util; 2 | 3 | import android.content.ClipData; 4 | import android.content.ClipboardManager; 5 | import android.content.Context; 6 | 7 | import java.util.Objects; 8 | 9 | /** 10 | * Created by jrsen on 17-9-29. 11 | */ 12 | 13 | public final class Clipboard { 14 | 15 | public static boolean putContent(Context context, CharSequence text) { 16 | try { 17 | ClipboardManager clipboard = Objects.requireNonNull((ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE)); 18 | ClipData clip = ClipData.newPlainText(text, text); 19 | clipboard.setPrimaryClip(clip); 20 | return true; 21 | } catch (Throwable ignore) { 22 | //因为堆栈溢出trace信息很大无法set到剪切板 23 | // e.printStackTrace(); 24 | return false; 25 | } 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/viewcontroller/util/CommandUtil.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.viewcontroller.util; 2 | 3 | import java.io.BufferedOutputStream; 4 | import java.io.IOException; 5 | import java.io.PrintStream; 6 | 7 | public class CommandUtil { 8 | 9 | 10 | /** 11 | * @param: [command] 12 | * @return: java.lang.String 13 | */ 14 | public static Process su() throws IOException { 15 | //等待命令执行完成 16 | return Runtime.getRuntime().exec("su"); 17 | } 18 | 19 | public static void run(Process process, String command) throws IOException { 20 | PrintStream outputStream = new PrintStream(new BufferedOutputStream(process.getOutputStream(), 8192)); 21 | outputStream.println(command); 22 | outputStream.flush(); 23 | outputStream.close(); 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/viewcontroller/util/DisplayUtils.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.viewcontroller.util; 2 | 3 | import android.content.Context; 4 | import android.content.res.Resources; 5 | import android.util.DisplayMetrics; 6 | import android.view.WindowManager; 7 | 8 | public class DisplayUtils { 9 | /** 10 | * 将px值转换为dp值 11 | */ 12 | public static int px2dp(Context context, float pxValue) { 13 | final float scale = context.getResources().getDisplayMetrics().density; 14 | return (int) (pxValue / scale + 0.5f); 15 | } 16 | 17 | /** 18 | * 将px值转换为dp值 19 | */ 20 | public static int px2dp(Resources resources, float pxValue) { 21 | final float scale = resources.getDisplayMetrics().density; 22 | return (int) (pxValue / scale + 0.5f); 23 | } 24 | 25 | /** 26 | * 将dp值转换为px值 27 | */ 28 | public static int dp2px(Context context, float dpValue) { 29 | final float scale = context.getResources().getDisplayMetrics().density; 30 | return (int) (dpValue * scale + 0.5f); 31 | } 32 | 33 | /** 34 | * 将dp值转换为px值 35 | */ 36 | public static int dp2px(Resources resources, float dpValue) { 37 | final float scale = resources.getDisplayMetrics().density; 38 | return (int) (dpValue * scale + 0.5f); 39 | } 40 | 41 | /** 42 | * 将dp值转换为px值 43 | */ 44 | public static int dp2px(float scale , float dpValue) { 45 | return (int) (dpValue * scale + 0.5f); 46 | } 47 | 48 | /** 49 | * 获取屏幕宽度 50 | */ 51 | public static int getScreenWidthPixels(WindowManager windowManager) { 52 | DisplayMetrics metric = new DisplayMetrics(); 53 | windowManager.getDefaultDisplay().getMetrics(metric); 54 | return metric.widthPixels; 55 | } 56 | 57 | /** 58 | * 获取屏幕高度 59 | */ 60 | public static int getScreenHeightPixels(WindowManager windowManager) { 61 | DisplayMetrics metric = new DisplayMetrics(); 62 | windowManager.getDefaultDisplay().getMetrics(metric); 63 | return metric.heightPixels; 64 | } 65 | /** 66 | * 将dp值转换为px值,保证尺寸大小不变 67 | * 68 | * @return 69 | */ 70 | public static int dpAdapt(WindowManager windowManager, float dp, float widthDpBase) { 71 | DisplayMetrics dm = new DisplayMetrics(); 72 | windowManager.getDefaultDisplay().getMetrics(dm); 73 | int heightPixels = dm.heightPixels;//高的像素 74 | int widthPixels = dm.widthPixels;//宽的像素 75 | // int densityDpi = dm.densityDpi;//dpi 76 | // float xdpi = dm.xdpi;//xdpi 77 | // float ydpi = dm.ydpi;//ydpi 78 | float density = dm.density;//density=dpi/160,密度比 79 | // float scaledDensity = dm.scaledDensity;//scaledDensity=dpi/160 字体缩放密度比 80 | float heightDP = heightPixels / density;//高度的dp 81 | float widthDP = widthPixels / density;//宽度的dp 82 | float w = Math.min(widthDP, heightDP); 83 | // final float scale = activity.getResources().getDisplayMetrics().density; 84 | return (int) (dp * w / widthDpBase * density + 0.5f); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/viewcontroller/util/PermissionHelper.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.viewcontroller.util; 2 | 3 | import android.app.Activity; 4 | import android.content.pm.PackageManager; 5 | 6 | import androidx.core.app.ActivityCompat; 7 | import androidx.core.content.ContextCompat; 8 | 9 | import tiiehenry.viewcontroller.injection.util.Logger; 10 | 11 | import java.lang.ref.WeakReference; 12 | import java.util.ArrayList; 13 | import java.util.Objects; 14 | 15 | import static tiiehenry.viewcontroller.GodModeApplication.TAG; 16 | 17 | public class PermissionHelper { 18 | 19 | private final static int REQUEST_PERMISSION_CODE = 1; 20 | 21 | private final WeakReference mActivityReference; 22 | 23 | public PermissionHelper(Activity activity) { 24 | mActivityReference = new WeakReference<>(activity); 25 | } 26 | 27 | public void applyPermissions(String... permissions) { 28 | try { 29 | Activity activity = Objects.requireNonNull(mActivityReference.get(), "Activity can't be null"); 30 | ArrayList unauthorizedPermissionList = new ArrayList<>(); 31 | for (String permission : permissions) { 32 | if (ContextCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED) { 33 | unauthorizedPermissionList.add(permission); 34 | } 35 | } 36 | ActivityCompat.requestPermissions(activity, unauthorizedPermissionList.toArray(new String[0]), REQUEST_PERMISSION_CODE); 37 | } catch (Throwable e) { 38 | Logger.e(TAG, e.getMessage(), e); 39 | } 40 | } 41 | 42 | public boolean checkSelfPermission(String permission) { 43 | Activity activity = Objects.requireNonNull(mActivityReference.get(), "Activity can't be null"); 44 | return ContextCompat.checkSelfPermission(activity, permission) == PackageManager.PERMISSION_GRANTED; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/viewcontroller/util/ShareUtil.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.viewcontroller.util; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.net.Uri; 6 | import android.os.Build; 7 | 8 | import androidx.core.content.FileProvider; 9 | 10 | import java.io.File; 11 | 12 | public class ShareUtil { 13 | public static void share(Context context, File file) { 14 | Uri uri = getFileUri(context, file); 15 | Intent intent = new Intent(Intent.ACTION_SEND); 16 | intent.setType("application/zip"); 17 | intent.putExtra(Intent.EXTRA_STREAM, uri); 18 | intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); 19 | context.startActivity(Intent.createChooser(intent, "Share to ...")); 20 | } 21 | 22 | public static Uri getFileUri(Context context, File file) { 23 | Uri uri; 24 | // 低版本直接用 Uri.fromFile 25 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { 26 | uri = Uri.fromFile(file); 27 | } else { 28 | // 使用 FileProvider 会在某些 app 下不支持(在使用FileProvider 方式情况下QQ不能支持图片、视频分享,微信不支持视频分享) 29 | uri = FileProvider.getUriForFile(context, 30 | "tiiehenry.viewcontroller.fileProvider", 31 | file); 32 | // uri=Uri.parse("content://" + file.getAbsolutePath()); 33 | } 34 | return uri; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/tiiehenry/viewcontroller/widget/preference/ImageViewPreference.java: -------------------------------------------------------------------------------- 1 | package tiiehenry.viewcontroller.widget.preference; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.util.AttributeSet; 6 | import android.widget.ImageView; 7 | 8 | import androidx.preference.PreferenceViewHolder; 9 | 10 | import tiiehenry.viewcontroller.R; 11 | 12 | /** 13 | * Created by jrsen on 17-10-19. 14 | */ 15 | 16 | public final class ImageViewPreference extends androidx.preference.Preference { 17 | 18 | private ImageView mImageView; 19 | private Bitmap mBitmap; 20 | 21 | public ImageViewPreference(Context context, AttributeSet attrs) { 22 | super(context, attrs); 23 | init(); 24 | } 25 | 26 | public ImageViewPreference(Context context) { 27 | super(context); 28 | init(); 29 | } 30 | 31 | private void init() { 32 | setLayoutResource(R.layout.preference_image_preview); 33 | } 34 | 35 | @Override 36 | public void onBindViewHolder(PreferenceViewHolder holder) { 37 | super.onBindViewHolder(holder); 38 | mImageView = (ImageView) holder.findViewById(R.id.image); 39 | if (mBitmap != null) { 40 | mImageView.setImageBitmap(mBitmap); 41 | } 42 | } 43 | 44 | public void setImageBitmap(Bitmap bm) { 45 | mBitmap = bm; 46 | if (mImageView != null) { 47 | mImageView.setImageBitmap(bm); 48 | } 49 | } 50 | 51 | public void setImageResource(int resId) { 52 | if (mImageView != null) { 53 | mImageView.setImageResource(resId); 54 | } 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/res/anim/mddialogs_dialog_enter.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 12 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/anim/mddialogs_dialog_exit.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 14 | 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-anydpi/ic_donate.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-anydpi/ic_people.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-anydpi/ic_puzzle.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-anydpi/ic_setting.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/577fkj/GodMode/a3a74a7be1ec1778070871157be9412364ed55a7/app/src/main/res/drawable-hdpi/down.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/exchange.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/577fkj/GodMode/a3a74a7be1ec1778070871157be9412364ed55a7/app/src/main/res/drawable-hdpi/exchange.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_block.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/577fkj/GodMode/a3a74a7be1ec1778070871157be9412364ed55a7/app/src/main/res/drawable-hdpi/ic_block.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_donate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/577fkj/GodMode/a3a74a7be1ec1778070871157be9412364ed55a7/app/src/main/res/drawable-hdpi/ic_donate.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_people.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/577fkj/GodMode/a3a74a7be1ec1778070871157be9412364ed55a7/app/src/main/res/drawable-hdpi/ic_people.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_puzzle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/577fkj/GodMode/a3a74a7be1ec1778070871157be9412364ed55a7/app/src/main/res/drawable-hdpi/ic_puzzle.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_setting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/577fkj/GodMode/a3a74a7be1ec1778070871157be9412364ed55a7/app/src/main/res/drawable-hdpi/ic_setting.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/577fkj/GodMode/a3a74a7be1ec1778070871157be9412364ed55a7/app/src/main/res/drawable-hdpi/up.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/577fkj/GodMode/a3a74a7be1ec1778070871157be9412364ed55a7/app/src/main/res/drawable-mdpi/down.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/exchange.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/577fkj/GodMode/a3a74a7be1ec1778070871157be9412364ed55a7/app/src/main/res/drawable-mdpi/exchange.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_block.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/577fkj/GodMode/a3a74a7be1ec1778070871157be9412364ed55a7/app/src/main/res/drawable-mdpi/ic_block.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_donate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/577fkj/GodMode/a3a74a7be1ec1778070871157be9412364ed55a7/app/src/main/res/drawable-mdpi/ic_donate.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_people.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/577fkj/GodMode/a3a74a7be1ec1778070871157be9412364ed55a7/app/src/main/res/drawable-mdpi/ic_people.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_puzzle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/577fkj/GodMode/a3a74a7be1ec1778070871157be9412364ed55a7/app/src/main/res/drawable-mdpi/ic_puzzle.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_setting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/577fkj/GodMode/a3a74a7be1ec1778070871157be9412364ed55a7/app/src/main/res/drawable-mdpi/ic_setting.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/577fkj/GodMode/a3a74a7be1ec1778070871157be9412364ed55a7/app/src/main/res/drawable-mdpi/up.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/577fkj/GodMode/a3a74a7be1ec1778070871157be9412364ed55a7/app/src/main/res/drawable-xhdpi/down.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/exchange.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/577fkj/GodMode/a3a74a7be1ec1778070871157be9412364ed55a7/app/src/main/res/drawable-xhdpi/exchange.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_block.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/577fkj/GodMode/a3a74a7be1ec1778070871157be9412364ed55a7/app/src/main/res/drawable-xhdpi/ic_block.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_donate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/577fkj/GodMode/a3a74a7be1ec1778070871157be9412364ed55a7/app/src/main/res/drawable-xhdpi/ic_donate.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_people.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/577fkj/GodMode/a3a74a7be1ec1778070871157be9412364ed55a7/app/src/main/res/drawable-xhdpi/ic_people.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_puzzle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/577fkj/GodMode/a3a74a7be1ec1778070871157be9412364ed55a7/app/src/main/res/drawable-xhdpi/ic_puzzle.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_setting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/577fkj/GodMode/a3a74a7be1ec1778070871157be9412364ed55a7/app/src/main/res/drawable-xhdpi/ic_setting.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ripple_drawable_20dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/rounded_bg_bottom_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/rounded_bg_full.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/577fkj/GodMode/a3a74a7be1ec1778070871157be9412364ed55a7/app/src/main/res/drawable-xhdpi/up.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/577fkj/GodMode/a3a74a7be1ec1778070871157be9412364ed55a7/app/src/main/res/drawable-xxhdpi/down.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/exchange.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/577fkj/GodMode/a3a74a7be1ec1778070871157be9412364ed55a7/app/src/main/res/drawable-xxhdpi/exchange.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_block.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/577fkj/GodMode/a3a74a7be1ec1778070871157be9412364ed55a7/app/src/main/res/drawable-xxhdpi/ic_block.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_donate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/577fkj/GodMode/a3a74a7be1ec1778070871157be9412364ed55a7/app/src/main/res/drawable-xxhdpi/ic_donate.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_people.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/577fkj/GodMode/a3a74a7be1ec1778070871157be9412364ed55a7/app/src/main/res/drawable-xxhdpi/ic_people.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_puzzle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/577fkj/GodMode/a3a74a7be1ec1778070871157be9412364ed55a7/app/src/main/res/drawable-xxhdpi/ic_puzzle.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_setting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/577fkj/GodMode/a3a74a7be1ec1778070871157be9412364ed55a7/app/src/main/res/drawable-xxhdpi/ic_setting.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/577fkj/GodMode/a3a74a7be1ec1778070871157be9412364ed55a7/app/src/main/res/drawable-xxhdpi/up.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/577fkj/GodMode/a3a74a7be1ec1778070871157be9412364ed55a7/app/src/main/res/drawable-xxxhdpi/down.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/exchange.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/577fkj/GodMode/a3a74a7be1ec1778070871157be9412364ed55a7/app/src/main/res/drawable-xxxhdpi/exchange.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_block.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/577fkj/GodMode/a3a74a7be1ec1778070871157be9412364ed55a7/app/src/main/res/drawable-xxxhdpi/ic_block.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/577fkj/GodMode/a3a74a7be1ec1778070871157be9412364ed55a7/app/src/main/res/drawable-xxxhdpi/up.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/design_snackbar_background.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_backup_table_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_settings_backup_restore_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/rectange_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_container_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_snackbar.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 24 | 25 | 39 | 40 |