From 1944e6303670505ef12f4c7a4b2297d6579cd3c1 Mon Sep 17 00:00:00 2001 From: GangJust <201741884@qq.com> Date: Wed, 8 Mar 2023 21:51:16 +0800 Subject: [PATCH] create MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 适配24.4.0、24.5.0 --- .gitignore | 10 + app/.gitignore | 2 + app/build.gradle | 77 + app/dic.txt | 4586 +++++++++++++++++ app/proguard-rules.pro | 79 + .../freegang/fplus/ExampleInstrumentedTest.kt | 24 + app/src/main/AndroidManifest.xml | 58 + app/src/main/assets/update.log | 2 + app/src/main/java/com/freegang/fplus/App.kt | 11 + .../main/java/com/freegang/fplus/Themes.kt | 126 + .../freegang/fplus/activity/HomeActivity.kt | 614 +++ .../freegang/fplus/activity/MainActivity.kt | 147 + .../freegang/fplus/component/CardButton.kt | 95 + .../com/freegang/fplus/component/FCard.kt | 39 + .../com/freegang/fplus/component/FDialog.kt | 94 + .../com/freegang/fplus/resource/ColorRes.kt | 78 + .../com/freegang/fplus/resource/ShapeRes.kt | 13 + .../com/freegang/fplus/resource/StringRes.kt | 8 + .../drawable-v24/ic_launcher_foreground.xml | 30 + app/src/main/res/drawable/ic_dark_mode.xml | 9 + app/src/main/res/drawable/ic_find_file.xml | 9 + app/src/main/res/drawable/ic_github.xml | 10 + .../res/drawable/ic_launcher_background.xml | 170 + app/src/main/res/drawable/ic_light_mode.xml | 9 + app/src/main/res/drawable/ic_motion.xml | 9 + app/src/main/res/drawable/ic_note.xml | 9 + .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 + app/src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 3663 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 0 -> 5539 bytes app/src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 2573 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 0 -> 3388 bytes app/src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 5126 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 0 -> 7749 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 8292 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 0 -> 12584 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 11189 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 0 -> 17681 bytes app/src/main/res/values/arrays.xml | 6 + app/src/main/res/values/colors.xml | 11 + app/src/main/res/values/strings.xml | 4 + app/src/main/res/values/themes.xml | 7 + app/src/main/res/xml/backup_rules.xml | 13 + .../main/res/xml/data_extraction_rules.xml | 19 + .../com/freegang/fplus/ExampleUnitTest.kt | 17 + aweme/.gitignore | 1 + aweme/build.gradle | 41 + aweme/consumer-rules.pro | 0 aweme/proguard-rules.pro | 21 + aweme/readme.md | 1 + .../ugc/aweme/ExampleInstrumentedTest.kt | 24 + aweme/src/main/AndroidManifest.xml | 4 + .../widget/tabhost/AwemeFragmentTabHost.java | 15 + .../bytedance/ies/uikit/base/AbsActivity.java | 14 + .../bytedance/ies/uikit/base/AbsFragment.java | 15 + .../ies/uikit/tabhost/FragmentTabHost.java | 29 + .../aweme/app/host/AwemeHostApplication.java | 11 + .../ugc/aweme/base/ui/FlippableViewPager.java | 29 + .../ugc/aweme/detail/ui/DetailActivity.java | 15 + .../android/ugc/aweme/emoji/model/Emoji.java | 4 + .../similaremoji/EmojiDetailDialogNew.java | 25 + .../store/view/EmojiBottomSheetDialog.java | 24 + .../android/ugc/aweme/feed/model/Aweme.java | 4 + .../ui/view/MainFlippableViewPager.java | 28 + .../ui/view/MainTabStripScrollView.java | 27 + .../android/ugc/aweme/main/MainActivity.java | 21 + .../android/ugc/aweme/main/MainFragment.java | 28 + .../main/java/dmt/viewpager/DmtViewPager.java | 33 + .../ss/android/ugc/aweme/ExampleUnitTest.kt | 17 + build.gradle | 10 + core/.gitignore | 2 + core/build.gradle | 52 + core/consumer-rules.pro | 0 core/libs/api-82-sources.jar | Bin 0 -> 55741 bytes core/libs/api-82.jar | Bin 0 -> 25478 bytes core/proguard-rules.pro | 21 + .../freegang/xpler/ExampleInstrumentedTest.kt | 24 + core/src/main/AndroidManifest.xml | 5 + core/src/main/assets/xposed_init | 1 + .../main/java/com/freegang/base/BaseHook.kt | 188 + .../main/java/com/freegang/config/Config.kt | 100 + .../main/java/com/freegang/config/Version.kt | 48 + .../java/com/freegang/douyin/DouYinMain.kt | 49 + .../java/com/freegang/douyin/HAbsActivity.kt | 299 ++ .../freegang/douyin/HEmojiDetailDialogNew.kt | 102 + .../java/com/freegang/douyin/HMainActivity.kt | 73 + .../java/com/freegang/douyin/HMainFragment.kt | 55 + .../freegang/douyin/logic/DownloadLogic.kt | 260 + .../com/freegang/douyin/logic/SaveLogic.kt | 46 + .../java/com/freegang/douyin/model/DeAweme.kt | 23 + .../main/java/com/freegang/view/KDialog.kt | 60 + .../view/adapter/DialogChoiceAdapter.kt | 46 + .../main/java/com/freegang/xpler/HookInit.kt | 90 + .../main/java/com/freegang/xpler/HookMain.kt | 21 + .../java/com/freegang/xpler/HookPackages.kt | 19 + .../xpler/loader/HybridClassLoader.java | 121 + .../freegang/xpler/utils/app/KAlbumUtils.kt | 36 + .../xpler/utils/app/KAppCrashUtils.kt | 64 + .../xpler/utils/app/KAppVersionUtils.kt | 106 + .../freegang/xpler/utils/app/KNotifiUtils.kt | 167 + .../com/freegang/xpler/utils/io/KFileUtils.kt | 78 + .../freegang/xpler/utils/io/KStorageUtils.kt | 41 + .../freegang/xpler/utils/json/KJSONUtils.kt | 330 ++ .../com/freegang/xpler/utils/log/KLogCat.kt | 294 ++ .../freegang/xpler/utils/net/KHttpUtils.kt | 132 + .../xpler/utils/other/KExtensionMore.kt | 32 + .../xpler/utils/other/KResourceUtils.kt | 31 + .../freegang/xpler/utils/view/GViewUtils.java | 656 +++ .../freegang/xpler/utils/view/KViewUtils.kt | 232 + .../com/freegang/xpler/xp/KtXposedHelpers.kt | 139 + .../com/freegang/xpler/xp/KtXposedMore.kt | 152 + .../xpler/xp/bridge/ConstructorHook.kt | 9 + .../com/freegang/xpler/xp/bridge/KtHook.kt | 20 + .../freegang/xpler/xp/bridge/MethodHook.kt | 64 + .../main/res/drawable/dialog_background.xml | 9 + .../dialog_cancel_button_background.xml | 16 + .../dialog_confirm_button_background.xml | 16 + .../dialog_single_button_background.xml | 16 + .../src/main/res/drawable/hook_add_button.xml | 16 + .../main/res/drawable/hook_save_button.xml | 16 + .../res/drawable/item_selector_background.xml | 14 + .../main/res/layout/dialog_choice_item.xml | 16 + .../main/res/layout/dialog_choice_layout.xml | 50 + .../main/res/layout/dialog_message_layout.xml | 76 + .../res/layout/hook_add_save_emoji_layout.xml | 29 + .../main/res/layout/hook_appbar_layout.xml | 27 + core/src/main/res/values/dimens.xml | 4 + .../com/freegang/xpler/ExampleUnitTest.kt | 17 + gradle.properties | 23 + gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 59203 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 185 + gradlew.bat | 89 + settings.gradle | 18 + 134 files changed, 11777 insertions(+) create mode 100644 .gitignore create mode 100644 app/.gitignore create mode 100644 app/build.gradle create mode 100644 app/dic.txt create mode 100644 app/proguard-rules.pro create mode 100644 app/src/androidTest/java/com/freegang/fplus/ExampleInstrumentedTest.kt create mode 100644 app/src/main/AndroidManifest.xml create mode 100644 app/src/main/assets/update.log create mode 100644 app/src/main/java/com/freegang/fplus/App.kt create mode 100644 app/src/main/java/com/freegang/fplus/Themes.kt create mode 100644 app/src/main/java/com/freegang/fplus/activity/HomeActivity.kt create mode 100644 app/src/main/java/com/freegang/fplus/activity/MainActivity.kt create mode 100644 app/src/main/java/com/freegang/fplus/component/CardButton.kt create mode 100644 app/src/main/java/com/freegang/fplus/component/FCard.kt create mode 100644 app/src/main/java/com/freegang/fplus/component/FDialog.kt create mode 100644 app/src/main/java/com/freegang/fplus/resource/ColorRes.kt create mode 100644 app/src/main/java/com/freegang/fplus/resource/ShapeRes.kt create mode 100644 app/src/main/java/com/freegang/fplus/resource/StringRes.kt create mode 100644 app/src/main/res/drawable-v24/ic_launcher_foreground.xml create mode 100644 app/src/main/res/drawable/ic_dark_mode.xml create mode 100644 app/src/main/res/drawable/ic_find_file.xml create mode 100644 app/src/main/res/drawable/ic_github.xml create mode 100644 app/src/main/res/drawable/ic_launcher_background.xml create mode 100644 app/src/main/res/drawable/ic_light_mode.xml create mode 100644 app/src/main/res/drawable/ic_motion.xml create mode 100644 app/src/main/res/drawable/ic_note.xml create mode 100644 app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher_round.png create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher_round.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher_round.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png create mode 100644 app/src/main/res/values/arrays.xml create mode 100644 app/src/main/res/values/colors.xml create mode 100644 app/src/main/res/values/strings.xml create mode 100644 app/src/main/res/values/themes.xml create mode 100644 app/src/main/res/xml/backup_rules.xml create mode 100644 app/src/main/res/xml/data_extraction_rules.xml create mode 100644 app/src/test/java/com/freegang/fplus/ExampleUnitTest.kt create mode 100644 aweme/.gitignore create mode 100644 aweme/build.gradle create mode 100644 aweme/consumer-rules.pro create mode 100644 aweme/proguard-rules.pro create mode 100644 aweme/readme.md create mode 100644 aweme/src/androidTest/java/com/ss/android/ugc/aweme/ExampleInstrumentedTest.kt create mode 100644 aweme/src/main/AndroidManifest.xml create mode 100644 aweme/src/main/java/com/bytedance/ies/dmt/ui/widget/tabhost/AwemeFragmentTabHost.java create mode 100644 aweme/src/main/java/com/bytedance/ies/uikit/base/AbsActivity.java create mode 100644 aweme/src/main/java/com/bytedance/ies/uikit/base/AbsFragment.java create mode 100644 aweme/src/main/java/com/bytedance/ies/uikit/tabhost/FragmentTabHost.java create mode 100644 aweme/src/main/java/com/ss/android/ugc/aweme/app/host/AwemeHostApplication.java create mode 100644 aweme/src/main/java/com/ss/android/ugc/aweme/base/ui/FlippableViewPager.java create mode 100644 aweme/src/main/java/com/ss/android/ugc/aweme/detail/ui/DetailActivity.java create mode 100644 aweme/src/main/java/com/ss/android/ugc/aweme/emoji/model/Emoji.java create mode 100644 aweme/src/main/java/com/ss/android/ugc/aweme/emoji/similaremoji/EmojiDetailDialogNew.java create mode 100644 aweme/src/main/java/com/ss/android/ugc/aweme/emoji/store/view/EmojiBottomSheetDialog.java create mode 100644 aweme/src/main/java/com/ss/android/ugc/aweme/feed/model/Aweme.java create mode 100644 aweme/src/main/java/com/ss/android/ugc/aweme/homepage/ui/view/MainFlippableViewPager.java create mode 100644 aweme/src/main/java/com/ss/android/ugc/aweme/homepage/ui/view/MainTabStripScrollView.java create mode 100644 aweme/src/main/java/com/ss/android/ugc/aweme/main/MainActivity.java create mode 100644 aweme/src/main/java/com/ss/android/ugc/aweme/main/MainFragment.java create mode 100644 aweme/src/main/java/dmt/viewpager/DmtViewPager.java create mode 100644 aweme/src/test/java/com/ss/android/ugc/aweme/ExampleUnitTest.kt create mode 100644 build.gradle create mode 100644 core/.gitignore create mode 100644 core/build.gradle create mode 100644 core/consumer-rules.pro create mode 100644 core/libs/api-82-sources.jar create mode 100644 core/libs/api-82.jar create mode 100644 core/proguard-rules.pro create mode 100644 core/src/androidTest/java/com/freegang/xpler/ExampleInstrumentedTest.kt create mode 100644 core/src/main/AndroidManifest.xml create mode 100644 core/src/main/assets/xposed_init create mode 100644 core/src/main/java/com/freegang/base/BaseHook.kt create mode 100644 core/src/main/java/com/freegang/config/Config.kt create mode 100644 core/src/main/java/com/freegang/config/Version.kt create mode 100644 core/src/main/java/com/freegang/douyin/DouYinMain.kt create mode 100644 core/src/main/java/com/freegang/douyin/HAbsActivity.kt create mode 100644 core/src/main/java/com/freegang/douyin/HEmojiDetailDialogNew.kt create mode 100644 core/src/main/java/com/freegang/douyin/HMainActivity.kt create mode 100644 core/src/main/java/com/freegang/douyin/HMainFragment.kt create mode 100644 core/src/main/java/com/freegang/douyin/logic/DownloadLogic.kt create mode 100644 core/src/main/java/com/freegang/douyin/logic/SaveLogic.kt create mode 100644 core/src/main/java/com/freegang/douyin/model/DeAweme.kt create mode 100644 core/src/main/java/com/freegang/view/KDialog.kt create mode 100644 core/src/main/java/com/freegang/view/adapter/DialogChoiceAdapter.kt create mode 100644 core/src/main/java/com/freegang/xpler/HookInit.kt create mode 100644 core/src/main/java/com/freegang/xpler/HookMain.kt create mode 100644 core/src/main/java/com/freegang/xpler/HookPackages.kt create mode 100644 core/src/main/java/com/freegang/xpler/loader/HybridClassLoader.java create mode 100644 core/src/main/java/com/freegang/xpler/utils/app/KAlbumUtils.kt create mode 100644 core/src/main/java/com/freegang/xpler/utils/app/KAppCrashUtils.kt create mode 100644 core/src/main/java/com/freegang/xpler/utils/app/KAppVersionUtils.kt create mode 100644 core/src/main/java/com/freegang/xpler/utils/app/KNotifiUtils.kt create mode 100644 core/src/main/java/com/freegang/xpler/utils/io/KFileUtils.kt create mode 100644 core/src/main/java/com/freegang/xpler/utils/io/KStorageUtils.kt create mode 100644 core/src/main/java/com/freegang/xpler/utils/json/KJSONUtils.kt create mode 100644 core/src/main/java/com/freegang/xpler/utils/log/KLogCat.kt create mode 100644 core/src/main/java/com/freegang/xpler/utils/net/KHttpUtils.kt create mode 100644 core/src/main/java/com/freegang/xpler/utils/other/KExtensionMore.kt create mode 100644 core/src/main/java/com/freegang/xpler/utils/other/KResourceUtils.kt create mode 100644 core/src/main/java/com/freegang/xpler/utils/view/GViewUtils.java create mode 100644 core/src/main/java/com/freegang/xpler/utils/view/KViewUtils.kt create mode 100644 core/src/main/java/com/freegang/xpler/xp/KtXposedHelpers.kt create mode 100644 core/src/main/java/com/freegang/xpler/xp/KtXposedMore.kt create mode 100644 core/src/main/java/com/freegang/xpler/xp/bridge/ConstructorHook.kt create mode 100644 core/src/main/java/com/freegang/xpler/xp/bridge/KtHook.kt create mode 100644 core/src/main/java/com/freegang/xpler/xp/bridge/MethodHook.kt create mode 100644 core/src/main/res/drawable/dialog_background.xml create mode 100644 core/src/main/res/drawable/dialog_cancel_button_background.xml create mode 100644 core/src/main/res/drawable/dialog_confirm_button_background.xml create mode 100644 core/src/main/res/drawable/dialog_single_button_background.xml create mode 100644 core/src/main/res/drawable/hook_add_button.xml create mode 100644 core/src/main/res/drawable/hook_save_button.xml create mode 100644 core/src/main/res/drawable/item_selector_background.xml create mode 100644 core/src/main/res/layout/dialog_choice_item.xml create mode 100644 core/src/main/res/layout/dialog_choice_layout.xml create mode 100644 core/src/main/res/layout/dialog_message_layout.xml create mode 100644 core/src/main/res/layout/hook_add_save_emoji_layout.xml create mode 100644 core/src/main/res/layout/hook_appbar_layout.xml create mode 100644 core/src/main/res/values/dimens.xml create mode 100644 core/src/test/java/com/freegang/xpler/ExampleUnitTest.kt create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 gradlew create mode 100644 gradlew.bat create mode 100644 settings.gradle diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8cacfbd --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +*.iml +.gradle +.idea +/local.properties +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..956c004 --- /dev/null +++ b/app/.gitignore @@ -0,0 +1,2 @@ +/build +/release \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..6d23e41 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,77 @@ +plugins { + id 'com.android.application' + id 'org.jetbrains.kotlin.android' +} + +android { + namespace 'com.freegang.fplus' + compileSdk 33 + + defaultConfig { + applicationId "com.freegang.fplus" + minSdk 21 + targetSdk 33 + versionCode 1 + versionName "1.0.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + vectorDrawables { + useSupportLibrary true + } + } + + buildTypes { + release { + minifyEnabled true + shrinkResources true + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + + debug { + minifyEnabled true + shrinkResources true + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } + kotlinOptions { + jvmTarget = '1.8' + } + buildFeatures { + compose true + } + composeOptions { + kotlinCompilerExtensionVersion '1.1.1' + } + packagingOptions { + resources { + excludes += '/META-INF/{AL2.0,LGPL2.1}' + } + } +} + +dependencies { + implementation project(":core") + + implementation 'androidx.core:core-ktx:1.8.0' + implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1' + implementation 'androidx.activity:activity-compose:1.4.0' + implementation "androidx.compose.ui:ui:$compose_ui_version" + implementation "androidx.compose.ui:ui-tooling-preview:$compose_ui_version" + implementation 'androidx.compose.material:material:1.3.1' + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.3' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' + androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_ui_version" + debugImplementation "androidx.compose.ui:ui-tooling:$compose_ui_version" + debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_ui_version" + + // custom + implementation "com.google.accompanist:accompanist-insets:0.28.0" + implementation "com.google.accompanist:accompanist-insets-ui:0.28.0" + implementation "com.google.accompanist:accompanist-systemuicontroller:0.28.0" + implementation 'com.google.accompanist:accompanist-permissions:0.28.0' +} \ No newline at end of file diff --git a/app/dic.txt b/app/dic.txt new file mode 100644 index 0000000..7f3198a --- /dev/null +++ b/app/dic.txt @@ -0,0 +1,4586 @@ +0000O000000o +000O00000Oo +000O00000o0 +000O00000o +000O00000oO +000O00000oo +000O0000O0o +000O0000OOo +000O0000Oo0 +000O0000Oo +000O0000OoO +00O0000Ooo +00O0000o00 +00O0000o0 +00O0000o0O +00O0000o0o +00O0000o +00O0000oO0 +00O0000oO +00O0000oOO +000O0000oOo +00O0000oo0 +00O0000oo +00O0000ooO +00O0000ooo +00O00oOooO +00O00oOooo +00O000O00o +00O000O0OO +00O000O0Oo +000O00oOoOo +00O000O0o0 +00O000O0o +00O000O0oO +00O000O0oo +00O000OO00 +00O000OO0o +00O000OO +00O000OOOo +00O000OOo0 +000O000OOo +00O000OOoO +00O000OOoo +00O000Oo00 +00O000Oo0 +00O000Oo0O +00O000Oo0o +00O000OoO0 +00O00O0Oo +00O000OoO +000O000OoOO +00O000OoOo +00O000Ooo0 +00O000Ooo +00O000OooO +00O000Oooo +00O000o000 +00O000o00 +00O000o00O +00O000o00o +000O000o0 +00O000o0O0 +00O000o0O +00O000o0OO +00O000o0Oo +00O000o0o0 +00O000o0o +00O000o0oo +00O000o +00O000oO00 +000O000oO0 +00O000oO0O +00O000oO0o +00O000oO +00O000oOO0 +00O000oOO +00O000oOOO +00O000oOOo +00O000oOo0 +00O000oOo +000O000oOoO +00O000oOoo +00O000oo0 +00O000oo0O +00O000oo0o +00O000oo +00O000ooO0 +00O000ooO +00O000ooOO +00O000ooOo +000O000ooo0 +00O000ooo +00O000oooO +00O000oooo +00oooOoO +00O00oOOoo +00O00O000o +00O00O00Oo +00O00O00o0 +00O00O00o +000O00O00oO +00O00O00oo +00O00O0O0o +00O00O0OO +00O00O0OOo +00O00O0Oo0 +00O00O0OoO +00O00O0Ooo +00O00O0o00 +00O00O0o0 +00O00O0o0O +0O00O0o0o +0O00O0o +0O00O0oO0 +0O00O0oOO +0O00O0oOo +0O00O0oo0 +0O00O0oo +0O00O0ooO +0O00O0ooo +00O00OO0O +0O00OO0o +0O00OOOo +0O00OOo0 +0O00OOo +0O00OOoO +0O00OOoo +0O00Oo00 +0O00Oo00o +0O00Oo0 +00O00Oo0OO +0O00Oo0Oo +0O00Oo0o0 +0O00Oo0o +0O00Oo0oO +0O00OooOO +0O00Oo0oo +0O00Oo +0O00OoO0 +0O00OoO0o +00O00OoO0O +0O00Ooo +0O00OoO +0O00OoOO0 +0O00OoOO +0O00OoOo0 +0O00OoOo +0O00OoOoO +0O00OoOoo +0O00Ooo00 +00O00Ooo0 +0O00Ooo0O +0O00Ooo0o +0O00OooO0 +0O00OooO +0O00OooOo +0O00Oooo0 +0O00Oooo +0O00Ooooo +0O00OoooO +00O00o0000 +0O00o000 +0O00o000O +0O00o000o +0O00o00 +0O00o00O0 +0O00o00O +0O00o00OO +0O00o00Oo +0O00o00o0 +00O00o00o +0O00o00oO +0O00o00oo +0O00o0 +0O00o0O00 +0O00o0O0 +0O00oo000 +0O00o0O0O +0O00o0O0o +0O00o0O +00O00o0OO0 +0O00o0OO +0O00o0OOO +0O00o0OOo +0O00o0Oo0 +0O00o0Oo +0O00o0OoO +0O00o0Ooo +0O00o0o00 +0O00o0o0 +00O00o0o0O +0O00o0o0o +0O00o0o +0O00o0oO0 +0O00o0oO +0O00o0oOO +0O00o0oOo +0O00o0oo0 +0O00o0oo +0O00o0ooO +000O00o0ooo +00O00o +00O00oO0O0 +00O00oO000 +00O00oO00 +00O00oO00O +00O00oO0OO +00O00oO00o +00O00oO0 +00O00oO0O +00O00ooO00 +0O00oO0Oo +0O00oO0o0 +0O00oO0o +0O00oO0oO +0O00oO0oo +0O00oO +0O00oOO0o +0O00oOO00 +0O00oOO0 +00O00oOO0O +0O00oOo +0O00oOO +0O00oOOO0 +0O00oOOO +0O00oOOOO +0O00oOOOo +0O00oOOo0 +0O00oOOo +0O00oOOoO +00O00oOo00 +0O00oOo0 +0O00oOo0O +0O00oOo0o +0O00oOoO +0O00oOoOO +0O00oOoo0 +0O00oOoo +0O00oo00 +0O00oo00O +00O00oo00o +0O00oo0 +0O00oo0O0 +0O00oo0O +0O00oo0OO +0O00oo0Oo +0O00oo0o0 +0O00oo0o +0O00oo0oo +0O00oo +00O00ooO0 +0O00ooO0O +0O00ooO0o +0O00ooO +0O00ooOo0 +0O00ooOO0 +0O00ooOO +0O00ooOOO +0O00ooOOo +0O00ooOo +00O00ooOoO +0O00ooOoo +0O00ooo00 +0O00ooo0 +0O00ooo0o +0O00ooo +0O00oooO0 +0O00oooO +0O00oooOO +0O00oooOo +00O00oooo0 +0O00oooo +0O00ooooo +0ooooooo +0O0O00oO +0O0O000o +0O0O00OO +0O0O00Oo +0O0O00o0 +0O0O00o +00O0O0O0o +0O0O0O +0O0O0OO0 +0O0O0OO +0O0O0OOO +0O0O0OOo +0O0O0Oo0 +0O0O0OoO +0O0O0Ooo +0O0O0o00 +00O0O0o0 +0O0O0o0O +0O0O0o0o +0O0O0o +0O0O0oO0 +0O0O0oO +0O0O0oOO +0O0O0oOo +0O0O0oo0 +0O0O0oo +000O0O0ooO +00O0O0ooo +00O0OoOo +00O0OoOO +00O0OO00O +00O0OO00o +00O0OO0O +00O0Oo0o0 +00O0OO0Oo +00O0OO0o0 +00O0OO0oO +0O0OO0oo +0O0OOO00 +0O0OOoo +0O0OOoO +0O0OOO0 +0O0OOO0O +0O0OOO0o +0OO0oO +0O0OOOO +00O0OOOOO +0O0OOOOo +0O0OOOo0 +0O0OOOo +0O0OOOoO +0O0OOOoo +0O0OOo00 +0O0OOo0O +0O0OOo0o +0O0OOo +00O0OOoO0 +0O0OOoOO +0O0OOoOo +0O0OOoo0 +0O0OOooO +0O0OOooo +0O0Oo000 +0O0Oo00 +0O0Oo00O +0O0Oo00o +00O0Oo0 +0O0Oo0O0 +0O0Oooo +0O0Oo0O +0O0Oo0OO +0O0Oo0Oo +0O0Oo0o +0O0Oo0oO +0O0Oo0oo +0o00o00O +00O0Oo +0O0OoO00 +0O0OoO0 +0O0OoO0O +0OooOO +0OoO0o +0O0OoO +0O0OoOO0 +0O0OoOOO +0O0OoOOo +00O0OoOo0 +0O0OoOoO +0O0OoOoo +0O0Ooo00 +0O0Ooo0 +0O0Ooo0O +0O0Ooo0o +0O0Ooo +0O0OooO0 +0O0OooO +00O0OooOO +0O0OooOo +0O0Oooo0 +0O0OoooO +0O0Ooooo +0O0o0000 +0O0o000 +0O0o000O +0O0o000o +0O0o00 +00O0o00O0 +0O0o00O +0O0o00OO +0O0o00Oo +0O0o00o0 +0O0o00o +0O0o00oO +0O0o00oo +0O0o0 +0O0o0O00 +00O0o0O0 +0O0o0O0O +0O0o0O0o +0O0o0O +0O0o0OO0 +0O0oo0o +0O0o0OO +0O0o0OOO +0O0o0OOo +0O0o0Oo0 +000O0o0Oo +00O0o0OoO +00O0o0Ooo +00O0o0o00 +00O0o0o0 +00O0o0o0O +00O0o0o0o +00O0o0oO0 +00O0o0oO +00O0o0oOO +00O0o0oOo +0O0o0oo +0O0o0ooO +0oOOoOO +0O0o +0O0oO000 +0O0oO00 +0O0oO0oO +0O0oO00O +0O0oO00o +00O0oO0 +0O0oO0O0 +0O0oO0O +0O0oO0OO +0O0oO0Oo +0O0oO0o0 +0O0oO0o +0O0oO0oo +0o0Oo0o0 +0O0oO +00O0oOO00 +0O0oOO0 +0o00O00O0 +0O0oOO0O +0O0oOO0o +0O0oOO +0O0oOOO0 +0O0oOOOo +0O0oOOo0 +0Oo0OOo +00O0oOOo +0O0oOOoO +0O0oOOoo +0O0oOo00 +0O0oOo0 +0ooO0Ooo +0O0oOo0O +0O0oOo0o +0O0oOo +0O0oOoO0 +00O0oOoO +0O0oOoOO +0O0oOoOo +0OOoOoo +0O0oOoo0 +0O0oOoo +0O0oOooO +0O0oOooo +0O0oo000 +0O0oo00 +00O0oo00O +0O0oo00o +0O0oo0 +0O0oo0O0 +0O0oo0O +0O0oo0OO +0O0oo0Oo +0O0oo0o0 +0O0oo0oO +0O0oo0oo +00O0oo +0O0ooO00 +0O0ooOo +0O0oooo +0O0oooO +0O0ooO0 +0O0oooOo +0O0ooO0O +0O0ooO0o +0O0ooO +00O0ooOO0 +0O0ooOO +0O0ooOOO +0O0ooOOo +0O0ooOo0 +0O0ooOoO +0O0ooOoo +0O0ooo00 +0O0ooo0 +0O0ooo0O +00O0ooo0o +0O0ooo +0O0oooO0 +0O0oooOO +0O0oooo0 +0O0ooooO +0O0ooooo +0OO0000 +0OO0000o +0OoOOO +000OO000OO +00OO000Oo +00OO000o0 +00OO000o +00OO000oO +00OO000oo +00OO00OO +00OO00O0 +00OO00O0o +00OO00O +00OO00OOO +0OO00OOo +0OO00Oo0 +0OO0ooO +0OO00Oo +0OO00OoO +0OO00Ooo +0OO00o00 +0OO00o0O +0OO00o0o +00OO00oO0 +0OO00oO +0OO00oOO +0OO00oOo +0OO00oo0 +0OO00oo +0OO00ooO +0OO00ooo +0OO0O00o +0OO0O0O +00OO0O0OO +0OO0O0Oo +0OO0O0o0 +0OO0oOo +0OO0oOO +0OO0O0o +0OO0O0oO +0OO0O0oo +0oOo00 +0OO0OO0o +00OOo00 +0OO0OO +0OO0OOO +0OO0OOOO +0OO0OOOo +0OO0OOo0 +0OO0OOo +0OO0OOoO +0OO0OOoo +0OO0Oo00 +00OO0Oo0 +0OO0Oo0O +0OO0Oo0o +0Oo0Oo0O +0OO0OoO0 +0OO0OoO +0OO0OoOO +0Oo0Ooo0 +0OO0OoOo +0OO0Ooo0 +00OO0Ooo +0OO0OooO +0OO0Oooo +0OO0o000 +0OO0o00 +0OO0o00O +0OO0o00o +0OO0o0 +0OO0o0O0 +0OO0o0OO +00OO0o0Oo +0OO0o0o0 +0OO0o0o +0OO0o0oO +0OO0o0oo +0OO0o +0OO0oO00 +0OO0oO0 +0OO0oO0O +0OO0oO0o +00OO0oOO0 +0OoOOoOo +0OO0oOOO +0OO0oOOo +0OO0oOo0 +0OO0oOoO +0OO0oOoo +0OO0oo00 +0OO0oo0 +0OO0oo0O +00OO0oo0o +0OO0oo +0OO0ooO0 +0OO0ooOO +0OO0ooOo +0OO0ooo0 +0OO0ooo +0OO0oooO +0OO0oooo +0OOO000o +000OOO00 +00OOO00O0 +00OOO00Oo +00OOO00o0 +00OOO00o +00OOO00oO +00OOO00oo +00OOO0O0O +00OOO0O0o +00OOO0O +00o00OOOOO +0OOO0OO0 +0OOO0OOO +0OOO0OOo +0OOO0Oo0 +0OOO0oO +0OOO0Oo +0OOO0OoO +0OOO0Ooo +0OOO0o00 +00OOO0o0 +0OOO0o0O +0OOO0o0o +0OOO0o +0OOO0oO0 +0OOO0oOO +0OOO0oOo +0OOO0oo0 +0OOO0oo +0OOO0ooO +00OOO0ooo +0OOO +0OOOO00O +0OOOO00o +0OOOO0 +0OOOOoO +0OOOOo0 +0OOOOoo +0OOOO0O +0OOOO0OO +00OOOO0Oo +0OOOO0o0 +0OOOO0oO +0OOOO0oo +0OOOOO00 +0OOOOO0 +0OOOOO0o +0OOOOO +0OOOOOO +0OOOOOOO +00OOOOOOo +0OOOOOo0 +0OOooOo +0OOoooo +0OOOOOo +0OOOOOoO +0OOOOOoo +0OOOOo00 +0OOOOo0O +0OOOOo0o +00OOOOo +0OOOOoO0 +0OOOOoOO +0OOOOoOo +0OOOOoo0 +0OOOOooO +0OOOOooo +0OOOo000 +0OOo000 +0OOOo00 +00OOOo00O +0OOOo00o +0OOOo0 +0ooOOo00 +0OOOo0O0 +0OOOo0O +0OOOo0OO +0OOOo0Oo +0OOOo0o0 +0OOOo0o +00OOOo0oO +0OOOo0oo +0OOOo +0OOOoO00 +0OOOoO0 +0OOOoO0O +0o0ooo0OO +0OOOoO0o +0OOOoO +0OOOoOO0 +00OOOoOO +0OOOoOOO +0OOOoOOo +0OOOoOo0 +0OOOoOo +0OOOoOoO +0OOOoOoo +0ooO00O0 +0OOOoo00 +0OOOoo0 +000o00O0oO0 +00OOOoo0O +00OOOoo0o +00OOOoo +00OOOooO0 +00OOOooO +00o0OOoOoo +00OOOooOO +00OOOooOo +00OOOooo0 +00OOOooo +0OOOoooO +0OOOoooo +0OOo0000 +0OOo000O +0OOo000o +0OOo00O0 +0OOo00O +0OOo00OO +0OOo00Oo +00OOo00o0 +0OOo00o +0OOo00oO +0OOo00oo +0ooOo +0OOo0 +0OOo0O00 +0OOo0OO +0OOo0Oo +0OOoo00 +00OOo0O0 +0OOo0O0O +0OOo0O0o +0OOoOOoO +0OOo0OO0 +0OOo0OOO +0OOo0OOo +0OOo0Oo0 +0OOo0OoO +0OOo0Ooo +00OOo0o00 +0OOo0o0 +0OOo0o0O +0OOo0o0o +0OOo0o +0OOo0oO0 +0OOo0oO +0OOo0oOO +0OOo0oOo +0OOo0oo0 +00OOo0oo +0OOo0ooO +0OOo0ooo +0OOoO000 +0OOoO00 +0OOoO00O +0OOoO00o +0OOoO0 +0OOoO0O0 +0OOoO0o +00OOoO0O +0OOoO0OO +0OOoO0Oo +0OOoO0o0 +0OOoO0oO +0OOoO0oo +0OOoO +0OOoOO00 +0OOoOO0O +0OOoOO0o +00OOoOO +0OOoOOO0 +0OOoOOO +0OOoOOOO +0OOoOOOo +0OOoOOo0 +0OOoOOo +0OOoOOoo +0OOoOo00 +0OOoOo0 +00OOoOo0O +0OOoOo0o +0OOoOo +0OOoOoO0 +0oO0OO0 +0OOoOoO +0OOoOoOO +0OOoOoOo +0OOoOoo0 +0OOoOooO +00OOoOooo +0OOoo000 +0OOoo00O +0OOoo00o +0OOoo0 +0OOoo0O0 +0OOoo0O +0OOoo0OO +0OOoo0Oo +0OOoo0o0 +000OOoo0o +00OOoo0oO +00OOoo0oo +00OOoo +00OOooO00 +00OOooO0 +00OOooO0O +00OOooO0o +00OOooO +00OOooOO0 +00OOooOO +0OOooOOO +0OOooOOo +0OOooOo0 +0OOooOoO +0OOooOoo +0OOooo00 +0OOooo0 +0OOooo0O +0OOooo0o +00OOooo +0OOoooO0 +0OOoooO +0OOoooOO +0OOoooOo +0OOoooo0 +0OOooooO +0OOooooo +0Oo00000 +0Oo0000O +00Oo0000o +0Oo000 +0Oo000O0 +0Oo000O +0Oo000OO +0Oo000Oo +0Oo000o0 +0Oo000o +0Oo000oO +0Oo000oo +00Oo00 +0Oo00O00 +0Oo00O0 +0Oo0o0Oo +0Oo00O0O +0Oo00O0o +0Oo00O +0Oo00OO0 +0Oo0o0o +0Oo00Oo +00Oo00OO +0Oo00OOO +0Oo00OOo +0Oo00Oo0 +0Oo0o0O0 +0Oo00OoO +0Oo00Ooo +0Oo00o00 +0Oo00o0 +0Oo00o0O +00Oo00o0o +0Oo00o +0Oo00oO0 +0Oo00oOO +0Oo00oo0 +0Oo00oo +0Oo00ooO +0Oo00ooo +0Oo0 +0Oo0O000 +00Oo0O00 +0Oo0O00O +0Oo0O00o +0Oo0O0 +0Oo0O0O0 +0Oo0O0O +0Oo0O0OO +0Oo0O0Oo +0Oo0O0o0 +0Oo0O0o +00Oo0O0oO +0Oo0O0oo +0o00oO00 +0o00OO00 +0Oo0O +0Oo0OO00 +0Oo0OO0 +0Oo0OO0O +0Oo0OO0o +0Oo0OO +00Oo0OOO0 +0Oo0OOOO +0Oo0OOOo +0Oo0OOo0 +0Oo0OOoO +0Oo0OOoo +0Oo0Oo00 +0Oo0Oo0 +0Oo0Oo0o +0Oo0Oo +000Oo0OoO0 +00Oo0OoO +00Oo0OoOo +00Oo0Ooo +00Oo0OooO +00Oo0Oooo +00Oo0o000 +00Oo0o00 +00Oo0o00O +00Oo0o00o +00Oo0o0 +0Oo0o0O +0Oo0o0o0 +0Oo0o0oO +0Oo0o0oo +0Oo0o +0Oo0oO00 +0Oo0ooO +0Oo0oOO +0Oo0oo0 +00Oo0ooo +0Oo0oO0 +0Oo0oO0O +0Oo0oO0o +0Oo0oO +0Oo0oOO0 +0Oo0oOOO +0Oo0oOOo +0Oo0oOo0 +0Oo0oOo +00Oo0oOoO +0Oo0oOoo +0Oo0oo00 +0Oo0oo0O +0Oo0oo0o +0Oo0oo +0Oo0ooO0 +0Oo0ooOO +0Oo0ooOo +0Oo0ooo0 +00Oo0oooO +0Oo0oooo +0Oo +0OoO0000 +0OoO000 +0OoO000O +0OoO000o +0OoO00O0 +0OoO00O +0OoO00OO +00OoO00Oo +0OoO00o0 +0OoO00o +0OoO00oO +0OoO00oo +0OoO0 +0OoO0O00 +0OoO0Oo +0OoO0O0 +0OoO0O0O +00OoO0O0o +0OoO0O +0OoO0OO0 +0OoO0OO +0OoO0OOO +0OoOo00O +0OoO0OOo +0OoO0Oo0 +0OoO0OoO +0OoO0Ooo +00OoO0o00 +0OoO0o0 +0OoO0o0O +0OoO0o0o +0OoO0oO0 +0OoO0oo +0OoO0oO +0OoO0oOO +0OoO0oOo +0OoO0oo0 +00OoO0ooO +0OoO0ooo +0OoO +0OoOO000 +0OoOOo0 +0OoOOOO +0OoOO0O +0OoOOoo +0OoOOoO +0OoOOO0 +00OoOOOo +0OoOoOo +0OoOoOO +0OoOO00 +0OoOO00O +0OoOO00o +0OoOO0 +0OoOO0O0 +0OoOO0OO +0OoOO0Oo +000OoOO0o0 +00OoOO0o +00OoOO0oO +00OoOO0oo +00OoOo +00OoOO +00OoOOO0O +00OoOOO0o +00OoOOOO0 +00OoOOOOO +00OoOOOOo +0OoOOOo0 +0OoOOOoO +0OoOOOoo +0OoOOo00 +0OoOOo0O +0OoOOo0o +0OoOOo +0OoOOoO0 +0OoOOoOO +00OoOOoo0 +0OoOOooO +0OoOOooo +0OoOo000 +0OoOooo +0OoOoo0 +0OoOo00 +0OoOo00o +0OoOo0 +0OoOo0O0 +00OoOo0o +0OoOo0O +0OoOo0OO +0OoOo0Oo +0OoOo0o0 +0OoOo0oO +0OoOo0oo +0OoOoO00 +0OoOoO0 +0OoOoO0O +00OoOoO0o +0OoOoO +0OoOoOO0 +0OoOoOOO +0OoOoOOo +0OoOoOo0 +0OoOoOoO +0OoOoOoo +0OoOoo00 +0OoOoo0O +00OoOoo0o +0OoOoo +0OoOooO0 +0OoOooO +0OoOooOO +0OoOooOo +0OoOooo0 +0OoOoooO +0OoOoooo +0Ooo0000 +00Ooo000 +0Ooo000O +0Ooo000o +0Ooo00 +0Ooo00O0 +0Ooo00O +0Ooo00OO +0Ooo00Oo +0Ooo00o0 +0Ooo00o +00Ooo00oO +0o00oo00o +0Ooo00oo +0Ooo0 +0Ooo0O00 +0Ooo0O0 +0Ooo0O0O +0Ooo0O0o +0Ooo0O +0Ooo0OO0 +00Oooo00 +0Ooo0oO +0Ooo0Oo +0Ooo0OO +0o000O0OO +0o000oO0O +0Ooo0OOO +0Ooo0OOo +0Ooo0OoO +0Ooo0Ooo +00Ooo0o00 +0Ooo0o0 +0Ooo0o0o +0Ooo0o +0Ooo0oO0 +0Ooo0oOO +0Ooo0oOo +0Ooo0oo0 +0Ooo0oo +0Ooo0ooO +00Ooo0ooo +0o0oo +0Ooo +0OooO000 +0OooO0O +0Ooooo0 +0OooooO +0OoooOO +0OoooOo +0OooO00 +0OooO00O +OooO00o +OooO0O0 +OooO0OO +OooO0Oo +OooO0o0 +OooO0o +OooO0oO +OooO0oo +OooO +0OooOO00 +OooOO0 +OooOO0O +OooOO0o +OooOOO0 +OooOOO +OooOOOO +OooOOOo +OooOOo0 +OooOOo +0OooOOoO +OooOOoo +OooOo00 +OooOo0 +OooOo0O +OooOo0o +OooOo +OooOoO0 +OooOoO +OooOoOO +0OooOoOo +OooOoo0 +OooOoo +OooOooO +OooOooo +Oooo000 +Oooo00O +Oooo00o +Oooo0 +Oooo0O0 +0Oooo0O +Oooo0OO +Oooo0o0 +Oooo0o +Oooo0oO +Oooo0oo +Oooo +OoooO00 +OoooO0 +OoooO0O +0OoooO0o +OoooO +OoooOO0 +o000oOoO +OoooOOO +OoooOOo +OoooOo0 +OoooOoO +OoooOoo +Ooooo00 +0Ooooo0O +Ooooo0o +OooooO0 +OooooOO +OooooOo +Oooooo0 +Oooooo +OoooooO +Ooooooo +o0OoOo0 +0o0OoO0O +ooOO +o00O0O +o00Oo0 +o00Ooo +o00o0O +o00ooo +oo000o +o00oO0o +o00oO0O +0o0oo000 +o0ooOO0 +o0ooOOo +o0ooOoO +o0OOO0o +o0Oo0oo +o0OO00O +oo0o0Oo +o0O0O00 +o000OOo +00oo0oOoO +0ooo0Ooo +0ooOo00O +0o0OoOO0 +0o0O00oo +0o0ooo00 +0o000OOoo +0o000OOOo +0o000OOOO +0o000OOO0 +0o0000000 +o000000 +o000000O +o000000o +o00000 +o00000O0 +o00000O +o00000OO +o00000Oo +o00000o0 +0o00000o +o0000Ooo +o00000oO +o00000oo +o0000 +o0000O00 +o0000oo +o0000oO +o0000O0 +o0000O0O +0o0000O0o +o000OO +o0000O +o0000OO0 +o0000OO +o0000OOO +o0000OOo +o0000Oo0 +o0000Oo +o0000OoO +0o0000o00 +o0000o0 +o0000o0O +o0000o0o +o0000o +o0000oO0 +o0000oOO +o0000oOo +o0000oo0 +o0000ooO +0o0000ooo +o000 +o000O000 +o000OoO +o000O0o +o000Ooo +o000O0O +o000Oo0 +o000O00 +o000O00O +0o000O00o +o000O0 +o000O0Oo +o000OO0O +o000O0O0 +o000O0o0 +o000O0oO +o000O0oo +o000O +o000OO00 +0o000OO0 +o0OoO0o +o000OO0o +o000OOO +o000OOo0 +o000OOoO +o000Oo00 +o000Oo0O +o000Oo0o +o000Oo +0o000OoO0 +o000OoOO +o000OoOo +o000Ooo0 +o000OooO +o000Oooo +o000o000 +o000o00 +o000o00O +o000o00o +0o000o0 +oooo00o +o000o0O0 +o000o0O +o000o0OO +o000o0Oo +o000o0o0 +o000o0o +o000o0oO +o000o0oo +00o000o +0o000oO00 +0o000oO0 +0o000oO0o +0o000oO +0o000oOO0 +0o000oOO +0o000oOOO +0o000oOOo +0o000oOo0 +0o000oOo +o000oOoo +o000oo00 +o000oo0 +o000oo0O +o000oo0o +o000oo +o000ooO0 +o000ooO +o000ooOO +0o000ooOo +o000ooo0 +o000ooo +o000oooO +o000oooo +o00 +o00O0000 +o0O0ooO +o00oOoo +o00O000 +0o00O000O +o00O000o +o00O00 +o00O00O +oOO00O +o00O00OO +o00O00Oo +o00O00o0 +o00O00o +o00O00oO +0o00O00oo +oo00o +o00O0 +o00O0O00 +o00O0O0 +o00O0O0O +o00O0O0o +o00O0OO0 +oo0o0O0 +o00O0OO +0o00Oo00O +o00O0OOO +o00O0OOo +o00O0Oo0 +oo0oOO0 +o00O0Oo +o00O0OoO +o00O0Ooo +o00O0o00 +o00oOOo +0o00oOOO +o00O0o0 +o00O0o0O +o00O0o0o +o00O0o +o00O0oO +o00O0oOO +o00O0oOo +o00O0oo0 +o00O0oo +0o00O0ooO +o00O0ooo +o00O +o00OO000 +o00OO00O +o00OO00o +o00OO0 +o00OO0O0 +o00OO0O +o00OO0OO +0o00OO0Oo +o00OO0o0 +o00OO0o +o00OO0oO +o00OO0oo +oo0O +o00OO +o00OOO00 +o00OOO0 +o00OOO0O +0o00OOO0o +o0o0Oo +o00OOO +o00OOOO0 +o00OOOO +o00OOOOo +oOooo0o +o00OOOo0 +o00OOOo +o00OOOoO +00o00OOOoo +0o00OOo00 +0o00OOo0 +0o00OOo0O +0o00OOo0o +0o00OOo +0o00OOoO0 +0o00OOoO +0o00OOoOO +0o00OOoOo +0o00OOoo0 +o00OOoo +o00OOooO +o00OOooo +o00OoOoO +o00Oo000 +o00Oo00 +o00Oo00o +o00Oo0O0 +o00Oo0O +0o00Oo0OO +o00Oo0Oo +o00Oo0o0 +o00Oo0o +o00Oo0oO +o00Oo0oo +o0oOO +o00Oo +o00OoO00 +o00OoO0 +0o00OoO0O +o00OoO0o +o00OoO +o00OoOO0 +o00OoOO +o00OoOOO +o00OoOOo +o00OoOo0 +o00OoOo +o00OoOoo +0o00Oooo0 +o00Ooo00 +o00Ooo0 +o00Ooo0O +o00Ooo0o +o00OooO0 +o00OooO +o00OooOO +o00OooOo +o0O00o0 +0o00Oooo +o00OoooO +o00Ooooo +o00o0000 +o00o000 +o00o000O +o00o000o +oo00oO +o00o00 +o00o00O0 +0o00o00OO +o00o00Oo +o00o00o0 +o00o00o +o00o00oO +o00o00oo +o00o0 +o00o0O00 +o00o0O0 +o00o0O0O +0o00o0O0o +o00o0OO0 +o00o0OO +o00o0OOO +o00o0OOo +o00o0Oo0 +o00o0Oo +o00o0OoO +o00o0Ooo +o00o0o00 +0o00o0o0 +o00o0o0O +o00o0o0o +o00o0o +o00o0oO0 +o00o0oO +o00o0oOO +o00o0oOo +o00o0oo0 +o00o0oo +0o00o0ooO +o00o0ooo +o00o +o00oo000 +o00oO000 +o00oO00O +o00oO00o +o00oOo +o00oO0 +o00oO0O0 +00o00oO0OO +0o00oO0Oo +0o00oO0o0 +0o00oO0oO +0o00oO0oo +0o00oO +0o00oOOoo +0o00oOO00 +0o00oOO0 +0o00oOO0O +0o00oOO0o +o0oOOo +o0ooOO +o00oOO +o00oOOO0 +o00oOOOO +o00oOOOo +o00oOOo0 +o00oOOoO +o00oOo00 +0o00oOo0 +o00oOooO +o00oOo0O +o00oOo0o +o00oOoO0 +o00oOoO +o00oOoOO +o00oOoOo +o00oOoo0 +o00oOooo +0o00oo00 +o00oo00O +o00oo0 +o00oo0OO +o00oo0O0 +o00oo0O +o00oo0Oo +o00oo0o0 +o00oo0o +o00oo0oO +0o00oo0oo +o0O0o +o00oo +o00ooO00 +o00ooO0 +o00ooO0O +o00ooO0o +o00ooO +o00ooOO0 +o00ooOO +0o00ooOOO +o00ooOOo +o00ooOo0 +o00ooOo +o00ooOoO +o00ooOoo +o00ooo00 +o00ooo0 +o00ooo0O +o00ooo0o +0o00oooO0 +o00oooO +o00oooOO +o00oooOo +o00oooo0 +o00oooo +o00ooooO +o00ooooo +o0 +o0O00000 +0o0O0000 +o0O0000O +o0O0000o +o0O000 +o0O000O +o0OoOoOo +o0O000Oo +o0OoOoOO +o0O000o0 +o0O000o +0o0O000oO +o0ooOoOO +o0O000oo +o0O00 +o0O00O0 +o0OoO00O +o0O00O0o +o0O00O +o0O00OO +o0O00OOO +0o0O00Oo0 +o0O00Oo +o0oO0Ooo +o0O00OoO +o0O00Ooo +o0O00o00 +o0O00o0O +o0O00o0o +o0O00o +o0O00oO0 +00o0O00oO +0o0O00oOO +0o0O00oOo +0o0O00oo0 +0o0Oo0oOO +0o0O00ooO +0o0O00ooo +0o0O0 +0o0ooOOOo +0o0O0O00O +0o0O0O0 +o0O0O0O +o0oO0O0o +o0O0oo0o +o0O0O0Oo +o0O0O0o0 +o0O0O0o +o0O0O0oO +o0O0O0oo +o0ooO +0o0O0O +o0O0OO0 +o0O0OO0O +o0O0OO +o0O0OOO0 +o0O0OOO +o0O0OOOo +o0O0OOo +o0O0OOoO +o0O0OOoo +0o0O0Oo00 +o0OooO0 +o0O0Oo0 +o0O0Oo0O +o0O0Oo0o +o0O0Oo +o0O0Oooo +o0O0OoO0 +oo0OOoo +o0O0OoO +0o0O0OoOO +o0O0OoOo +o0O0Ooo0 +o0O0Ooo +o0O0OooO +o0O0o000 +o0O0o00 +o0O0o00O +o0O0o00o +o0O0o0 +0o0O0o0O0 +o0O0o0O +o0O0o0OO +o0O0o0Oo +o0O0o0o0 +o0O0o0o +o0oOo0O0 +o0O0o0oO +o0O0o0oo +o0O0oo0O +0o0O0oO00 +oooOO0 +o0O0oO0 +o0O0oO0O +o0O0oO0o +o0O0oO +o0O0oOO0 +o0O0oOO +o0O0oo00 +o0O0oOOO +0o0O0oOOo +o0O0oOo0 +oo0oOOo +o0O0oOo +o0O0oOoO +o0O0oOoo +o0O0oo0 +o0O0oo +o0O0ooO0 +o0O0ooOO +0o0O0ooOo +o0O0ooo0 +o0O0ooo +o0O0oooO +o0O0oooo +o0O +o0OO000 +o0OO000o +oo0oO0 +oo0ooO +0o0OO00 +o0OO00OO +o0OO00oo +o0OO00Oo +o0OO00o0 +o0OoOoO +o0OO00o +o0OO0 +o0OO0O0 +o0OO0O0O +00o0OO0O0o +0o0OO0O +0o0OOoOOo +0o0OOoOoO +0o0OO0OO0 +0o0OO0OO +0o0OOoOO0 +0o0OO0OOO +0o0OO0OOo +0o0OO0Oo0 +0o0OO0Oo +o0OO0OoO +o0OOooO0 +o0OO0Ooo +o0OO0o00 +o0OO0o0 +o0OO0o0O +o0OO0o0o +o0OO0o +o0OO0oO0 +0o0OO0oo +o0OO0oO +o0OO0oOO +o0OOoooO +o0OO0oOo +o0OO0oo0 +o0OO0ooO +o0OO0ooo +o0OO +o0OOO00 +0o0OOO00O +o0OOO00o +o0OOO0 +o0OOO0O0 +o0OOO0O +o0OOO0OO +o0OOO0Oo +o0OOO0o0 +o0OOO0oO +o0OOO0oo +0o0OOO +o0OOOOoO +o0OOOO00 +o0OOOO0 +o0OOOO0o +o0OOOO +o0OOOOO0 +o0OOOOO +o0OOOOOO +o0OOOOOo +0o0OOOOo0 +o0OOOOo +o0OOOOoo +o0OOOo00 +o0OOOo0 +o0OOOo0O +o0OOOo0o +o0OOOo +o0OOOoO0 +o0OOOoO +0o0OOOoOO +o0OOOoOo +o0OOOoo0 +o0OOOoo +o0OOOooO +o0OOOooo +o0OOo000 +o0OOo00 +o0OOo00O +o0OOo00o +0o0OOo0 +o0OOo0O0 +o0OooOo +o0OOo0O +o0OOo0OO +o0OOo0Oo +o0OOo0o0 +o0OOo0o +o0OOo0oO +o0OOo0oo +0o0OOo +o0OOoO00 +o0OOoO0 +o0OOoO0O +o0OOoO0o +o0OOoO +o0OOoOO +o0OOoOOO +o0OOoOo0 +o0OOoOo +0o0OOoo00 +o0OOoo0 +o0OOoo0O +o0OOoo0o +o0OOoo +o0OOooO +o0OOooOO +o0OOooOo +o0OOooo0 +o0OOooo +00o0OOoooo +0o0Oo0000 +0o0Oo000 +0o0Oooo0O +0o0Oo00O0 +0o0Oo000O +0o0Oo000o +0o0Oo00 +0o0Oo00OO +0o0Oo00Oo +0o0oooOoo +o0Oo00o0 +o0Oo00o +o0Oo00oO +o0Oo00oo +o0Oo0 +o0ooO0O0 +o0Oo0O00 +o0Oo0O0 +o0Oo0O0O +0o0Oo0O0o +o0Oo0O +o0Oo0OO0 +o0Oo0OO +o0oOooO0 +o0ooOOOO +o0oOo000 +o0Oo0OOO +o0oOo0o0 +o0oOoo00 +0o0oO0OOo +o0Oo0OOo +o0Oo0Oo0 +o0Oo0Oo +o0Oo0OoO +o0Oo0oOo +o0Oo0Ooo +o0Oo0o00 +o0Oo0o0O +o0Oo0o0o +0o0Oo0o +o0Oo0oO0 +o0Oo0oO +o0Oo0oo0 +o0Oo0ooO +o0Oo0ooo +o0Oo +o0OooOoo +o0OoO000 +o0OoO0 +0o0OoO0O0 +o0oO0O00 +o0OoO0OO +o0ooOOO0 +o0OoO0Oo +o0OoO0o0 +o0OoO0oO +o0OoO0oo +o0OoO +o0OoOO00 +0o0OoOO0O +o0OoOO0o +o0OoOO +o0OoOOoO +o0OoOOO0 +o0OoOOO +o0OoOOOO +o0OoOOOo +o0OoOOo0 +o0OoOOo +0o0OoOOoo +o0OoOo00 +o0OoOo0O +o0ooOOoo +o0OoOo0o +o0OoOo +o0OoOoO0 +o0OoOoo0 +o0OoOoo +o0OoOooO +0o0OoOooo +o0Ooo000 +o0Ooo00 +o0Ooo00O +o0oOO0Oo +o0oOoOoo +o0Ooo00o +o0Ooo0 +o0Ooo0O0 +o0Ooo0O +0o0Ooo0OO +o0oOOOoo +o0OoooO0 +o0oOOO0o +o0Ooo0Oo +o0Ooo0o0 +o0Ooo0o +o0Ooo0oO +o0Ooo0oo +o0Ooo +00o0OooO00 +0o0OooO0O +0o0OooO0o +0o0OooO +0o0OooOO0 +0o0OooOO +0o0OooOOO +0o0OooOOo +0o0OooOo0 +0o0OooOoO +0o0Oooo00 +o0Oooo0 +o0Oooo0o +o0Oooo +o0OoooO +o0OoooOO +o0OoooOo +o0Ooooo0 +o0Ooooo +o0OooooO +0o0Oooooo +o0o0000 +o0o0o00O +o0o0000o +o0o000 +o0o000O0 +o0o000O +o0o000OO +o0o000Oo +o0o000o0 +0o0o000o +o0o000oO +o0o000oo +o0o00 +o0o00O00 +o0o00O0 +o0o00O0O +o0o00O0o +o0o00O +o0o00OO0 +0o0o00OO +o0o00OOO +o0o00OOo +o0o00Oo0 +o0o00Oo +o0o00OoO +o0o00Ooo +o0o00o00 +o0o00o0 +o0o00o0O +0o0o00o0o +o0o00o +o0o00oO0 +o0o00oO +o0o00oOO +o0o00oOo +o0o00oo0 +o0o00oo +o0o00ooO +o0o00ooo +0o0o0 +o0o0O000 +o0o0O00 +o0o0O00O +o0o0O00o +o0o0O0 +o0o0O0O0 +o0o0O0O +o0o0Oo00 +o0o0O0OO +0o0o0O0Oo +o0o0O0o0 +o0o0O0o +o0o0O0oO +o0o0O0oo +oO0Oo +oO0OO +o0ooo +o0o0O +o0o0OoO0 +0o0o0OO00 +o0o0OO0 +o0o0OO0O +o0o0OO0o +o0o0OO +o0o0OOO0 +o0o0OOO +o0o0OOOO +o0o0OOOo +o0o0OOo0 +0o0o0OOo +o0o0OOoO +o0o0OOoo +o0o0Oo0 +o0o0Oo0O +o0o0Oo0o +o0o0OoO +o0o0OoOO +o0o0OoOo +o0o0Ooo0 +000o0o0Ooo +00o0o0OooO +00o0o0Oooo +00o0o0o000 +00o0o0o00 +00o0o0o00o +00o0o0o0 +00o0o0o0O0 +00o0o0o0O +00o0o0o0OO +00o0o0o0Oo +0o0o0o0o0 +0o0o0o0o +0o0o0o0oO +0o0o0o0oo +0o0o0o +0o0o0oO00 +0o0o0oO0 +0o0o0oO0O +0o0o0oO0o +00o0oo00 +0o0o0oO +0o0o0oOO0 +0o0o0oOO +0o0o0oOOO +0o0o0oOOo +0o0o0oOo0 +0o0o0oOo +0o0o0oOoO +0o0o0oOoo +00o0o0oo00 +0o0o0oo0 +0o0o0oo0O +0o0o0oo0o +0o0o0oo +0o0o0ooO0 +0o0o0ooO +0o0o0ooOO +0o0o0ooOo +0o0o0ooo0 +00o0o0ooo +0o0o0oooO +0o0o0oooo +0o0o +0o0oO0000 +0ooooooO +0o0oO000 +0o0oO000O +0o0oO000o +0o0oO00 +00o0oO00O0 +0o0oO00O +0o0oO00Oo +0o0oO00OO +0o0oO00o0 +0o0oO00o +0o0oO00oO +0o0oO00oo +0o0oO0 +0o0oO0O0 +00o0oO0O +0o0oO0OO0 +0o0oO0OO +0o0oO0OOO +0o0oO0Oo +0o0oO0OoO +0o0oO0o00 +0o0oO0o0 +0o0oO0o0o +0o0oO0o0O +00o0oO0o +0o0oO0oO0 +0o0oO0oO +0o0oO0oOO +0o0oO0oo0 +0o0oO0oo +0o0oO0ooO +0o0oO0ooo +0o0oO +0o0oOO000 +00o0oOO00 +0o0oOO00O +0o0oOO00o +0o0oOO0 +0o0oOO0O0 +0o0oOO0O +0o0oOo0oO +0o0oOo0oo +0o0oOO0OO +0o0oOO0o0 +00o0oOO0o +0o0oOO0oO +0o0oOO0oo +0o0oOOO00 +0o0oOOO0 +0o0oOOO0O +0o0oOOO +0o0oOOOO0 +0o0oOOOO +0o0oOOOOO +00o0oOOOOo +0o0oOOOo0 +0o0oOOOo +0o0oOOOoO +0o0oOOo00 +0o0oOOo0 +0o0oOOo0O +0o0oOOo0o +0o0oOOoO0 +0o0oOOoO +0o0oOoO00 +o0oOOoOO +o0oOooOO +o0oOOoOo +o0oOOoo0 +o0oOOoo +o0oOOooO +o0oOOooo +o0oOo00 +o0oOo00O +0o0oOo00o +o0oOo0 +o0oOo0O +o0oOo0OO +o0oOo0Oo +o0oOo0o +o0oOo +o0oOoO0 +o0oOoO0o +o0oOoOo0 +0o0oOoO0O +o0oOoO +o0oOoOO0 +o0oOoOO +o0oOoOOO +o0oOoOOo +o0oOoOo +o0oOoOoO +o0oOoo0 +o0oOoo0O +0o0oOoo0o +o0oOoo +o0oOooO +o0oOooOo +o0oOooo0 +o0oOooo +o0oOoooO +o0oOoooo +o0oo0000 +o0oo000O +0o0oo000o +o0oo00O0 +o0oo00O +o0oo00OO +o0oo00Oo +o0oo00o0 +o0oo00o +o0oo00oO +o0oo00oo +o0oo0 +0o0oo0O00 +o0oo0O0 +o0oo0O0O +o0oo0O0o +o0oo0o +o0oo0O +o0oo0OO0 +o0oo0OO +o0oo0OOO +o0oo0OOo +0o0oo0Oo0 +o0oo0Oo +o0oo0OoO +o0oo0Ooo +o0oo0o00 +o0oo0o0 +o0oo0o0O +o0oo0o0o +o0oo0oO0 +o0oo0oO +0o0oo0oOO +o0oo0oOo +o0oo0oo0 +o0oo0oo +o0oo0ooO +o0oo0ooo +o0ooO000 +o0ooO00 +o0ooO00O +o0ooO00o +0o0ooO0 +o0ooO0O +o0ooO0OO +o0ooO0Oo +o0ooO0o0 +o0ooO0o +o0ooO0oO +o0ooO0oo +o0ooOO0O +o0ooOO0o +00o0ooOOO +0o0ooOOoO +0o0ooOo00 +0o0ooOo0 +0o0ooOo0o +0o0ooOo0O +0o0ooOo +0o0ooOooO +0o0ooOoO0 +0o0ooOoOo +0o0ooOoo0 +o0ooOoo +o0ooOooo +o0ooo000 +o0ooo00O +o0ooo00o +o0ooo0 +o0ooo0O +o0ooo0o0 +o0ooo0o +0o0ooo0oO +o0ooo0oo +o0oooO00 +o0oooO0 +o0oooO0o +o0oooO0O +o0oooO +o0oooOO0 +o0oooOO +o0oooOOo +0o0oooOOO +o0oooOo0 +o0oooOo +o0oooOoO +o0oooo00 +o0oooo0 +o0oooo0O +o0oooo0o +o0oooo +o0ooooO0 +0o0ooooO +o0ooooOo +o0ooooo0 +ooo0Oo0 +o0ooooo +o0oooooO +o0oooooo +o +oO00000 +oO00000o +0oO0000 +oO0000O +oO0000Oo +oO0000o0 +oO0000o +oO0000oO +oO0000oo +oO000 +oO000O0 +oO000O0O +0oO0Ooo00 +oO0Ooooo +oO000O0o +oO0o0o +oO000O +oO0OoOO0 +oO0OOooo +oO0OoOOO +oO0OOoO0 +oO0Oo0oo +0oO000OOO +oO0OO0oo +oO0OOooO +oO000OOo +oO0OoOoO +ooOOOOoo +oO0OOo0o +oO000Oo0 +oO000Oo +oO0OO0OO +0oO0OoOoo +oO0OOoo0 +oO000OoO +oO0OOo0O +oO000Ooo +oO000o00 +oO000o0 +ooOOOOOo +oO000o0o +oO000o +0oO0Oo0OO +oO000oO0 +oO000oO +oO0Ooo0O +oO000oOO +oO0Ooo0o +oO000oOo +oO000oo0 +oO000oo +oO0Oo0Oo +00oO000ooO +0oO0Oo0O0 +0oO000ooo +0oO00 +0oO00O00 +0oO0O0o0o +0ooOOoooO +0oO00O00o +0oO00O0 +0oO00O0O +0oO00O0Oo +oO0O0OoO +ooOOooOo +oO00O0o0 +oO00O0o +oOo00OO0 +oO00O0oO +oO0O0OOo +oO00O0oo +oO00O +0oO00OO0 +ooOOoOoO +oO0O0OOO +oO00OO0O +oO00OO +oO00OOO +oO00OOOo +oOo00Oo0 +ooOOoOOo +oO00OOo0 +0oO00OOo +oO0oOOO0 +oO00OOoO +oOOoOoOO +oO00OOoo +oO00Oo00 +oO00Oo0 +oO00Oo0O +oO00Oo0o +oO00Oo +0oO00oOOO +oO00OoO0 +oO00OoO +oOOoOOO0 +oO00OoOO +oO0oOOOo +oO00OoOo +oO00Ooo0 +oO00Ooo +oO00OooO +0oO00Oooo +oO0OOoOo +oO00o000 +oO00o00 +oO0OooOO +oO0OoOo0 +oO0OOO00 +oO00o00O +oO0OOoOO +oO00o00o +0ooOO0O +ooOOo0 +oO00o0 +oO0OOOoo +oO00o0O0 +oO00o0O +oO0OOOOo +oO00o0OO +oO0OOOoO +oO00o0Oo +0oO0OoO0O +oO0OOO0o +oO00o0o0 +oO00o0o +oO0OOOo0 +oO00o0oO +oO0OOO0O +oO00o0oo +oO00o +oO0OO0oO +0oO0OO000 +oOo00o0o +oO00oO00 +oO00oO0 +oO0OoooO +oOo00ooO +oO00oO0O +oO0OooOo +oOo00ooo +oO00oO0o +0oO00oO +oO00oOO0 +oO00oOO +oO0OO00o +oO00oOOo +oOo00oO0 +oO00oOo0 +oO00oOo +oO0OO00O +ooOOOOoO +00oOo00oOO +0oO00oOoO +0oOo00oOo +0oO00oOoo +0oO0OOOOO +0oO00oo00 +0oO00oo0 +0oO0OO0o0 +0oO00oo0O +0oO00oo0o +0oO00oo +oO00ooO0 +oO00ooO +oOo000Oo +oO00ooOO +oO00ooOo +oOo0000O +oO00ooo0 +oO00ooo +oOo000oo +0oO00oooO +oOo000o0 +oO00oooo +oO0 +oO0O000 +oO0Oo00O +ooOOoOo0 +oO0O000o +oO0O00 +oO0O00O +0oO0O00Oo +oO0O00o0 +oO0O00o +oO0O00oO +oO0O00oo +oOoo0 +oO0O0 +oO0O0O00 +oO0O0O0 +ooOOOoO0 +0oO0O0O0O +ooOOOoo0 +oO0O0O0o +oO0O0o +oO0O0O +oO0O0OO +oOo0oooO +ooOOOoOo +oO0O0Oo0 +oO0O0Oo +0oO0O0o0 +oO0O0o0O +oO0O0oO0 +oO0O0oO +oOo0o0oO +ooOOO0Oo +oO0O0oOO +oO0O0oOo +oOo00OOo +oO0O0oo0 +0oO0O0oo +oOo00OOO +ooOOO00O +oO0O0ooO +oO0O0ooo +ooo0o +oO0O +oO0OO00 +oO0OO0O +oO0OO0Oo +0oO0OO0o +oO0OOO0 +oO0OOO +oO0OooO0 +oO0OOOO0 +oO0OOOO +oO0OOOo +oO0OOo0 +oO0OOo +oO0OOoO +0oO0OOoo +oO0Oo00 +oO0Oo0 +oO0Oo0O +oO0Oo0o0 +oO0Oo0o +oO0OoO00 +oO0OoO0 +oO0OoO +oO0OoOO +0oO0OoOo +oO0Ooo0 +oO0Ooo +oO0OooO +oO0Oooo +oO0o0000 +oO0o000 +oO0o000O +ooOOoOoo +oO0o000o +00oO0o00 +0oO0o0oo0 +0oO0o00O0 +0oO0o00O +0oO0o00OO +0oO0o00Oo +0oO0o00o0 +0oO0o00o +0oO0o00oO +0oO0o00oo +0oO0o0 +oO0o0O00 +oO0o0O0 +oO0o0O0O +oO0o0O0o +oO0o0O +oO0o0OO0 +oO0o0OO +oO0o0OOO +oO0o0OOo +0oO0o0Oo0 +oO0o0Oo +oO0o0OoO +oO0o0Ooo +oO0o0o00 +oO0o0o0 +oO0o0o0O +oO0o0o0o +oO0o0oO0 +oO0o0oO +0oO0o0oOO +oO0o0oOo +oO0o0oo +oO0o0ooO +oO0o0ooo +oO0o +oO0oO000 +oO0oO00 +oOo00O0O +ooOOoOO0 +0oO0oO00O +oO0oO00o +oO0oO0 +oO0oO0O0 +oO0oO0O +oO0oO0OO +oOo00OoO +ooOOoOOO +oO0oO0Oo +oO0oO0o0 +0oO0oO0o +oO0oO0oO +oO0oO0oo +oOooo +oO0oO +oO0oOoOO +oO0oOO00 +oO0oOO0 +oO0oOO0O +oO0oOO0o +0oO0oOO +oO0oOOO +oO0oOOOO +oO0oOOo0 +oO0oOOo +oO0oOOoO +oO0oOOoo +oO0oOo00 +oO0oOo0 +oO0oOo0O +0oO0oOo0o +oO0oOo +oO0oOoO0 +oO0oOoO +oO0oOooo +oO0oOoOo +oO0oOoo0 +oO0oOoo +oO0oOooO +oO0oo000 +0oO0oo00 +oO0oo00O +oO0oo00o +oO0oo0 +oO0oo0O0 +oO0oo0O +oO0oo0OO +oO0oo0Oo +oO0oo0o0 +oO0oo0o +0oO0oo0oO +oO0oo0oo +oO0oo +oO0ooO00 +oO0ooO0 +oO0ooO0O +oO0ooO0o +oO0ooO +oO0ooOO0 +oO0ooOO +00oO0ooOOO +0oO0ooOOo +0oO0ooOo0 +0oO0ooOo +0oO0ooOoO +0oO0ooOoo +0oO0ooo00 +0oO0ooo0 +0oO0ooo0O +0oO0ooo0o +0oO0ooo +oOo00o0O +oO0oooO0 +oO0oooO +oO0oooOO +oOo00oo0 +oO0oooOo +oOo000OO +oO0oooo0 +oO0oooo +0oO0ooooO +oOo00o00 +oO0ooooo +oO +oOO00000 +oOO0000 +oOO0000O +oOO0000o +oOO000 +oOO000O0 +0oOO000O +oOO000OO +oOO000Oo +oOO000o0 +oOO000o +oOO000oO +oOO000oo +oOO00 +oOO00O00 +oOO00O0 +0ooooOO00 +oOO00O0O +oOO00O0o +oOO00OO0 +oOO00OO +oOO00OOO +oOO00OOo +oOO00Oo0 +oOO00Oo +ooooO000 +0oOO00OoO +ooooOoOo +oOO00Ooo +oOO00o00 +oOO00o0 +oOO00ooo +oOO00o0O +oOO00o0o +oOO00o +oOO00oO0 +0oOO00oO +ooooOOo0 +ooooO0oo +oOO00oOO +oOO00oOo +oOO00oo +oOO00ooO +oOO0 +ooooOoOO +oOO0O000 +0oOO0O00 +oOO0O00O +oOO0O00o +oOO0O0 +oOO0O0O0 +oOO0O0O +oOO0O0OO +oOO0O0Oo +oOO0O0o0 +oOO0O0o +0oOO0O0oO +oOO0O0oo +oOO0O +oOO0OO0 +oOO0OO0O +oOO0OO0o +oOO0OO +oOO0OOO +oOO0OOOO +oOO0OOOo +0oOO0OOo0 +oOO0OOo +oOO0OOoO +oOO0OOoo +oOO0Oo00 +oOO0Oo0 +oOO0Oo0O +oOO0Oo0o +oOO0Oo +oOO0OoO0 +00oOO0OoO +0oOO0OoOO +0oOO0OoOo +0oOO0Ooo0 +0oOO0Ooo +0oOO0OooO +0oOO0Oooo +0oOO0o00 +0ooooOOoo +0oOO0o00o +0oOO0o0 +oOO0o0O0 +oOO0o0O +oOO0o0OO +oOO0o0Oo +ooooO0O0 +oOO0o0o0 +oOO0o0o +oOO0o0oO +oOO0o0oo +0oOO0o +oOO0oO00 +oOO0oO0 +oOO0oO0O +oOO0oO0o +oOO0oO +oOO0oOO0 +oOO0oOO +oOO0oOOO +oOO0oOOo +0oOO0oOo0 +oOO0oOo +oOO0oOoO +oOO0oOoo +oOO0oo00 +oOO0oo0 +oOO0oo0O +oOO0oo0o +oOO0oo +oOO0ooO0 +0oOO0ooO +oOO0ooOO +oOO0ooOo +oOO0ooo0 +oOO0ooo +oOO0oooO +oOO0oooo +oOO +oOOO000 +oOOO000o +0oOOO0O +oOOOoO +oOOO00 +oOOO00O +oOOO00Oo +oOOO00o0 +oOOO00o +oOOO00oO +oOOO00oo +oOOO0 +0oOOO0oOO +oOOO0O0 +oOOO0O0o +oOOO0OO0 +oOOO0OO +oOOO0OOO +oOOO0OOo +oOOO0Oo +oOOO0OoO +oOOO0Ooo +0oOOO0o00 +oOOO0o0 +oOOO0o0O +oOOO0o0o +oOOOOO +oOOO0o +oOOO0oO0 +oOOO0oO +oOOO0ooo +oOOO0ooO +0oOOO0oOo +oOOO0oo0 +oOOO0oo +oOOO +oOOOO0OO +oOOOO000 +oOOOO00 +oOOOoo00 +oOOOO00O +oOOOO00o +0oOOOO0 +oOOOO0O0 +oOOOO0O +oOOOO0Oo +oOOOO0o0 +oOOOO0o +oOOOO0oO +oOOOO0oo +oOo0o +oOOOO +00oOOOOO00 +0oOOOOO0 +0oOOOOO0O +0oOOOOO0o +0oOOOOOO0 +0oOOOOOO +0oOOOOOOO +0oOOOOOOo +0oOOOOOo0 +0oOOOOOo +0oOOOOOoO +oOOOOOoo +oOOOOo00 +oOOOOo0 +oOOOOo0O +oOOOOo0o +oOOOOo +oOOOOoO0 +oOOOOoO +oOOOOoOO +0oOOOOoOo +oOOOOoo0 +oOOOOoo +oOOOOooO +oOOOOooo +oOOOo000 +oOOOo00 +oOOOo00O +oOOOo00o +oOOOo0 +0oOOOo0O0 +oOOOo0O +oOOOo0OO +oOOOo0Oo +oOOOo0o0 +oOOOo0o +oOOOo0oO +oOOOo0oo +oOOOo +oOOOoO00 +0oOOOoO0 +oOOOoO0O +oOOOoO0o +oOOOoOO0 +oOOOoOO +oOOOoOOO +oOOOoOOo +oOOOoOo0 +oOOOoOo +oOOOoOoO +0oOOOoOoo +oOOOoo0 +oOOOoo0O +oOOOoo0o +oOOOoo +oOOOooO0 +oOOOooO +oOOOooOO +oOOOooOo +oOOOooo0 +0oOOOooo +oOOOoooO +oOOOoooo +oOOo0000 +oOOo000 +oOOo000O +oOOo000o +oOOo00 +oOOo00O0 +oOOo00O +0oOOo00OO +oOOo00Oo +oOOo00o0 +oOOo00o +oOOo00oO +oOOo00oo +oOOo0 +oOOo0O00 +oOOo0O0 +oOOo0O0O +0oOOo0O0o +oOOo0O +oOOo0OO0 +oOOo0OO +oOOo0OOO +oOOo0OOo +oOOo0Oo0 +oOOo0Oo +oOOo0OoO +oOOo0Ooo +0oOOo0o00 +oOOo0o0 +oOOo0o0O +oOOo0o0o +oOOo0o +oOOo0oO0 +oOOo0oO +oOOo0oOO +oOOo0oOo +oOOo0oo0 +00oOOo0oo +0oOOo0ooO +0oOOo0ooo +0oOOo +0oOOoO000 +0oOOoO00 +0oOOoO00O +0oOOoO00o +0oOOoO0 +0oooO0oOO +0oOOoO0O0 +oOOoO0O +oOOoO0OO +oOOoO0Oo +oOOoO0o +oOOoO0oO +oOOoO0oo +oOOoO +oOOoOO0o +oOOoOO00 +0oOOoOO0 +oOOoOO0O +oOOoOOO +oOOoOOOO +oOOoOOOo +oOOoOOo0 +oOOoOOo +oOOoOOoO +oOOoOOoo +oOOoOo00 +0oOOoOo0 +oOOoOo0O +oOOoOo0o +oOOoOo +oOOoOoO0 +oOOoOoO +oOOoOoOo +oOOoOoo0 +oOOoOoo +oOOoOooO +0oOOoOooo +oOOoo000 +oOOoo00 +oOOoo00O +oOOoo00o +oOOoo0 +oOOoo0O0 +oOOoo0O +oOOoo0OO +oOOoo0Oo +0oOOoo0o0 +oOOoo0o +oOOoo0oO +oOOoo0oo +oOOoo +oOOooO00 +oOOooO0 +oOOooO0O +oOOooO0o +oOOooO +0oOOooOO0 +oOOooOO +oOOooOOO +oOOooOOo +oOOooOo0 +oOOooOo +oOOooOoO +oOOooOoo +oOOooo00 +oOOooo0 +0oOOooo0O +oOOooo0o +oOOooo +oOOoooO0 +oOOoooO +oOOoooOO +oOOoooOo +oOOoooo0 +oOOoooo +oOOooooO +0oOOooooo +oOo0000 +oOo000 +oOo000O +oOo000o +oOo00O0 +oOo00O +oOo00OO +oOo0o00 +oOo00Oo +0oOo00o0 +oOo00o +oOo00oO +oOo00oo +oOo0 +oOo0O000 +oOo0O00 +oOo0O00O +oOo0oO0o +oOo0O00o +000oOo0oo +00oOo0O0 +00oOo0O0O0 +00oOo0O0O +00oOo0O0OO +00oOo0O0Oo +00oOo0O0o0 +00oOo0O0o +00oOo0O0oO +00oOo0O0oo +00oOo0O +0oOo0OO00 +0oOoo00o +0oOo0OO0 +0oOo0OO0O +0oOo0OO0o +0oOo0OO +0oOo0OOO0 +0oOo0OOO +0oOo0OOOO +00oOo0OOOo +0oOo0OOo0 +0oOo0OOo +0oOo0OOoO +0oOo0OOoo +0oOo0Oo00 +0oOo0Oo0 +0oOo0Oo0O +0oOo0Oo0o +0oOo0Oo +00oOo0OoO0 +0oOo0OoO +0oOo0OoOO +0oOo0OoOo +0oOo0Ooo0 +0oOo0Ooo +0oOo0OooO +0oOo0Oooo +0oOo0o000 +0oOo0o00O +00oOo0o00o +0oOo0o0 +0oOo0o0O +0oOo0o0OO +0oOo0o0Oo +0oOo0o0o0 +0oOo0o0o +0oOo0oO00 +0oOo0oO0 +0oOo0oO0O +00oOo0oO +0oOo0oOO0 +0oOo0oOO +0oOo0oOOO +0oOo0oOOo +0oOo0oOo0 +0oOo0oOo +0oOo0oOoO +0oOo0oOoo +0oOo0oo00 +00oOo0oo0 +0oOo0oo0O +0oOo0ooO0 +0oOo0ooO +0oOo0ooOO +0oOo0ooOo +0oOo0ooo0 +0oOo0ooo +0oOo +0oOooOOoO +00oOoO0000 +0oOoO000 +0oOoO000O +0oOoO000o +0oOoO00 +0oOoO00O0 +0oOoO00O +0oOoO00OO +0oOoO00Oo +0oOoO00o0 +00oOoO00o +0oOoO00oO +0oOoO00oo +0oOoO0 +0oOooOo0O +0oOoO0O00 +0oOoO0O0 +0oOoO0O0O +0oOoO0O0o +0oOoO0o +00oOoO0O +0oOoO0OO0 +0oOoO0OO +0oOoO0OOO +0oOoO0OOo +0oOoO0Oo0 +0oOoO0Oo +0oOoO0OoO +0oOoO0Ooo +0oOooOo00 +00oOoO0o00 +0oOoO0o0 +0oOoO0o0O +0oOoOoOOO +0oOoO0o0o +0oOoO0oO0 +0oOoO0oO +0oOoO0oOO +0oOoO0oOo +0oOooOo0o +0oOoO0oo0 +oOoO0oo +oOoO0ooO +oOoO0ooo +oOoO +oOoOO000 +oOoOO00 +oOoOO00O +oOoOO00o +oOoOO0 +0oOoOO0O0 +oOoOO0O +oOoOOo0O +oOoOO0OO +oOoOO0Oo +oOoOO0o0 +oOoOO0o +oOoOO0oO +oOooOOOO +oOoOO0oo +0oOoOO +oOoOOO00 +oOoOOO0 +oOoOOO0O +oOoOOO0o +oOoOOO +oOoOOOO0 +oOoOOOO +oOoOOOOO +oOoOOOOo +0oOoOOOo0 +oOoOOOo +oOoOOOoO +oOooOooO +oOoOOOoo +oOoOOo00 +oOoOOo0 +oOoOOo0o +oOoOOo +oOoOOoO0 +0oOoOOoO +oOoOOoOO +oOoOOoOo +oOoOOoo0 +oOoOOoo +oOoOOooO +oOoOOooo +oOoOo000 +oOoOo00 +oOoOo00O +0oOoOo00o +oOoOo0 +oOoOoo0O +oOoOo0O0 +oOoOo0O +ooOOO0oo +oOooo0Oo +oOooo0oo +oOoOo0OO +ooOOO0o0 +0oOoOo0Oo +oOoOo0o0 +oOoOo0o +oOoOo0oO +oOoOo0oo +oOoOo +ooOOOooo +oOoOoO00 +oOoOoO0 +oOoOoO0O +0ooOOOooO +oOooooOO +oOoOoO0o +oOoOoO +oOoOoOO0 +oOoOoOO +oOoOoOOo +oOoOoOo0 +oOoOoOo +oOooOOoo +0oOoOoOoO +oOoOoOoo +ooOOO0oO +oOooo0oO +oOoOoo00 +oOoOoo0 +ooOOO0O0 +oOooo0O0 +oOoOoo0o +oOoOoo +00ooOOOo0o +0oOoooooo +0oOoOooO0 +0oOoOooO +0oOoOooOO +0ooOOOo0O +0oOoooooO +0oOoOooOo +0oOoOooo0 +0oOoOooo +0ooOOOo00 +oOooooo0 +oOoOoooO +oOoOoooo +oOoo0000 +oOoo000 +oOoo000O +oOoo000o +oOoo00 +oOoo00O0 +0oOoo00O +oOoo00OO +oOoo00Oo +oOoo00o0 +oOoo00oO +oOoo00oo +oOoo0Oo0 +oOoo0O00 +oOoo0O0 +oOoo0O0O +0oOoo0O0o +oOoo0O +oOoo0OO0 +oOoo0oO +oOooo00 +oOoo0OO +oOoo0OOO +oOoo0OOo +oOoo0Oo +oOoo0OoO +0oOoo0Ooo +oOoo0o00 +oOoo0o0 +oOoo0o0O +oOoo0o0o +oOoo0o +oOoo0oO0 +oOoo0oOO +oOoo0oOo +oOoo0oo0 +0oOoo0oo +oOoo0ooO +oOoo0ooo +oOoo +oOooO000 +oOooO00 +oOooO00O +oOooO00o +oOooO0 +oOooO0O0 +0oOooO0O +oOooO0OO +oOooO0Oo +oOooO0o0 +oOooO0o +oOooO0oO +oOooO0oo +oOooO +oOooOO00 +oOooOO0 +0oOooOOo0 +oOooOO0O +oOooOO0o +oOooOO +oOooOOO0 +oOooOOO +oOooOOOo +oOooOOo +oOooOo0 +oOooOo +0oOooOoO0 +oOooOoO +oOooOoOO +oOooOoOo +oOooOoo0 +oOooOoo +oOooOooo +oOooo000 +oOooo00O +oOooo00o +0oOoooO +oOooo0 +oOooo0O +oOooo0OO +oOooo0o0 +oOoooO00 +oOoooO0 +oOoooO0O +oOoooO0o +oOoooOO0 +00oOoooOO +0oOoooOOO +0oOoooOOo +0oOoooOo0 +0oOoooOo +0oOoooOoO +0oOoooOoo +0oOoooo00 +0oOoooo0 +0oOoooo0O +0oOoooo0o +oOoooo +oOooooO0 +oOooooO +oOooooOo +oOooooo +oo000000 +oo00000 +oo00000O +oo00000o +0oo0000 +oo0000O0 +oo0000O +oo0000OO +oo0000Oo +oo0000o0 +oo0000o +oo0000oO +oo0000oo +oo000 +0oo000O00 +oo000O0 +oo000O0O +oo000O0o +oo000O +oo000OO0 +oo0OOOO +oo000OO +oo000OOO +oo000OOo +0oo000Oo0 +oo000Oo +oo000OoO +oo000Ooo +oo000o00 +oo000o0 +oo000o0O +oo000o0o +oo000oO0 +oo000oO +0oo000oOO +oo000oOo +oo000oo0 +oo000oo +oo000ooO +oo00 +oo00O00o +oo00O000 +oo00O00 +oo00O00O +0oo00O0 +oo00O0O0 +oo00O0O +oo00Oo0O +oo00O0OO +oo00O0Oo +oo00O0o0 +oo00O0o +oo00O0oO +oo00O0oo +0oo00O +oo00OO00 +oo00OO0 +oo00OO0O +oo00OO0o +oo00OO +oo00OOO0 +oo00OOO +oo00OOOO +oo00OOOo +0oo00OOo0 +oo00OOo +oo00OOoO +oo00OOoo +oo00Oo00 +oo00Oo0 +oo00Oo0o +oo00Oo +oo00OoO0 +oo00OoO +0oo00OoOO +oo00OoOo +oo00Ooo0 +oo00Ooo +oo00OooO +oo00Oooo +oo00o000 +oo00o00 +oo00o0o0 +oo00o00O +00oo00o00o +0oo00o0 +0oo00o0oo +0oo00ooOO +0oo00o0O0 +0oo00o0O +0oo00o0OO +0oo00o0Oo +0oo00o0o +0oo00o0oO +0oo00oO00 +oo00oO0 +oo00oO0O +oo00oO0o +oo00oOoO +oo00oOO0 +oo00oOo +oo00oOO +oo00oOOO +oo00oOOo +0oo00oOo0 +oo00oOoo +oo00oo00 +oo00oo0 +oo00oo0O +oo00oo0o +oo00oo +oo00ooO0 +oo00ooO +oo00ooOo +0oo00ooo0 +oo00ooo +oo00oooO +oo00oooo +oo0 +oo0O0000 +oo0O000 +oo0O000O +oo0O000o +oo0O00 +0oo0oOOOO +oo0O00O0 +oo0O00O +oo0O00OO +oo0O00Oo +oo0O00o0 +oo0O00o +oo0O00oO +oo0O00oo +oo0O0 +0oo0O0O00 +oo0O0O0 +oo0O0O0O +oo0O0O0o +oo0O0O +oo0O0OO0 +oo0O0OO +oo0O0OOO +oo0O0OOo +oo0O0Oo0 +0oo0O0Oo +oo0O0OoO +oo0O0Ooo +oo0O0o00 +oo0O0o0 +oo0O0o0O +oo0O0o0o +oo0O0o +oo0O0oO0 +ooo0OoO +0oo0O0oO +oo0oOOoO +oo0O0oOO +oo0O0oOo +oo0O0oo0 +oo0O0oo +oo0O0ooO +oo0O0ooo +oo0OoOO0 +oo0OO00o +0oo0OO00O +oo0OO000 +oo0OO00 +oo0Oo0 +oo0Ooo +oo0OoO +oo0OO0 +oo0OOoOO +oo0OO0Oo +oo0OOo0O +0oo0OOooO +oo0OO0O0 +oo0OO0O +oo0OO0OO +oo0OO0o0 +oo0OO0o +oo0OO0oO +oo0OO0oo +oo0OO +oo0OOO00 +00oo0oOoo +0oo0OOO0 +0oo0OOO0O +0ooo000O0 +0oo0OOO0o +0oo0OOO +0ooo000o0 +0oo0OOOO0 +0oo0OOOOO +0oo0OOOOo +0oo0OOOo0 +oo0OOOo +oo0OOOoO +ooo0000O +ooo00000 +oo0OOOoo +oo0OOo00 +oo0OOo0 +oo0OOo0o +oo0OOo +0oo0OOoO0 +oo0OOoO +oo0OOoOo +oo0OOoo0 +oo0OOooo +oo0Oo000 +oo0Oo00 +oo0Oo00O +oo0Oo00o +oo0Oo0O0 +0oo0Oo0O +oo0Oo0OO +oo0Oo0Oo +oo0Oo0o0 +oo0Oo0o +oo0Oo0oO +oo0Oo0oo +oo0Oo +oo0OoO00 +oo0OoO0 +0oo0OoO0O +oo0OoO0o +oo0OoOO +oo0OoOOO +oo0OoOOo +oo0OoOo0 +oo0OoOo +oo0OoOoO +oo0OoOoo +oo0Ooo00 +0oo0Ooo0 +oo0Ooo0O +oo0Ooo0o +oo0OooO +oo0OooOO +oo0OooOo +oo0Oooo0 +oo0Oooo +oo0OoooO +0oo0Ooooo +oo0o0000 +oo0o000 +oo0o000O +oo0o000o +oo0o00 +oo0o00O0 +oo0o00o +oo0o00O +oo0o00Oo +0oo0o00oO +oo0o00OO +oo0o00o0 +oo0o00oo +oo0o0 +oo0o0O00 +oo0o0O0o +oo0o0o +oo0o0O +oo0o0OO0 +0oo0o0OO +oo0o0OOO +oo0o0OOo +oo0o0Oo0 +oo0o0OoO +oo0o0Ooo +oo0o0o00 +oo0o0o0 +oo0o0o0O +oo0o0o0o +0oo0o0oO0 +oo0o0oO +oo0o0oOO +oo0o0oOo +oo0o0oo0 +oo0o0oo +oo0o0ooO +oo0o0ooo +oo0o +oo0oO000 +00oo0oO00 +0oo0oO0oO +0oo0oO00O +0oo0oO00o +0oo0oO0O0 +0oo0oO0O +0oo0oO0OO +0oo0oO0Oo +0oo0oO0o0 +0oo0oO0o +0oo0oO0oo +oo0oO +oo0oOO00 +oo0oOO0O +oo0oOO0o +oo0oOO +oo0oOOO0 +oo0oOOO +oo0oOOOo +oo0oOOo0 +0oo0oOOoo +oo0oOo00 +oo0oOo0 +oo0oOo0O +oo0oOo0o +oo0oOo +oo0oOoO0 +oo0oOoOO +oo0oOoOo +oo0oOoo0 +0oo0oOooO +oo0oOooo +oo0oo000 +ooo0oOO +oooOooO +oo0oo00 +oo0oo00O +oo0oo00o +oo0oo0 +oo0oo0O0 +0oo0oo0O +oo0oo0OO +oo0oo0Oo +oo0oo0o0 +oo0oo0o +oo0oo0oO +oo0oo0oo +oo0oo +oo0ooO00 +oo0ooO0 +0oo0ooO0O +oo0ooO0o +oo0ooOO0 +oo0ooOO +ooo0O0oo +ooo0oooO +oo0ooOOO +oo0ooOOo +oo0ooOo0 +oo0ooOo +0oo0ooOoO +oo0ooOoo +oo0ooo00 +oo0ooo0 +oo0ooo0o +oo0ooo0O +oo0ooo +oo0oooOO +oo0oooO0 +oo0oooO +0oo0oooOo +oo0oooo0 +oo0oooo +oo0ooooO +oo0ooooo +oo +ooO00000 +oooooOo +ooO0000 +ooO0000O +0ooOooOOO +ooO0000o +ooO0oo +ooO000 +ooO000O0 +ooO000O +ooO000OO +ooO000Oo +ooO000o0 +ooO000o +0ooO000oO +ooO000oo +ooO00 +ooO0OOoo +ooOo000o +ooOo00O0 +ooO00O00 +ooO00O0O +ooO0OOoO +ooO00O0o +00ooO00O +0ooOo00o0 +0ooO00OO0 +0ooO00OO +0ooO0OOOO +0ooO00OOO +0ooO00OOo +0ooO00Oo0 +0ooO00Oo +0ooO00OoO +0ooOo00OO +ooO00Ooo +ooO00o00 +ooO00o0 +ooO00o0O +ooO00o0o +ooO00o +ooO00oO0 +ooO00oO +ooO00oOO +0ooO00oOo +ooO00oo0 +ooO00oo +ooO00ooO +ooO00ooo +ooO0 +ooO0O000 +oooo000 +ooO0O00 +ooO0O00O +0ooO0O00o +ooO0O0 +ooO0O0O0 +ooO0O0O +ooO0O0OO +ooO0O0Oo +ooO0O0o0 +ooO0OOO +ooO0O0o +ooO0O0oO +0ooO0O0oo +ooO0O +ooOo000O +ooO0OO00 +ooO0OO0 +ooO0OO0O +ooO0OO0o +ooO0OO +ooO0OOO0 +ooO0OOOo +0ooO0OOo0 +ooO0OOo +ooO0Oo00 +ooo0oOo +ooO0Oo0 +ooO0Oo0O +ooO0Oo0o +ooO0Oo +ooO0OoO0 +ooO0OoO +0ooO0OoOO +ooO0OoOo +ooO0Ooo0 +ooO0OooO +ooO0Oooo +ooO0o000 +ooO0o00 +ooO0o00O +ooO0o00o +ooO0o0 +0ooO0o0O0 +ooO0o0O +ooO0o0OO +ooO0o0Oo +ooO0o0o0 +ooO0o0o +ooO0o0oO +ooO0o0oo +ooO0o +ooO0oO00 +0ooO0oO0 +ooO0oO0O +ooOo00Oo +ooO0oO0o +ooO0oO +ooO0oOO0 +ooO0oOO +ooO0oOOO +ooO0oOOo +ooO0oOo0 +0ooo0OOo +ooO0oOo +ooO0oOoO +ooO0oOoo +ooO0oo00 +ooO0oo0 +ooO0oo0O +ooO0oo0o +ooO0ooO0 +ooO0ooO +00ooO0ooOO +0ooO0ooOo +0ooO0ooo0 +0ooO0ooo +0ooO0oooO +0ooO0oooo +0ooo +0ooO +0ooOO0000 +0ooOO000 +0ooOO000O +ooOO000o +ooOO00 +ooOO00O0 +ooOO00O +ooOO00OO +ooOO00Oo +ooOO00o0 +ooOO00o +ooOO00oO +0ooOO00oo +ooOO0 +ooOO0O00 +ooOO0O0 +ooOO0O0O +ooOO0O0o +ooOO0OO0 +ooOO0OO +ooOO0OOO +ooOOo0OO +0ooOO0OOo +ooOO0Oo0 +ooOO0Oo +ooOO0OoO +ooOO0Ooo +ooOO0o00 +ooOO0o0 +ooOO0o0O +ooOO0o0o +ooOO0o +0ooOO0oO0 +ooOO0oO +ooOO0oOO +ooOO0oOo +ooOO0oo0 +ooOO0oo +ooOO0ooO +ooOO0ooo +ooOOO00 +ooOOoO +0ooOOO0 +ooOOO0O +ooOOO0o +ooOOO +ooOOOO00 +ooOOOO0 +ooOOOO0O +ooOOOO0o +ooOOOO +ooOOOOO0 +0ooOOOOO +ooOOOOOO +ooOOOOo +ooOOOo0 +ooOOOo +ooOOOoO +ooOOOoo +ooOOo000 +ooOOo00O +ooOOo00o +0ooOOo0O0 +ooOOo0O +ooOOo0Oo +ooOOo0o0 +ooOOo0o +ooOOo0oO +ooOOo0oo +ooOOo +ooOOoO0 +ooOOoO0O +0ooOOoO0o +ooOOoOo +ooOOoo00 +ooOOoo0 +ooOOoo0O +ooOOoo0o +ooOOoo +ooOOooO0 +ooOOooO +ooOOooOO +0ooOOooo0 +ooOOooo +ooOOoooo +ooOo0000 +ooOo000 +ooOo00 +ooOo00oO +ooOo00oo +ooOo0 +ooOo0O00 +00ooOo0o0 +0ooOo0O0 +0ooOo0oOo +0ooOo0O0O +0ooOo0O0o +0ooOo0O +0ooOo0OO0 +0ooOo0OO +0ooOoOo0o +0ooOooOOo +0ooOo0OOO +ooOo0OOo +ooOo0Oo0 +ooOo0Oo +ooOo0OoO +ooOo0Ooo +ooOo0o00 +ooOo0o0O +ooOo0o0o +ooOo0o +0ooOo0oO0 +ooOo0oO +ooOo0oOO +ooOo0oo0 +ooOo0oo +ooOo0ooO +ooOo0ooo +ooOoO000 +ooOoO00 +ooOoO00O +0ooOoO00o +ooOoO0 +ooOoO0O0 +ooOoO0O +ooOoO0OO +ooOoO0Oo +ooOoO0o0 +ooOoO0o +ooOoO0oO +ooOoO0oo +0ooOoO +ooOoOoOO +ooOoOO00 +ooOoOO0 +ooOoOOOo +ooOoOO0O +ooOoOO0o +ooOoOO +ooOoOOO0 +ooOoOOO +0ooOoOOOO +ooOoOOo0 +ooOoOOo +ooOoOOoO +ooOoOOoo +ooOoOo00 +ooOoOo0 +ooOoOo0O +ooOoOo +ooOoOoO0 +0ooOoOoO +ooOoOoOo +ooOoOoo0 +ooOoOoo +ooOoOooO +ooOoOooo +ooOoo000 +ooOoo00 +ooOooo0o +ooOoo00O +0ooOoo00o +ooOoo0 +ooOoo0O0 +ooOoo0O +ooOoo0OO +ooOoo0Oo +ooOoo0o0 +ooOoo0o +ooOoo0oo +ooOoo0oO +0ooooO +ooOoo +ooOooO00 +ooOooO0 +ooOooO0O +ooOooO0o +ooOooo +ooOooO +ooOooOO0 +ooOooOO +0ooOooOo0 +ooOooOo +ooOooOoO +ooOooOoo +ooOooo00 +ooOooo0 +ooOooo0O +ooOoooO0 +ooOoooO +ooOoooOO +000ooOoooOo +00ooOoooo0 +00ooOoooo +00ooOooooO +00ooOooooo +00ooo0000 +00ooo000oo +00ooo0000o +00ooo000 +00ooo000O +00ooo000OO +0ooo000Oo +0ooo000o +0ooo000oO +0ooo00 +0ooo00O00 +0ooo00oO0 +0ooo00O0O +0ooo00O0o +0ooo00O +00ooo00Oo0 +0ooo00OO0 +0ooo00OO +0ooo00OOO +0ooo00OOo +0ooo00Oo +0ooo00OoO +0ooo00Ooo +0ooo00o00 +0ooo00o0 +00ooo00o0O +0ooo00o0o +0ooo00o +0ooo00oO +0ooo00oOO +0ooo00oOo +0ooo00oo0 +0ooo00oo +0ooo00ooO +0ooo00ooo +00ooo0 +0ooo0O000 +0ooo0O00 +0ooo0O00O +0ooo0O00o +0ooo0oo +0ooo0o0 +0ooo0oO +0ooo0O0 +0ooo0O0O0 +00ooo0O0O +0ooo0O0OO +0ooo0O0Oo +0ooo0O0o0 +0ooo0O0o +0ooo0O0oO +0ooo0O +0ooo0OO00 +0ooo0OO0 +0ooo0OO0O +00ooo0OO0o +0ooo0Oo +0ooo0OO +0ooo0OOO0 +0ooo0OOO +0oooo00oo +0ooo0OOoO +0ooo0OOOO +0ooo0OOOo +0ooo0OOo0 +00ooo0OOoo +0ooo0Oo00 +0ooo0Oo0O +0ooo0Oo0o +0ooo0OoO0 +0ooo0OoOO +0ooo0OoOo +0ooo0Ooo0 +0ooo0OooO +0ooo0Oooo +00ooo0o000 +0ooo0o00 +0ooo0o00O +0ooo0o00o +0ooo0o0OO +0ooo0o0O0 +0ooo0o0O +0ooo0o0Oo +0ooo0o0o0 +0ooo0o0o +00ooo0o0oO +0ooo0o0oo +0ooo0oO00 +0ooo0oO0 +0ooo0oO0O +0ooo0oO0o +0ooo0oOO0 +0ooo0oOOO +0ooo0oOOo +0ooo0oOo0 +00ooo0oOoO +0ooo0oOoo +0ooo0oo00 +0ooo0oo0 +0ooo0oo0O +0ooo0oo0o +0ooo0ooO0 +0ooo0ooO +0ooo0ooOO +0ooo0ooOo +0ooo0ooo0 +ooo0oooo +oooO0oo0 +oooO0000 +oooO000 +oooO00o0 +oooO000O +oooO0ooo +oooO000o +ooooo0 +0oooooo +ooooOo +oooO00 +oooO00O0 +oooO00O +oooO00Oo +oooO00OO +oooO00o +ooooOOOo +ooooOOOO +0ooooOo00 +oooO0oO0 +oooO00oO +oooO00oo +oooO0 +oooO0O00 +oooO0O0 +oooO0O0O +oooO0O0o +oooO0O +0oooOOo0O +oooO0OO0 +oooO0OO +oooO0OOO +oooO0OOo +oooO0Oo0 +oooO0Oo +oooO0OoO +oooO0Ooo +oooO0o00 +0oooO0o0 +oooO0o0O +oooO0o0o +oooooO +oooO0o +oooO0oO +oooO0oo +oooO0ooO +oooO +oooOooOO +0oooOO000 +oooOO00 +oooOO0o0 +oooOO00O +oooOO00o +oooOO0O0 +oooOO0O +oooOO0OO +oooOO0Oo +oooOO0o +0oooOO0oO +oooOO0oo +oooOO +oooOOO00 +oooOOO0 +oooOOO0O +oooOOO0o +oooOOO +oooOOOO0 +oooOOOO +0oooOOOOO +oooOOOOo +oooOOOo0 +oooOOOo +oooOOOoO +oooOOOoo +oooOOo00 +oooOOo0 +oooOOo0o +oooOOo +0oooOOoO0 +oooOOoO +oooOOoOO +oooOOoOo +oooOOoo0 +oooOOoo +oooOOooO +oooOOooo +oooOo000 +oooOo00 +00oooOo00O +0oooOo00o +0oooOo0 +0oooOo0oo +0oooOo0O0 +0oooOo0O +0oooOo0OO +0oooOo0Oo +0oooOo0o0 +0oooOo0o +0oooOo0oO +oooOo +oooOoO00 +oooOoO0 +oooOoO0O +oooOoO0o +oooOoOOO +oooOoOO0 +oooOoOO +oooOoOOo +0oooOoOo0 +oooOoOo +oooOoOoO +oooOoOoo +oooOoo00 +oooOoo0 +oooOoo0O +oooOoo0o +oooOooO0 +oooOooOo +0oooOooo0 +oooOooo +oooOoooO +oooOoooo +oooo0000 +oooo000O +oooo000o +oooo00 +oooo00o0 +oooo00O0 +0oooo00O +oooo00OO +oooo00Oo +oooo00oO +oooo0 +oooo0O00 +oooo0O0 +oooo0O0O +oooo0O0o +oooo0O +0oooo0OO0 +oooo0OO +oooo0OOO +oooo0OOo +oooo0Oo0 +oooo0Oo +oooo0OoO +oooo0Ooo +oooo0o00 +oooo0o0 +0oooo0o0O +oooo0o0o +oooo0o +oooo0oO0 +oooo0oO +oooo0oOO +oooo0oOo +oooo0oo0 +oooo0oo +oooo0ooO +0oooo0ooo +oooo +ooooO00 +ooooOOoO +ooooO00O +ooooO00o +ooooO0 +ooooO0O +ooooO0OO +ooooO0Oo +0ooooO0o0 +ooooO0o +ooooO0oO +ooooOO0 +ooooOO0o +ooooOO +ooooOoo0 +ooooOOO0 +ooooOOO +ooooOOo +0ooooOo0 +ooooOo0O +ooooOo0o +ooooOoO0 +ooooOoO +ooooOoo +ooooOooO +ooooOooo +ooooo000 +ooooo00 +00ooooo00O +0ooooo00o +0ooooo0O0 +0ooooo0O +0ooooo0OO +0ooooo0Oo +0ooooo0o0 +0ooooo0o +0ooooo0oO +0ooooo0oo +0ooooo +oooooO00 +oooooO0 +oooooOo0 +oooooO0O +oooooO0o +oooooOO0 +oooooOO +oooooOOO +oooooOOo +0oooooOoO +oooooOoo +oooooo00 +oooooo0 +oooooo0O +oooooo0o +ooooooO0 +ooooooOO +ooooooOo +ooooooo0 +0oooooooO +oooooooo +O +O0 +O00 +O000 +O0000 +O00000 +O000000 +O0000000 +O000000O +O00000O +O00000O0 +O00000OO +O0000O +O0000O0 +O0000O00 +O0000O0O +O0000OO +O0000OO0 +O0000OOO +O000O +O000O0 +O000O00 +O000O000 +O000O00O +O000O0O +O000O0O0 +O000o0oO +O000Oo +O000OO0 +O000oo00 +O000OO0O +O000OOO +O000OOO0 +O000OOOO +O00O +O00O0 +O00O00 +O00O000 +O00O0000 +O00O000O +O00O00O +O00O00O0 +O00O00OO +O00O0O +O00O0O0 +O00O0O00 +O00O0O0O +O00O0oO +O00O0OO0 +O00O0OOO +O00OO +O00OO0 +O00OO00 +O00Oo000 +O00Oo00O +O00Oo0O +O00Oo0O0 +O00oo0oO +O00OOO +O00OOO0 +O00OoO00 +O00ooo0O +O00OOOO +O00oOoO0 +O00ooooO +O0O +O0O0 +O0O00 +O0O000 +O0O0000 +o0o00000 +o0o0000O +O0O000O +o0O000O0 +o0O000OO +O0O00O +O0O00O0 +o0O00O00 +o0O00O0O +O0O00oo +o0O00OO0 +o0O00OOo +O0o0o +O0O0O0 +O0O0O00 +o0O0O000 +o0O0O00o +O0O0O0O +o0O0O0O0 +o0O0O0OO +O0O0Oo +O0o0oo0 +o0O0OO00 +o0O0OO0o +O0o0ooo +o0O0OOo0 +o0O0OOOO +O0OO +O0OO0 +O0OO00 +O0OO000 +o0OO0000 +o0OO000O +o0Oo00O +o0OO00O0 +o0OO00oO +O0OO0o +O0OO0O0 +o0OO0O00 +o0oO0O0O +O0OO0OO +o0oO0Oo0 +o0oO0oOo +O0OOO +O0OOo0 +o0OoO00 +o0OOO000 +o0OoO00o +O0OoO0o +o0ooo0O0 +o0ooo0Oo +O0oOOO +O0OOOO0 +o0ooOO00 +o0OOOO0O +O0oOOOO +o0ooOOo0 +o0ooooOO +OO +OO0 +OO00 +OO000 +Oo0000 +OO00000 +oO000000 +oO00000O +OO0000O +oO0000O0 +oO0000OO +OO000O +OO000O0 +oO000O00 +oO000o0O +oO000OO +oO000OO0 +oo000ooo +OO00o +OO00o0 +OO00O00 +oO00O000 +oO00O00O +OO00O0O +oO00O0O0 +oO00O0OO +Oo00oO +OO00OO0 +oO00OO00 +oO00OO0o +Oo00oOo +oO00OOO0 +oO00OOOO +OO0O +OO0O0 +OO0O00 +OO0O000 +oO0O0000 +oO0O000O +OO0O00O +oO0O00O0 +oO0O00OO +OO0o0O +OO0O0O0 +oO0O0o00 +oo0o0O0O +Oo0o0OO +oO0O0OO0 +oO0O0Ooo +OO0Oo +OO0OO0 +OO0OO00 +oO0Oo000 +oO0Oo00o +OO0OO0O +oO0OO0O0 +oO0Oo0oO +Oo0OOO +OO0OOO0 +oO0OOo00 +oO0OoO0o +Oo0OoOO +oO0Oooo0 +oO0OoOOo +OOo +OOO0 +OoO00 +OOO000 +OOO0000 +oOo00000 +oOo0000o +OOO000O +oOo000O0 +oOo000oO +OOO00O +ooo00O0 +oOo00O00 +oOo00O0o +OOO00OO +oOO00oo0 +oOo00Ooo +OOo0O +OOO0O0 +OOO0O00 +oOO0o000 +oOO0o00O +Ooo0o0O +oOo0o0O0 +oOo0o0oo +OOO0OO +Ooo0Oo0 +oOO0OO00 +oOo0oo0o +ooo0ooo +oOO0OOO0 +oOo0oooo +OOOO +OooO0 +OOOO00 +OOOO000 +oOOO0000 +oOOO000O +ooOo00o +oOOO00O0 +oOOO00OO +OOOO0o +OOOO0O0 +oOOO0O00 +oOOO0O0O +Oooo0Oo +oOOO0Oo0 +oooO0oOo +Ooooo +OOoOO0 +OoOOO00 +ooOOO000 +ooOOO00o +OOOOO0O +oOOoO0o0 +ooOOO0OO +oooOoo +OOOOOO0 +ooOOoO00 +ooooOO0O +ooOOoOO +ooOOOOo0 +ooOOOoOO diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..af63e88 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,79 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile + +# --------------------------------------------基本指令区--------------------------------------------# +#指定外部模糊字典 +-obfuscationdictionary dic.txt +#指定class模糊字典 +-classobfuscationdictionary dic.txt +#指定package模糊字典 +-packageobfuscationdictionary dic.txt + +# 代码混淆压缩比,在0~7之间,默认为5,一般不做修改 +-optimizationpasses 5 + +# 混合时不使用大小写混合,混合后的类名为小写 +-dontusemixedcaseclassnames + +# 指定不去忽略非公共库的类 +-dontskipnonpubliclibraryclasses + +# 这句话能够使我们的项目混淆后产生映射文件 +# 包含有类名->混淆后类名的映射关系 +-verbose + +# 指定不去忽略非公共库的类成员 +-dontskipnonpubliclibraryclassmembers + +# 不做预校验,preverify是proguard的四个步骤之一,Android不需要preverify,去掉这一步能够加快混淆速度。 +-dontpreverify + +# 保留Annotation不混淆 +-keepattributes *Annotation*,InnerClasses + +# 避免混淆泛型 +-keepattributes Signature + +# 抛出异常时保留代码行号 +-keepattributes SourceFile,LineNumberTable + +# 指定混淆是采用的算法,后面的参数是一个过滤器 +# 这个过滤器是谷歌推荐的算法,一般不做更改 +-optimizations !code/simplification/cast,!field/*,!class/merging/* + +# 保留我们使用的四大组件,自定义的Application等等这些类不被混淆 +# 因为这些子类都有可能被外部调用 +-keep public class * extends androidx.* +-keep public class * extends android.* + +# 保留以下类不做混淆, 否则模块无法生效 +-keep class com.freegang.xpler.xp.** { + *; +} + +-keep class com.freegang.xpler.HookInit extends * { + *; +} + +-keep class com.freegang.fplus.activity.HomeActivity extends * { + hookHint(); +} \ No newline at end of file diff --git a/app/src/androidTest/java/com/freegang/fplus/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/freegang/fplus/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..7ccf03a --- /dev/null +++ b/app/src/androidTest/java/com/freegang/fplus/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.freegang.fplus + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.freegang.fplus", appContext.packageName) + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..a0ce7da --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/assets/update.log b/app/src/main/assets/update.log new file mode 100644 index 0000000..53f739e --- /dev/null +++ b/app/src/main/assets/update.log @@ -0,0 +1,2 @@ +v1.0.0 +适配抖音24.4.0、24.5.0 \ No newline at end of file diff --git a/app/src/main/java/com/freegang/fplus/App.kt b/app/src/main/java/com/freegang/fplus/App.kt new file mode 100644 index 0000000..d370211 --- /dev/null +++ b/app/src/main/java/com/freegang/fplus/App.kt @@ -0,0 +1,11 @@ +package com.freegang.fplus + +import android.app.Application +import com.freegang.xpler.utils.app.KAppCrashUtils + +class App : Application() { + override fun onCreate() { + super.onCreate() + KAppCrashUtils.instance.init(this, "Freedom+崩溃退出!") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/freegang/fplus/Themes.kt b/app/src/main/java/com/freegang/fplus/Themes.kt new file mode 100644 index 0000000..d387286 --- /dev/null +++ b/app/src/main/java/com/freegang/fplus/Themes.kt @@ -0,0 +1,126 @@ +package com.freegang.fplus + +import android.view.Window +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.systemBars +import androidx.compose.foundation.layout.windowInsetsPadding +import androidx.compose.material.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp +import androidx.core.view.WindowCompat +import com.freegang.fplus.resource.ColorRes +import com.freegang.fplus.resource.ShapeRes +import com.google.accompanist.systemuicontroller.rememberSystemUiController + +@Composable +fun FreedomTheme( + window: Window, + isImmersive: Boolean = true, + isDark: Boolean = isSystemInDarkTheme(), + followSystem: Boolean = true, + content: @Composable () -> Unit, +) { + //沉浸式则不设置系统视图 + if (isImmersive) { + WindowCompat.setDecorFitsSystemWindows(window, false) + } + + //主题自适应 + Themes.ThemeContainer( + isImmersive = isImmersive, + isDark = isDark, + followSystem = followSystem, + content = content, + ) +} + +object Themes { + var isImmersive = false + var isDark = false + + val nowColors: ColorRes.KColors + get() { + return if (isDark) { + ColorRes.dark() + } else { + ColorRes.light() + } + } + + val nowTypography: Typography + get() { + return Typography( + subtitle1 = TextStyle( + fontWeight = FontWeight.Medium, + fontSize = 18.sp, + color = nowColors.title, + ), + subtitle2 = TextStyle( + fontWeight = FontWeight.Normal, + fontSize = 14.sp, + color = nowColors.subtitle, + ), + body1 = TextStyle( + fontWeight = FontWeight.Normal, + fontSize = 14.sp, + color = nowColors.body, + ), + body2 = TextStyle( + fontWeight = FontWeight.Normal, + fontSize = 12.sp, + color = nowColors.body, + ), + button = TextStyle( + fontWeight = FontWeight.Medium, + fontSize = 14.sp, + color = nowColors.body, + ), + caption = TextStyle( + fontWeight = FontWeight.Normal, + fontSize = 12.sp, + color = nowColors.caption, + ), + overline = TextStyle( + fontWeight = FontWeight.Normal, + fontSize = 10.sp, + color = nowColors.body, + ), + ) + } + + // 主题容器 + @Composable + fun ThemeContainer( + isImmersive: Boolean = true, + isDark: Boolean = false, + followSystem: Boolean = true, + content: @Composable () -> Unit, + ) { + Themes.isImmersive = isImmersive + Themes.isDark = if (followSystem) isSystemInDarkTheme() else isDark + + MaterialTheme( + colors = nowColors.colors, + typography = nowTypography, + shapes = ShapeRes.defaultShapes, + content = { + //沉浸式状态栏 + rememberSystemUiController().setSystemBarsColor( + color = nowColors.colors.background, + darkIcons = !Themes.isDark, + ) + //沉浸式则补充间隙 + Surface( + modifier = if (Themes.isImmersive) Modifier.windowInsetsPadding(WindowInsets.systemBars) else Modifier, + color = nowColors.colors.background, + contentColor = nowColors.colors.background, + content = { content() } + ) + } + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/freegang/fplus/activity/HomeActivity.kt b/app/src/main/java/com/freegang/fplus/activity/HomeActivity.kt new file mode 100644 index 0000000..7cbab7f --- /dev/null +++ b/app/src/main/java/com/freegang/fplus/activity/HomeActivity.kt @@ -0,0 +1,614 @@ +package com.freegang.fplus.activity + +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import android.os.Environment +import android.util.Log +import android.widget.Toast +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.animation.core.tween +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.clickable +import androidx.compose.foundation.combinedClickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.text.BasicTextField +import androidx.compose.material.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.rotate +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import androidx.lifecycle.lifecycleScope +import com.freegang.config.Config +import com.freegang.config.Version +import com.freegang.config.VersionConfig +import com.freegang.fplus.FreedomTheme +import com.freegang.fplus.R +import com.freegang.fplus.Themes +import com.freegang.fplus.component.FCard +import com.freegang.fplus.component.FDialog +import com.freegang.fplus.resource.StringRes +import com.freegang.xpler.utils.app.KAppVersionUtils.appVersionCode +import com.freegang.xpler.utils.app.KAppVersionUtils.appVersionName +import com.freegang.xpler.utils.io.KFileUtils.child +import com.freegang.xpler.utils.io.KStorageUtils.storageRootFile +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import java.util.* +import kotlin.random.Random + +class HomeActivity : ComponentActivity() { + private val isDark = mutableStateOf(false) + private val hookHint = mutableStateOf(StringRes.moduleHintFailed) + + private val versionConfig = mutableStateOf(null) + + private lateinit var config: Config + private val isOwnerDir = mutableStateOf(false) + private val isDownload = mutableStateOf(false) + private val isEmoji = mutableStateOf(false) + private val isHideTab = mutableStateOf(false) + private val hideTabKeyword = mutableStateOf("") + + // Freedom -> 外置存储器/Download/Freedom/ + private val freedomData + get() = application.storageRootFile + .child(Environment.DIRECTORY_DOWNLOADS) + .child("Freedom") + + // FreedomPlus -> 外置存储器/DCIM/Freedom/ + private val freedomPlusData + get() = application.storageRootFile + .child(Environment.DIRECTORY_DCIM) + .child("Freedom") + + // 模块反射调用 + private fun hookHint() { + hookHint.value = StringRes.moduleHintSucceeded + } + + + @OptIn(ExperimentalFoundationApi::class) + @Composable + fun TopBarView() { + var showUpdateLogDialog by remember { mutableStateOf(false) } + var updateLog by remember { mutableStateOf("") } + + var rotate by remember { mutableStateOf(0f) } + val rotateAnimate by animateFloatAsState( + targetValue = rotate, + animationSpec = tween(durationMillis = Random.nextInt(500, 1500)) + ) + + TopAppBar( + modifier = Modifier.padding(vertical = 24.dp), + elevation = 0.dp, + backgroundColor = Themes.nowColors.colors.background, + content = { + Row(verticalAlignment = Alignment.CenterVertically) { + Column(modifier = Modifier.weight(1f)) { + Text( + text = StringRes.moduleTitle, + style = Themes.nowTypography.subtitle1, + ) + Spacer(modifier = Modifier.padding(vertical = 2.dp)) + Text( + text = StringRes.moduleSubtitle, + style = Themes.nowTypography.subtitle2, + ) + } + Icon( + painter = painterResource(id = if (isDark.value) R.drawable.ic_light_mode else R.drawable.ic_dark_mode), + contentDescription = if (isDark.value) "浅色模式" else "深色模式", + tint = Themes.nowColors.icon, + modifier = Modifier + .size(20.dp) + .clickable( + indication = null, + interactionSource = remember { MutableInteractionSource() }, + onClick = { + isDark.value = !isDark.value + }, + ) + ) + Spacer(modifier = Modifier.padding(horizontal = 12.dp)) + Icon( + painter = painterResource(id = R.drawable.ic_motion), + contentDescription = "检查更新/日志", + tint = Themes.nowColors.icon, + modifier = Modifier + .size(20.dp) + .rotate(rotateAnimate) + .combinedClickable( + indication = null, + interactionSource = remember { MutableInteractionSource() }, + onClick = { + rotate = if (rotate == 0f) 360f else 0f + }, + onLongClick = { + lifecycleScope.launch { + updateLog = withContext(Dispatchers.IO) { + assets + .open("update.log") + .readBytes() + .decodeToString() + } + showUpdateLogDialog = updateLog.isNotBlank() + } + } + ) + ) + } + }, + ) + + //更新日志弹层 + if (showUpdateLogDialog) { + FDialog( + title = "更新日志", + onlyConfirm = true, + confirm = "确定", + onConfirm = { showUpdateLogDialog = false }, + content = { + LazyColumn( + modifier = Modifier, + content = { + item { + Text( + text = updateLog, + modifier = Modifier.fillMaxWidth() + ) + } + }, + ) + }, + ) + } + } + + @Composable + fun ContentView() { + var showInputKeywordDialog by remember { mutableStateOf(false) } + var showHideTabTips by remember { mutableStateOf(false) } + var showDataMoveDialog by remember { mutableStateOf(freedomData.exists()) } + + Column( + modifier = Modifier.fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally, + content = { + // 模块状态 + FCard( + modifier = Modifier.padding(bottom = 24.dp, top = 12.dp), + content = { + Box( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 24.dp), + content = { + Text( + text = hookHint.value, + style = Themes.nowTypography.body1, + modifier = Modifier.align(Alignment.Center), + ) + } + ) + } + ) + + // 选项 + SwitchItem( + text = "视频创作者单独创建文件夹", + checked = isOwnerDir, + onCheckedChange = { + isOwnerDir.value = it + config.isOwnerDir = isOwnerDir.value + }, + ) + SwitchItem( + text = "视频/图文/音乐下载", + checked = isDownload, + onClick = { + }, + onCheckedChange = { + isDownload.value = it + config.isDownload = isDownload.value + } + ) + SwitchItem( + text = "评论区图片/表情包保存", + checked = isEmoji, + onCheckedChange = { + isEmoji.value = it + config.isEmoji = isEmoji.value + }, + ) + SwitchItem( + text = "隐藏顶部tab", + subtext = "点击设置关键字", + checked = isHideTab, + onClick = { + showInputKeywordDialog = true + }, + onCheckedChange = { + isHideTab.value = it + if (isHideTab.value) { + showHideTabTips = true + } + }, + ) + + //数据目录 + FCard( + modifier = Modifier + .padding(top = 24.dp, bottom = 4.dp) + .clickable( + indication = null, + interactionSource = remember { MutableInteractionSource() }, + onClick = { + startActivity( + Intent().apply { + action = "com.android.fileexplorer.export.VIEW_HOME" + addCategory("android.intent.category.DEFAULT") + } + ) + } + ), + content = { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 24.dp, vertical = 16.dp), + content = { + Icon( + painter = painterResource(id = R.drawable.ic_find_file), + contentDescription = "Github", + tint = Themes.nowColors.icon, + modifier = Modifier.size(24.dp) + ) + Spacer(modifier = Modifier.padding(horizontal = 8.dp)) + Text(text = "数据目录: `外置存储器/DCIM/Freedm`") + } + ) + } + ) + + //源码地址 + FCard( + modifier = Modifier + .padding(vertical = 4.dp) + .clickable( + indication = null, + interactionSource = remember { MutableInteractionSource() }, + onClick = { + startActivity( + Intent( + Intent.ACTION_VIEW, + Uri.parse("https://github.com/GangJust/FreedomPlus"), + ) + ) + } + ), + content = { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 24.dp, vertical = 16.dp), + content = { + Icon( + painter = painterResource(id = R.drawable.ic_github), + contentDescription = "Github", + tint = Themes.nowColors.icon, + modifier = Modifier.size(24.dp) + ) + Spacer(modifier = Modifier.padding(horizontal = 8.dp)) + Column { + Text( + text = "源码地址", + style = Themes.nowTypography.body1, + ) + Text( + text = "https://github.com/GangJust/FreedomPlus", + style = Themes.nowTypography.overline, + ) + } + } + ) + } + ) + } + ) + + //版本更新 + if (versionConfig.value != null) { + val version = versionConfig.value!! + if (version.name.compareTo("v${application.appVersionName}") >= 1) { + resetConfig() + FDialog( + title = "发现新版本${version.name}!", + onlyConfirm = true, + confirm = "确定", + onConfirm = { + startActivity( + Intent( + Intent.ACTION_VIEW, + Uri.parse(version.browserDownloadUrl), + ) + ) + }, + content = { + LazyColumn( + modifier = Modifier, + content = { + item { + Text( + text = version.body, + modifier = Modifier.fillMaxWidth() + ) + } + }, + ) + } + ) + } + } + + //过滤关键字输入弹层 + if (showInputKeywordDialog) { + FDialog( + title = "请输入关键字, 用逗号分开", + cancel = "取消", + confirm = "确定", + onCancel = { + showInputKeywordDialog = false + }, + onConfirm = { + config.hideTabKeyword = hideTabKeyword.value + if (config.hideTabKeyword.isBlank()) { + Toast.makeText(application, "关键字内容为空!", Toast.LENGTH_SHORT).show() + } else { + showInputKeywordDialog = false + } + }, + content = { + FCard( + content = { + BasicTextField( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 12.dp, horizontal = 12.dp), + value = hideTabKeyword.value, + onValueChange = { + hideTabKeyword.value = it + } + ) + } + ) + }, + ) + } + + //开启tab过滤,提示 + if (showHideTabTips) { + FDialog( + title = "提示", + cancel = "关闭", + confirm = "开启", + onCancel = { + showHideTabTips = false + isHideTab.value = false + config.isHideTab = isHideTab.value + }, + onConfirm = { + showHideTabTips = false + isHideTab.value = true + config.isHideTab = isHideTab.value + }, + content = { + Text(text = "一旦开启顶部Tab隐藏, 将禁止左右滑动切换, 具体效果自行查看!") + }, + ) + } + + //旧数据迁移 + if (showDataMoveDialog) { + var showMoveToContent by remember { mutableStateOf("存在Freedom下载数据, 正在迁移至Freedom+下载目录!") } + var showMoveToConfirm by remember { mutableStateOf("请稍后...") } + + FDialog( + title = "提示", + onlyConfirm = true, + confirm = showMoveToConfirm, + onConfirm = { + if (showMoveToConfirm == "确定") { + showDataMoveDialog = false + } + }, + content = { + Text(text = showMoveToContent) + }, + ) + + //数据迁移 + LaunchedEffect(key1 = "moveData") { + val result = withContext(Dispatchers.IO) { + freedomData.copyRecursively( + target = freedomPlusData, + overwrite = true, + onError = { _, _ -> + OnErrorAction.TERMINATE + }, + ) + } + showMoveToConfirm = "确定" + showMoveToContent = if (!result) { + "数据迁移失败, 请手动将[外置存储器/Download/Freedom]目录合并至[外置存储器/DCIM/Freedom]中!" + } else { + val deleteAll = freedomData.deleteRecursively() + if (deleteAll) "数据迁移成功!" else "数据迁移成功, 但旧数据删除失败, 请手动将[外置存储器/Download/Freedom]删除!" + } + } + } + } + + @OptIn(ExperimentalFoundationApi::class) + @Composable + private fun SwitchItem( + text: String, + subtext: String = "", + checked: MutableState, + onCheckedChange: (checked: Boolean) -> Unit, + onClick: () -> Unit = {}, + onLongClick: () -> Unit = {}, + ) { + FCard( + modifier = Modifier + .padding(vertical = 4.dp) + .combinedClickable( + indication = null, + interactionSource = remember { MutableInteractionSource() }, + onClick = { + onClick.invoke() + }, + onLongClick = { + onLongClick.invoke() + } + ), + content = { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 24.dp) + .then(if (subtext.isNotBlank()) Modifier.padding(vertical = 4.dp) else Modifier), + verticalAlignment = Alignment.CenterVertically, + content = { + Column( + modifier = Modifier.weight(1f), + content = { + Text( + text = text, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + style = Themes.nowTypography.body1, + ) + if (subtext.isNotBlank()) { + Text( + text = subtext, + style = Themes.nowTypography.overline.copy( + color = Themes.nowColors.subtitle, + ), + modifier = Modifier.padding(vertical = 2.dp) + ) + } + }, + ) + Switch( + colors = SwitchDefaults.colors( + checkedThumbColor = Themes.nowColors.checkedThumb, + checkedTrackColor = Themes.nowColors.checkedTrack, + uncheckedThumbColor = Themes.nowColors.uncheckedThumb, + uncheckedTrackColor = Themes.nowColors.uncheckedTrack, + ), + checked = checked.value, + onCheckedChange = { + onCheckedChange.invoke(it) + }, + ) + } + ) + } + ) + } + + // 获取远程版本信息 + private fun getVersion() { + lifecycleScope.launch { + versionConfig.value = withContext(Dispatchers.IO) { + Version.getRemoteReleasesLatest() + } + } + } + + // 读取配置 + private fun readConfig() { + lifecycleScope.launch { + config = withContext(Dispatchers.IO) { Config.read(application) } + isOwnerDir.value = config.isOwnerDir + isDownload.value = config.isDownload + isEmoji.value = config.isEmoji + isHideTab.value = config.isHideTab + hideTabKeyword.value = config.hideTabKeyword + } + } + + // 重置配置 + private fun resetConfig() { + isOwnerDir.value = false + isDownload.value = false + isEmoji.value = false + isHideTab.value = false + + config.isSupportHint = true + config.isOwnerDir = isOwnerDir.value + config.isDownload = isDownload.value + config.isEmoji = isEmoji.value + config.isHideTab = isHideTab.value + } + + // 保存配置 + private fun saveConfig() { + lifecycleScope.launch { + config.versionName = application.appVersionName + config.versionCode = application.appVersionCode + config.save(application) + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContent { + readConfig() + + FreedomTheme( + window = window, + isImmersive = true, + isDark = isDark.value, + followSystem = false, + content = { + Scaffold( + modifier = Modifier.padding(horizontal = 24.dp), + topBar = { TopBarView() }, + content = { + BoxWithConstraints( + modifier = Modifier.padding(it), + content = { + ContentView() + } + ) + }, + ) + } + ) + } + } + + override fun onResume() { + super.onResume() + getVersion() + } + + override fun onPause() { + super.onPause() + saveConfig() + } +} + diff --git a/app/src/main/java/com/freegang/fplus/activity/MainActivity.kt b/app/src/main/java/com/freegang/fplus/activity/MainActivity.kt new file mode 100644 index 0000000..11ce1dd --- /dev/null +++ b/app/src/main/java/com/freegang/fplus/activity/MainActivity.kt @@ -0,0 +1,147 @@ +package com.freegang.fplus.activity + +import android.Manifest +import android.content.Intent +import android.net.Uri +import android.os.Build +import android.os.Bundle +import android.os.Environment +import android.provider.Settings +import android.util.Log +import android.widget.Toast +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.compose.foundation.layout.* +import androidx.compose.material.Text +import androidx.compose.ui.unit.sp +import androidx.core.content.PermissionChecker +import com.freegang.fplus.FreedomTheme +import com.freegang.fplus.Themes +import com.freegang.fplus.component.FDialog +import com.freegang.xpler.utils.io.KStorageUtils.hasOperationStorage +import com.google.accompanist.permissions.ExperimentalPermissionsApi +import com.google.accompanist.permissions.rememberMultiplePermissionsState + +class MainActivity : ComponentActivity() { + + /// 检查是否具有某个权限 + private fun checkPermission(permission: String): Boolean { + return PermissionChecker.checkSelfPermission(application, permission) == PermissionChecker.PERMISSION_GRANTED + } + + /// 需要的权限列表 + private fun getPermissionList(): List { + val permissions = mutableListOf() + //是否具有外置存储器读 + if (!checkPermission(Manifest.permission.READ_EXTERNAL_STORAGE)) { + permissions.add(Manifest.permission.READ_EXTERNAL_STORAGE) + } + + //是否具有外置存储器写 + if (!checkPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)) { + permissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE) + } + + //是否具有外置存储器管理(特殊权限) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && !Environment.isExternalStorageManager()) { + permissions.add(Manifest.permission.MANAGE_EXTERNAL_STORAGE) + } + + return permissions + } + + /// 跳转对应权限修改页 + private fun requestAgainPermissions(deniedPermissions: List) { + //处理未请求成功的权限 + //应用设置页, 开启外部存储读写权限, 跳转 + if (deniedPermissions.contains(Manifest.permission.READ_EXTERNAL_STORAGE) || deniedPermissions.contains(Manifest.permission.WRITE_EXTERNAL_STORAGE)) { + startActivity( + Intent( + Settings.ACTION_APPLICATION_DETAILS_SETTINGS, + Uri.parse("package:${packageName}") + ) + ) + return + } + + //Android11 必须要的管理外部存储完全管理权限, 跳转 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + if (deniedPermissions.contains(Manifest.permission.MANAGE_EXTERNAL_STORAGE) && !Environment.isExternalStorageManager()) { + startActivity( + Intent( + Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION, + Uri.parse("package:${packageName}") + ) + ) + } + return + } + } + + private fun toHomeActivity() { + if (getPermissionList().isEmpty()) { + startActivity(Intent(application, HomeActivity::class.java)) + finish() + } + } + + @OptIn(ExperimentalPermissionsApi::class) + override fun onCreate(savedInstanceState: Bundle?) { + toHomeActivity() + super.onCreate(savedInstanceState) + setContent { + val permissionsState = rememberMultiplePermissionsState(getPermissionList()) + FreedomTheme(window = window) { + FDialog( + title = "Freedom+需要以下权限才能正常运行", + cancel = "取消", + confirm = "确定", + onCancel = { + finish() + }, + onConfirm = { + if (getPermissionList().isNotEmpty()) { + permissionsState.launchMultiplePermissionRequest() + } + }, + content = { + Column { + Text( + text = "外置存储器读/写权限", + style = Themes.nowTypography.body1.copy( + color = Themes.nowColors.subtitle, + lineHeight = 1.2.sp, + ), + ) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + Text( + text = "外置存储器管理权限(Android11+)", + style = Themes.nowTypography.body1.copy( + color = Themes.nowColors.subtitle, + lineHeight = 1.2.sp + ), + ) + } + } + }, + ) + } + } + } + + override fun onResume() { + toHomeActivity() + super.onResume() + } + + override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + //Log.d("GLog", "requestCode=$requestCode ,resultCode=${permissions.joinToString()} ,grantResults=${grantResults.joinToString()}") + if (getPermissionList().isNotEmpty()) { + Toast.makeText(application, "请开启必要权限", Toast.LENGTH_SHORT).show() + requestAgainPermissions(permissions.filterIndexed { i, _ -> grantResults[i] == -1 }) + } else { + toHomeActivity() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/freegang/fplus/component/CardButton.kt b/app/src/main/java/com/freegang/fplus/component/CardButton.kt new file mode 100644 index 0000000..31d0250 --- /dev/null +++ b/app/src/main/java/com/freegang/fplus/component/CardButton.kt @@ -0,0 +1,95 @@ +package kit + +import androidx.compose.foundation.* +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.shadow +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp + +@Composable +fun CardButton( + modifier: Modifier = Modifier, + contentAlignment: Alignment = Alignment.Center, + elevation: Dp = 0.dp, + color: Color = Color.Transparent, + contentColor: Color = Color.Transparent, + border: BorderStroke? = null, + shape: Shape = RoundedCornerShape(8.dp), + onClick: () -> Unit, + interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, + indication: Indication? = LocalIndication.current, + enabled: Boolean = true, + onClickLabel: String? = null, + role: Role? = null, + content: @Composable () -> Unit, +) { + CardButton( + modifier = modifier, + elevation = elevation, + color = color, + contentColor = contentColor, + border = border, + shape = shape, + clickAndSemanticsModifier = Modifier.clickable( + interactionSource = interactionSource, + indication = indication, + enabled = enabled, + onClickLabel = onClickLabel, + role = role, + onClick = onClick + ), + content = { + BoxWithConstraints( + contentAlignment = contentAlignment, + content = { content() } + ) + } + ) +} + +@Composable +private fun CardButton( + modifier: Modifier, + shape: Shape, + color: Color, + contentColor: Color, + border: BorderStroke?, + elevation: Dp, + clickAndSemanticsModifier: Modifier, + content: @Composable () -> Unit, +) { + val elevationOverlay = LocalElevationOverlay.current + val absoluteElevation = LocalAbsoluteElevation.current + elevation + val backgroundColor = if (color == MaterialTheme.colors.surface && elevationOverlay != null) { + elevationOverlay.apply(color, absoluteElevation) + } else { + color + } + CompositionLocalProvider( + LocalContentColor provides contentColor, + LocalAbsoluteElevation provides absoluteElevation + ) { + Box( + propagateMinConstraints = true, + content = { content() }, + modifier = modifier + .shadow(elevation, shape, clip = false) + .then(if (border != null) Modifier.border(border, shape) else Modifier) + .background(color = backgroundColor, shape = shape) + .clip(shape) + .then(clickAndSemanticsModifier), + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/freegang/fplus/component/FCard.kt b/app/src/main/java/com/freegang/fplus/component/FCard.kt new file mode 100644 index 0000000..3825911 --- /dev/null +++ b/app/src/main/java/com/freegang/fplus/component/FCard.kt @@ -0,0 +1,39 @@ +package com.freegang.fplus.component + +import androidx.compose.foundation.border +import androidx.compose.material.Card +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import com.freegang.fplus.Themes +import com.freegang.fplus.resource.ColorRes +import com.freegang.fplus.resource.ShapeRes + + +@Composable +fun FCard( + modifier: Modifier = Modifier, + border: FCardBorder = FCardBorder(), + content: @Composable () -> Unit, +) { + Card( + modifier = modifier.border( + width = border.borderWidth, + color = border.borderColor, + shape = border.borderShape, + ), + elevation = 0.dp, + backgroundColor = ColorRes.transparent, + shape = ShapeRes.defaultShapes.large, + content = content + ) +} + +data class FCardBorder( + var borderWidth: Dp = 1.5.dp, + var borderColor: Color = Themes.nowColors.divider, + var borderShape: Shape = ShapeRes.defaultShapes.large, +) \ No newline at end of file diff --git a/app/src/main/java/com/freegang/fplus/component/FDialog.kt b/app/src/main/java/com/freegang/fplus/component/FDialog.kt new file mode 100644 index 0000000..2d065e5 --- /dev/null +++ b/app/src/main/java/com/freegang/fplus/component/FDialog.kt @@ -0,0 +1,94 @@ +package com.freegang.fplus.component + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Card +import androidx.compose.material.Text +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Dialog +import com.freegang.fplus.Themes +import kit.CardButton + +@Composable +fun FDialog( + modifier: Modifier = Modifier, + title: String, + cancel: String = "cancel", + confirm: String = "confirm", + onlyConfirm: Boolean = false, + onCancel: (() -> Unit)? = null, + onConfirm: (() -> Unit)? = null, + content: @Composable () -> Unit, +) { + val radius = 12.dp + Dialog( + onDismissRequest = { }, + content = { + Card( + shape = RoundedCornerShape(12.dp), + content = { + Column( + horizontalAlignment = Alignment.Start, + verticalArrangement = Arrangement.SpaceBetween, + content = { + Text( + text = title, + modifier = Modifier.padding(horizontal = 24.dp, vertical = 16.dp), + style = Themes.nowTypography.body1, + ) + BoxWithConstraints( + modifier = Modifier.padding(start = 24.dp, end = 24.dp, bottom = 16.dp).heightIn(max = 320.dp), + contentAlignment = Alignment.Center, + content = { + content() + } + ) + Row( + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.Bottom, + content = { + if (!onlyConfirm) { + CardButton( + modifier = Modifier + .weight(1f) + .heightIn(min = 48.dp), + shape = RoundedCornerShape(bottomStart = radius), + onClick = { onCancel?.invoke() }, + content = { + Text( + text = cancel, + style = Themes.nowTypography.body1, + ) + }, + ) + } + CardButton( + modifier = Modifier + .weight(1f) + .heightIn(min = 48.dp), + shape = RoundedCornerShape( + bottomStart = if (onlyConfirm) radius else 0.dp, + bottomEnd = radius, + ), + onClick = { onConfirm?.invoke() }, + content = { + Text( + text = confirm, + style = Themes.nowTypography.body1.copy( + Themes.nowColors.colors.secondary, + ), + ) + } + ) + } + ) + } + ) + }, + ) + }, + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/freegang/fplus/resource/ColorRes.kt b/app/src/main/java/com/freegang/fplus/resource/ColorRes.kt new file mode 100644 index 0000000..7ecd7ab --- /dev/null +++ b/app/src/main/java/com/freegang/fplus/resource/ColorRes.kt @@ -0,0 +1,78 @@ +package com.freegang.fplus.resource + +import androidx.compose.material.Colors +import androidx.compose.material.darkColors +import androidx.compose.material.lightColors +import androidx.compose.ui.graphics.Color + +object ColorRes { + val transparent = Color(0x00000000) + + fun light() = KColors( + title = Color(0xFF333333), + subtitle = Color(0XFF666666), + body = Color(0XFF666666), + caption = Color(0XFFA3A6B1), + divider = Color(0xFFD9D9D9), + icon = Color(0XFF666666), + checkedThumb = Color(0xFFEDA664), + checkedTrack = Color(0xFFF0BB88), + uncheckedThumb = Color(0XFFFDFDFD), + uncheckedTrack = Color(0xFF999999), + colors = lightColors( + primary = Color(0xFF03A9F4), + primaryVariant = Color(0xFF03A9F4), + secondary = Color(0xFFEDA664), + secondaryVariant = Color(0xFFF0BB88), + background = Color(0xFFF8FAFB), + surface = Color(0xFFFFFFFF), + error = Color(0xFFB00020), + onPrimary = Color(0xFFFFFFFF), + onSecondary = Color(0xFF000000), + onBackground = Color(0xFF000000), + onSurface = Color(0xFF000000), + onError = Color(0xFFFFFFFF), + ) + ) + + fun dark() = KColors( + title = Color(0xFFFFFFFF), + subtitle = Color(0xFFAAAAAA), + body = Color(0xFFFFFFFF), + caption = Color(0xFFAAAAAA), + divider = Color(0xFFDADADA), + icon = Color(0xFFFFFFFF), + checkedThumb = Color(0xFFFFFFFF), + checkedTrack = Color(0XFFAAAAAA), + uncheckedThumb = Color(0XFF666666), + uncheckedTrack = Color(0XFF999999), + colors = darkColors( + primary = Color(0xFF03A9F4), + primaryVariant = Color(0xFF03A9F4), + secondary = Color(0xFFEDA664), + secondaryVariant = Color(0xFFF0BB88), + background = Color(0xFF3A3A3A), + surface = Color(0xFF585858), + error = Color(0xFFB00020), + onPrimary = Color(0xFFFFFFFF), + onSecondary = Color(0xFF000000), + onBackground = Color(0xFF000000), + onSurface = Color(0xFF000000), + onError = Color(0xFFFFFFFF), + ) + ) + + class KColors( + val title: Color, + val subtitle: Color, + val body: Color, + val caption: Color, + val divider: Color, + val icon: Color, + val checkedThumb: Color, + val checkedTrack: Color, + val uncheckedThumb: Color, + val uncheckedTrack: Color, + val colors: Colors, + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/freegang/fplus/resource/ShapeRes.kt b/app/src/main/java/com/freegang/fplus/resource/ShapeRes.kt new file mode 100644 index 0000000..4ac29c7 --- /dev/null +++ b/app/src/main/java/com/freegang/fplus/resource/ShapeRes.kt @@ -0,0 +1,13 @@ +package com.freegang.fplus.resource + +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Shapes +import androidx.compose.ui.unit.dp + +object ShapeRes { + val defaultShapes = Shapes( + small = RoundedCornerShape(4.dp), + medium = RoundedCornerShape(8.dp), + large = RoundedCornerShape(12.dp) + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/freegang/fplus/resource/StringRes.kt b/app/src/main/java/com/freegang/fplus/resource/StringRes.kt new file mode 100644 index 0000000..418ece8 --- /dev/null +++ b/app/src/main/java/com/freegang/fplus/resource/StringRes.kt @@ -0,0 +1,8 @@ +package com.freegang.fplus.resource + +object StringRes { + const val moduleTitle = "Freedom+" + const val moduleSubtitle = "No one is always happy." + const val moduleHintFailed = "模块未加载!" + const val moduleHintSucceeded = "模块加载成功!" +} \ No newline at end of file diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..df6bd46 --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_dark_mode.xml b/app/src/main/res/drawable/ic_dark_mode.xml new file mode 100644 index 0000000..410540b --- /dev/null +++ b/app/src/main/res/drawable/ic_dark_mode.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_find_file.xml b/app/src/main/res/drawable/ic_find_file.xml new file mode 100644 index 0000000..d3ea97f --- /dev/null +++ b/app/src/main/res/drawable/ic_find_file.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_github.xml b/app/src/main/res/drawable/ic_github.xml new file mode 100644 index 0000000..29cdcb7 --- /dev/null +++ b/app/src/main/res/drawable/ic_github.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..b59966e --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_light_mode.xml b/app/src/main/res/drawable/ic_light_mode.xml new file mode 100644 index 0000000..6733667 --- /dev/null +++ b/app/src/main/res/drawable/ic_light_mode.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_motion.xml b/app/src/main/res/drawable/ic_motion.xml new file mode 100644 index 0000000..6e41a6a --- /dev/null +++ b/app/src/main/res/drawable/ic_motion.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_note.xml b/app/src/main/res/drawable/ic_note.xml new file mode 100644 index 0000000..643c532 --- /dev/null +++ b/app/src/main/res/drawable/ic_note.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..bbd3e02 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..bbd3e02 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..d48719cc81593623c7d154c344046eb26e2bb77f GIT binary patch literal 3663 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..ecbec464542cf178ae1a0869aa71f41964f9b7a6 GIT binary patch literal 5539 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..0c81885a21eb850dfaf7359a33ffb1890b19ca96 GIT binary patch literal 3388 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 SESG8k0000p-a3sQ>@~L{C@q-u3MGw^M+x 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 Pz6a3LGS;k9cZ~TD-(1P= literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..add244eac2e6d7178c2fc415c25a1582c416e5bc GIT binary patch literal 7749 zcmV-L9=hR)P)_JvZg4CyJ;#K?h6RA%A)LoCTf|Y z;Ep1j2q=hqsmO8xEj789_y0cU`#!vUcj2-XHlE+_`El>JzUO()dCz;^^L>|At?raN zrLIEMsx`U0-z5-d{Rgv9j^lIG*SS+HEP}vY?tc_uQq}t{EkY3GI($v_{yRl3qJ@L3 zKwwIsC08!v3+cXeTQGpRo=5aE$sqrRd+|P7i6N*FsffP@Map zN@LxgD35pjgz;77*00u-Z=Rl{iF3`-#Jm2$&@g^tT$6#%8qIpw^Ed{_igWl}Tm#p_ zHEZ1iqBpXK-L^%lcwDWDmu#Bay&_@ypo-1Y0xC95%VJz*PF|PL8Va=N}?PT zXf@w|T~)UE%c_zn$0~dmh;-1^g0h{D!Lc|;-d1xh<=XPSqH%B7V9LJVO;J~@VyEY$ zWgFbwnS=r(q~9Vq%Ap$Dm`q|)^+hoY9E)@0@71*hG7zHxw!kJ+_TIKd@w3YJidg4f zniwaa%H$R0nhkCW=^*H)KH+LKQUEn1L2ys`er4<3NZ7=-!NwZTAKE#Jb&N6*e2ION%d})pMw)eAh%d z4dzOnQd|uxT)uO;mMg6$!z7wf00Gzno8$(e2yu#q7*bO&gs&~VHz+eze7+p**tIOm zF}8;ITW*F#Yinr&uubCoE%yWw2z5YRMEx)}wA2Zm{-C9aa(thS{{a<{Dk-%r1ua^D z8r13y=TRqPb!`zv-$%0Hvud?^BR>B25I{f#2uj3sInrS^S5dg37Bem=hM-lHMmdBl zBlp=cL(-5&;OGAMLaaEnk!DRaFu@ib|rLEJAtm3M$<&!C<>7XtvF#vMnR4cF(c z!9^KRP6b+bWY<$DTn~4*TE^cIpazO7IvHqlDtc1C`}H|&P?1k=eJR3zo^U-X@_{=7 zsIC>|puM=(jxxLkEdh587C<@iT&Vq{tg1$|{s_Yy&m943?*m6@tS5tP%fJ0<;1l3b zN#50~$o{K!QG}hBsn!oPM1U))5p*>po=W4!80<5K<`kfeXTevqHc(|ssKGvyFe88f zb0zhH&jY|=y~hIOgflVjJu7!ED`il5;!<4&|207JN;!|R+;E=>+_;cSx~gC3TEQ-h z&Vyi;`-2U(n*xr-IqK~ONLoSH&!&-1TSeE;XBzG|1-4n#nd@$_Z>Pi+I6M2zyt4J~ z&X0r3^!m&Vm^aoPWXqW)YNwak>RaIP~ zs%sV8mCaYaIRvnwm(s7PeNYtfyTE{b| z;)!gtvty-;9QGaAtt8U%M^mAvJ{l|ld}Zt?`sH$=AOedhB}niYIUZzUm8)#ibTP

px@(QN>bbylc(9HQ&w_$Kam0 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�A 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 P00000NkvXXu0mjfiQH=k literal 0 HcmV?d00001 diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml new file mode 100644 index 0000000..5000760 --- /dev/null +++ b/app/src/main/res/values/arrays.xml @@ -0,0 +1,6 @@ + + + + com.ss.android.ugc.aweme + + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..d29adb9 --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,11 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + #00000000 + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..7c77597 --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,4 @@ + + Freedom+ + 依赖于抖音运行的辅助模块! + \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml new file mode 100644 index 0000000..54437d2 --- /dev/null +++ b/app/src/main/res/values/themes.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/backup_rules.xml b/app/src/main/res/xml/backup_rules.xml new file mode 100644 index 0000000..fa0f996 --- /dev/null +++ b/app/src/main/res/xml/backup_rules.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/data_extraction_rules.xml b/app/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 0000000..9ee9997 --- /dev/null +++ b/app/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/test/java/com/freegang/fplus/ExampleUnitTest.kt b/app/src/test/java/com/freegang/fplus/ExampleUnitTest.kt new file mode 100644 index 0000000..fc75761 --- /dev/null +++ b/app/src/test/java/com/freegang/fplus/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.freegang.fplus + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/aweme/.gitignore b/aweme/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/aweme/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/aweme/build.gradle b/aweme/build.gradle new file mode 100644 index 0000000..f1ea213 --- /dev/null +++ b/aweme/build.gradle @@ -0,0 +1,41 @@ +plugins { + id 'com.android.library' + id 'org.jetbrains.kotlin.android' +} + +android { + namespace 'com.ss.android.ugc.aweme' + compileSdk 33 + + defaultConfig { + minSdk 21 + targetSdk 33 + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles "consumer-rules.pro" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } + kotlinOptions { + jvmTarget = '1.8' + } +} + +dependencies { + + implementation 'androidx.core:core-ktx:1.7.0' + implementation 'androidx.appcompat:appcompat:1.4.1' + implementation 'com.google.android.material:material:1.5.0' + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.3' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' +} \ No newline at end of file diff --git a/aweme/consumer-rules.pro b/aweme/consumer-rules.pro new file mode 100644 index 0000000..e69de29 diff --git a/aweme/proguard-rules.pro b/aweme/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/aweme/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/aweme/readme.md b/aweme/readme.md new file mode 100644 index 0000000..b4d6aa2 --- /dev/null +++ b/aweme/readme.md @@ -0,0 +1 @@ +# Sub DouYin \ No newline at end of file diff --git a/aweme/src/androidTest/java/com/ss/android/ugc/aweme/ExampleInstrumentedTest.kt b/aweme/src/androidTest/java/com/ss/android/ugc/aweme/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..70b7586 --- /dev/null +++ b/aweme/src/androidTest/java/com/ss/android/ugc/aweme/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.ss.android.ugc.aweme + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.ss.android.ugc.aweme.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/aweme/src/main/AndroidManifest.xml b/aweme/src/main/AndroidManifest.xml new file mode 100644 index 0000000..a5918e6 --- /dev/null +++ b/aweme/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/aweme/src/main/java/com/bytedance/ies/dmt/ui/widget/tabhost/AwemeFragmentTabHost.java b/aweme/src/main/java/com/bytedance/ies/dmt/ui/widget/tabhost/AwemeFragmentTabHost.java new file mode 100644 index 0000000..df40a31 --- /dev/null +++ b/aweme/src/main/java/com/bytedance/ies/dmt/ui/widget/tabhost/AwemeFragmentTabHost.java @@ -0,0 +1,15 @@ +package com.bytedance.ies.dmt.ui.widget.tabhost; + +import android.content.Context; + +import androidx.annotation.NonNull; + +import com.bytedance.ies.uikit.tabhost.FragmentTabHost; + +public class AwemeFragmentTabHost extends FragmentTabHost { + + public AwemeFragmentTabHost(@NonNull Context context) { + super(context); + throw new RuntimeException("sub!"); + } +} \ No newline at end of file diff --git a/aweme/src/main/java/com/bytedance/ies/uikit/base/AbsActivity.java b/aweme/src/main/java/com/bytedance/ies/uikit/base/AbsActivity.java new file mode 100644 index 0000000..89280bc --- /dev/null +++ b/aweme/src/main/java/com/bytedance/ies/uikit/base/AbsActivity.java @@ -0,0 +1,14 @@ +package com.bytedance.ies.uikit.base; + +import android.os.Bundle; + +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; + +public class AbsActivity extends AppCompatActivity { + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + throw new RuntimeException("sub!"); + } +} \ No newline at end of file diff --git a/aweme/src/main/java/com/bytedance/ies/uikit/base/AbsFragment.java b/aweme/src/main/java/com/bytedance/ies/uikit/base/AbsFragment.java new file mode 100644 index 0000000..dce5a86 --- /dev/null +++ b/aweme/src/main/java/com/bytedance/ies/uikit/base/AbsFragment.java @@ -0,0 +1,15 @@ +package com.bytedance.ies.uikit.base; + +import android.os.Bundle; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; + +public class AbsFragment extends Fragment { + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + throw new RuntimeException("sub!"); + } +} \ No newline at end of file diff --git a/aweme/src/main/java/com/bytedance/ies/uikit/tabhost/FragmentTabHost.java b/aweme/src/main/java/com/bytedance/ies/uikit/tabhost/FragmentTabHost.java new file mode 100644 index 0000000..626acdb --- /dev/null +++ b/aweme/src/main/java/com/bytedance/ies/uikit/tabhost/FragmentTabHost.java @@ -0,0 +1,29 @@ +package com.bytedance.ies.uikit.tabhost; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.TabHost; + +/// 底部tabbar 视频、朋友、消息、我 等 +public class FragmentTabHost extends TabHost { + public FragmentTabHost(Context context) { + super(context); + } + + public FragmentTabHost(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public FragmentTabHost(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public FragmentTabHost(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + //切换 + public void onTabChanged(String s) { + throw new RuntimeException("sub!"); + } +} \ No newline at end of file diff --git a/aweme/src/main/java/com/ss/android/ugc/aweme/app/host/AwemeHostApplication.java b/aweme/src/main/java/com/ss/android/ugc/aweme/app/host/AwemeHostApplication.java new file mode 100644 index 0000000..11bfda1 --- /dev/null +++ b/aweme/src/main/java/com/ss/android/ugc/aweme/app/host/AwemeHostApplication.java @@ -0,0 +1,11 @@ +package com.ss.android.ugc.aweme.app.host; + +import android.app.Application; + +public class AwemeHostApplication extends Application { + @Override + public void onCreate() { + super.onCreate(); + throw new RuntimeException("sub!"); + } +} \ No newline at end of file diff --git a/aweme/src/main/java/com/ss/android/ugc/aweme/base/ui/FlippableViewPager.java b/aweme/src/main/java/com/ss/android/ugc/aweme/base/ui/FlippableViewPager.java new file mode 100644 index 0000000..8940ce5 --- /dev/null +++ b/aweme/src/main/java/com/ss/android/ugc/aweme/base/ui/FlippableViewPager.java @@ -0,0 +1,29 @@ +package com.ss.android.ugc.aweme.base.ui; + +import android.content.Context; +import android.util.AttributeSet; + +import dmt.viewpager.DmtViewPager; + +public class FlippableViewPager extends DmtViewPager { + + public FlippableViewPager(Context context) { + super(context); + throw new RuntimeException("sub!"); + } + + public FlippableViewPager(Context context, AttributeSet attrs) { + super(context, attrs); + throw new RuntimeException("sub!"); + } + + public FlippableViewPager(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + throw new RuntimeException("sub!"); + } + + public FlippableViewPager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + throw new RuntimeException("sub!"); + } +} \ No newline at end of file diff --git a/aweme/src/main/java/com/ss/android/ugc/aweme/detail/ui/DetailActivity.java b/aweme/src/main/java/com/ss/android/ugc/aweme/detail/ui/DetailActivity.java new file mode 100644 index 0000000..58af961 --- /dev/null +++ b/aweme/src/main/java/com/ss/android/ugc/aweme/detail/ui/DetailActivity.java @@ -0,0 +1,15 @@ +package com.ss.android.ugc.aweme.detail.ui; + +import android.os.Bundle; + +import androidx.annotation.Nullable; + +import com.bytedance.ies.uikit.base.AbsActivity; + +public class DetailActivity extends AbsActivity { + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + throw new RuntimeException("sub!"); + } +} diff --git a/aweme/src/main/java/com/ss/android/ugc/aweme/emoji/model/Emoji.java b/aweme/src/main/java/com/ss/android/ugc/aweme/emoji/model/Emoji.java new file mode 100644 index 0000000..5087ba0 --- /dev/null +++ b/aweme/src/main/java/com/ss/android/ugc/aweme/emoji/model/Emoji.java @@ -0,0 +1,4 @@ +package com.ss.android.ugc.aweme.emoji.model; + +public class Emoji { +} diff --git a/aweme/src/main/java/com/ss/android/ugc/aweme/emoji/similaremoji/EmojiDetailDialogNew.java b/aweme/src/main/java/com/ss/android/ugc/aweme/emoji/similaremoji/EmojiDetailDialogNew.java new file mode 100644 index 0000000..52bcaab --- /dev/null +++ b/aweme/src/main/java/com/ss/android/ugc/aweme/emoji/similaremoji/EmojiDetailDialogNew.java @@ -0,0 +1,25 @@ +package com.ss.android.ugc.aweme.emoji.similaremoji; + +import android.content.Context; + +import androidx.annotation.NonNull; + +import com.ss.android.ugc.aweme.emoji.store.view.EmojiBottomSheetDialog; + +public class EmojiDetailDialogNew extends EmojiBottomSheetDialog { + + public EmojiDetailDialogNew(@NonNull Context context) { + super(context); + throw new RuntimeException("sub!"); + } + + public EmojiDetailDialogNew(@NonNull Context context, int theme) { + super(context, theme); + throw new RuntimeException("sub!"); + } + + protected EmojiDetailDialogNew(@NonNull Context context, boolean cancelable, OnCancelListener cancelListener) { + super(context, cancelable, cancelListener); + throw new RuntimeException("sub!"); + } +} diff --git a/aweme/src/main/java/com/ss/android/ugc/aweme/emoji/store/view/EmojiBottomSheetDialog.java b/aweme/src/main/java/com/ss/android/ugc/aweme/emoji/store/view/EmojiBottomSheetDialog.java new file mode 100644 index 0000000..b8609b8 --- /dev/null +++ b/aweme/src/main/java/com/ss/android/ugc/aweme/emoji/store/view/EmojiBottomSheetDialog.java @@ -0,0 +1,24 @@ +package com.ss.android.ugc.aweme.emoji.store.view; + +import android.content.Context; + +import androidx.annotation.NonNull; + +import com.google.android.material.bottomsheet.BottomSheetDialog; + +public class EmojiBottomSheetDialog extends BottomSheetDialog { + public EmojiBottomSheetDialog(@NonNull Context context) { + super(context); + throw new RuntimeException("sub!"); + } + + public EmojiBottomSheetDialog(@NonNull Context context, int theme) { + super(context, theme); + throw new RuntimeException("sub!"); + } + + protected EmojiBottomSheetDialog(@NonNull Context context, boolean cancelable, OnCancelListener cancelListener) { + super(context, cancelable, cancelListener); + throw new RuntimeException("sub!"); + } +} diff --git a/aweme/src/main/java/com/ss/android/ugc/aweme/feed/model/Aweme.java b/aweme/src/main/java/com/ss/android/ugc/aweme/feed/model/Aweme.java new file mode 100644 index 0000000..a219459 --- /dev/null +++ b/aweme/src/main/java/com/ss/android/ugc/aweme/feed/model/Aweme.java @@ -0,0 +1,4 @@ +package com.ss.android.ugc.aweme.feed.model; + +public class Aweme { +} diff --git a/aweme/src/main/java/com/ss/android/ugc/aweme/homepage/ui/view/MainFlippableViewPager.java b/aweme/src/main/java/com/ss/android/ugc/aweme/homepage/ui/view/MainFlippableViewPager.java new file mode 100644 index 0000000..33452c9 --- /dev/null +++ b/aweme/src/main/java/com/ss/android/ugc/aweme/homepage/ui/view/MainFlippableViewPager.java @@ -0,0 +1,28 @@ +package com.ss.android.ugc.aweme.homepage.ui.view; + +import android.content.Context; +import android.util.AttributeSet; + +import com.ss.android.ugc.aweme.base.ui.FlippableViewPager; + +public class MainFlippableViewPager extends FlippableViewPager { + public MainFlippableViewPager(Context context) { + super(context); + throw new RuntimeException("sub!"); + } + + public MainFlippableViewPager(Context context, AttributeSet attrs) { + super(context, attrs); + throw new RuntimeException("sub!"); + } + + public MainFlippableViewPager(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + throw new RuntimeException("sub!"); + } + + public MainFlippableViewPager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + throw new RuntimeException("sub!"); + } +} diff --git a/aweme/src/main/java/com/ss/android/ugc/aweme/homepage/ui/view/MainTabStripScrollView.java b/aweme/src/main/java/com/ss/android/ugc/aweme/homepage/ui/view/MainTabStripScrollView.java new file mode 100644 index 0000000..6c6276d --- /dev/null +++ b/aweme/src/main/java/com/ss/android/ugc/aweme/homepage/ui/view/MainTabStripScrollView.java @@ -0,0 +1,27 @@ +package com.ss.android.ugc.aweme.homepage.ui.view; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.HorizontalScrollView; + +public class MainTabStripScrollView extends HorizontalScrollView { + public MainTabStripScrollView(Context context) { + super(context); + throw new RuntimeException("sub!"); + } + + public MainTabStripScrollView(Context context, AttributeSet attrs) { + super(context, attrs); + throw new RuntimeException("sub!"); + } + + public MainTabStripScrollView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + throw new RuntimeException("sub!"); + } + + public MainTabStripScrollView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + throw new RuntimeException("sub!"); + } +} diff --git a/aweme/src/main/java/com/ss/android/ugc/aweme/main/MainActivity.java b/aweme/src/main/java/com/ss/android/ugc/aweme/main/MainActivity.java new file mode 100644 index 0000000..c357704 --- /dev/null +++ b/aweme/src/main/java/com/ss/android/ugc/aweme/main/MainActivity.java @@ -0,0 +1,21 @@ +package com.ss.android.ugc.aweme.main; + +import android.os.Bundle; + +import androidx.annotation.Nullable; + +import com.bytedance.ies.uikit.base.AbsActivity; + +public class MainActivity extends AbsActivity { + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + throw new RuntimeException("sub!"); + } + + @Override + protected void onResume() { + super.onResume(); + throw new RuntimeException("sub!"); + } +} \ No newline at end of file diff --git a/aweme/src/main/java/com/ss/android/ugc/aweme/main/MainFragment.java b/aweme/src/main/java/com/ss/android/ugc/aweme/main/MainFragment.java new file mode 100644 index 0000000..ae83633 --- /dev/null +++ b/aweme/src/main/java/com/ss/android/ugc/aweme/main/MainFragment.java @@ -0,0 +1,28 @@ +package com.ss.android.ugc.aweme.main; + +import android.os.Bundle; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.bytedance.ies.uikit.base.AbsFragment; + +public class MainFragment extends AbsFragment { + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + throw new RuntimeException("sub!"); + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + throw new RuntimeException("sub!"); + } + + @Override + public void onPause() { + super.onPause(); + throw new RuntimeException("sub!"); + } +} diff --git a/aweme/src/main/java/dmt/viewpager/DmtViewPager.java b/aweme/src/main/java/dmt/viewpager/DmtViewPager.java new file mode 100644 index 0000000..293cca2 --- /dev/null +++ b/aweme/src/main/java/dmt/viewpager/DmtViewPager.java @@ -0,0 +1,33 @@ +package dmt.viewpager; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.ViewGroup; + +public class DmtViewPager extends ViewGroup { + + public DmtViewPager(Context context) { + super(context); + throw new RuntimeException("sub!"); + } + + public DmtViewPager(Context context, AttributeSet attrs) { + super(context, attrs); + throw new RuntimeException("sub!"); + } + + public DmtViewPager(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + throw new RuntimeException("sub!"); + } + + public DmtViewPager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + throw new RuntimeException("sub!"); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + throw new RuntimeException("sub!"); + } +} diff --git a/aweme/src/test/java/com/ss/android/ugc/aweme/ExampleUnitTest.kt b/aweme/src/test/java/com/ss/android/ugc/aweme/ExampleUnitTest.kt new file mode 100644 index 0000000..178e72f --- /dev/null +++ b/aweme/src/test/java/com/ss/android/ugc/aweme/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.ss.android.ugc.aweme + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..15dff9e --- /dev/null +++ b/build.gradle @@ -0,0 +1,10 @@ +buildscript { + ext { + compose_ui_version = '1.1.1' + } +}// Top-level build file where you can add configuration options common to all sub-projects/modules. +plugins { + id 'com.android.application' version '7.3.1' apply false + id 'com.android.library' version '7.3.1' apply false + id 'org.jetbrains.kotlin.android' version '1.6.10' apply false +} \ No newline at end of file diff --git a/core/.gitignore b/core/.gitignore new file mode 100644 index 0000000..956c004 --- /dev/null +++ b/core/.gitignore @@ -0,0 +1,2 @@ +/build +/release \ No newline at end of file diff --git a/core/build.gradle b/core/build.gradle new file mode 100644 index 0000000..eb9b548 --- /dev/null +++ b/core/build.gradle @@ -0,0 +1,52 @@ +plugins { + id 'com.android.library' + id 'org.jetbrains.kotlin.android' +} + +android { + namespace 'com.freegang.xpler' + compileSdk 32 + + defaultConfig { + minSdk 21 + targetSdk 32 + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles "consumer-rules.pro" + } + + buildFeatures { + viewBinding true + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } + kotlinOptions { + jvmTarget = '1.8' + } +} + +dependencies { + implementation 'androidx.core:core-ktx:1.7.0' + implementation 'androidx.appcompat:appcompat:1.4.1' + implementation 'com.google.android.material:material:1.5.0' + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.3' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' + + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4' + + //Xposed Api + compileOnly files('libs/api-82.jar') + compileOnly files('libs/api-82-sources.jar') + //Sub App + compileOnly project(":aweme") +} \ No newline at end of file diff --git a/core/consumer-rules.pro b/core/consumer-rules.pro new file mode 100644 index 0000000..e69de29 diff --git a/core/libs/api-82-sources.jar b/core/libs/api-82-sources.jar new file mode 100644 index 0000000000000000000000000000000000000000..6e3a0fb72dfb41904e6ab2f8135bf4d4e30ff0ac GIT binary patch literal 55741 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 z�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

sh`9j$%kzNe!htF>!xBqp;=w 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 F{SW-};m800 literal 0 HcmV?d00001 diff --git a/core/libs/api-82.jar b/core/libs/api-82.jar new file mode 100644 index 0000000000000000000000000000000000000000..eec212fa8b5c288645956e00b8c618cf4f970cb7 GIT binary patch literal 25478 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 Cn}aI= literal 0 HcmV?d00001 diff --git a/core/proguard-rules.pro b/core/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/core/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/core/src/androidTest/java/com/freegang/xpler/ExampleInstrumentedTest.kt b/core/src/androidTest/java/com/freegang/xpler/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..9376c58 --- /dev/null +++ b/core/src/androidTest/java/com/freegang/xpler/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.freegang.xpler + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.freegang.xpler.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/core/src/main/AndroidManifest.xml b/core/src/main/AndroidManifest.xml new file mode 100644 index 0000000..2d915ee --- /dev/null +++ b/core/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/core/src/main/assets/xposed_init b/core/src/main/assets/xposed_init new file mode 100644 index 0000000..966c521 --- /dev/null +++ b/core/src/main/assets/xposed_init @@ -0,0 +1 @@ +com.freegang.xpler.HookInit \ No newline at end of file diff --git a/core/src/main/java/com/freegang/base/BaseHook.kt b/core/src/main/java/com/freegang/base/BaseHook.kt new file mode 100644 index 0000000..1706ad0 --- /dev/null +++ b/core/src/main/java/com/freegang/base/BaseHook.kt @@ -0,0 +1,188 @@ +package com.freegang.base + +import android.app.Activity +import android.content.Context +import android.content.Context.VIBRATOR_SERVICE +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.os.* +import android.view.View +import android.widget.FrameLayout +import android.widget.Toast +import com.freegang.view.KDialog +import com.freegang.view.adapter.DialogChoiceAdapter +import com.freegang.xpler.R +import com.freegang.xpler.databinding.DialogChoiceLayoutBinding +import com.freegang.xpler.databinding.DialogMessageLayoutBinding +import com.freegang.xpler.utils.app.KNotifiUtils +import com.freegang.xpler.utils.log.KLogCat +import com.freegang.xpler.utils.other.KResourceUtils +import de.robv.android.xposed.callbacks.XC_LoadPackage +import kotlinx.coroutines.* + +abstract class BaseHook( + protected val lpparam: XC_LoadPackage.LoadPackageParam, +) { + private val mainScope: CoroutineScope = CoroutineScope(Dispatchers.Main) + private var toast: Toast? = null + + init { + this.onHook() + } + + abstract fun onHook() + + fun launch(block: suspend CoroutineScope.() -> Unit): Job { + val exceptionHandler = CoroutineExceptionHandler { coroutineContext, throwable -> + KLogCat.d("发生异常: \n${throwable.stackTraceToString()}") + if (coroutineContext.isActive) { + coroutineContext.cancel() + } + } + val job = mainScope.launch(exceptionHandler) { + try { + block.invoke(this) + } catch (e: Exception) { + KLogCat.d("发生异常: \n${e.stackTraceToString()}") + } + } + return job + } + + fun showToast(context: Context, message: String) { + mainScope.launch { + toast = if (toast == null) { + Toast.makeText(context.applicationContext, null, Toast.LENGTH_LONG) + } else { + toast?.cancel() + Toast.makeText(context.applicationContext, null, Toast.LENGTH_LONG) + } + toast?.setText(message) + toast?.show() + } + } + + fun vibrate(context: Context, milliseconds: Long) { + val vibrator = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + val vibratorManager = context.getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager + vibratorManager.defaultVibrator + } else { + context.getSystemService(VIBRATOR_SERVICE) as Vibrator + } + vibrator.vibrate(milliseconds) + } + + fun showMessageDialog( + activity: Activity, + title: CharSequence, + content: CharSequence, + cancel: CharSequence = "取消", + confirm: CharSequence = "确定", + singleButton: Boolean = false, //只会响应 onConfirm 方法 + onConfirm: () -> Unit = {}, + onCancel: () -> Unit = {}, + ) { + val kDialog = KDialog(activity) + + val dialogView = KResourceUtils.inflateView(activity, R.layout.dialog_message_layout) + val binding = DialogMessageLayoutBinding.bind(dialogView) + + binding.messageDialogContainer.background = KResourceUtils.getDrawable(R.drawable.dialog_background) + if (singleButton) { + binding.messageDialogCancel.visibility = View.GONE + binding.messageDialogConfirm.background = KResourceUtils.getDrawable(R.drawable.dialog_single_button_background) + } else { + binding.messageDialogCancel.background = KResourceUtils.getDrawable(R.drawable.dialog_cancel_button_background) + binding.messageDialogConfirm.background = KResourceUtils.getDrawable(R.drawable.dialog_confirm_button_background) + } + binding.messageDialogTitle.text = title + binding.messageDialogContent.text = content + binding.messageDialogCancel.text = cancel + binding.messageDialogConfirm.text = confirm + binding.messageDialogCancel.setOnClickListener { + onCancel.invoke() + kDialog.dismiss() + } + binding.messageDialogConfirm.setOnClickListener { + onConfirm.invoke() + kDialog.dismiss() + } + + kDialog.setView(binding.root) + kDialog.show() + } + + fun showChoiceDialog( + activity: Activity, + title: CharSequence, + items: Array, + cancel: CharSequence = "取消", + onChoice: (view: View, item: CharSequence, position: Int) -> Unit, + onCancel: () -> Unit = {}, + ) { + val kDialog = KDialog(activity) + val dialogView = KResourceUtils.inflateView(activity, R.layout.dialog_choice_layout) + val binding = DialogChoiceLayoutBinding.bind(dialogView) + + binding.choiceDialogContainer.background = KResourceUtils.getDrawable(R.drawable.dialog_background) + binding.choiceDialogCancel.background = KResourceUtils.getDrawable(R.drawable.dialog_single_button_background) + + binding.choiceDialogTitle.text = title + binding.choiceDialogCancel.text = cancel + binding.choiceDialogCancel.setOnClickListener { + onCancel.invoke() + kDialog.dismiss() + } + + binding.choiceDialogList.adapter = DialogChoiceAdapter(activity, items) + binding.choiceDialogList.divider = ColorDrawable(Color.TRANSPARENT) + binding.choiceDialogList.selector = KResourceUtils.getDrawable(R.drawable.item_selector_background) + binding.choiceDialogList.setOnItemClickListener { _, view, position, _ -> + onChoice.invoke(view, items[position], position) + kDialog.dismiss() + } + + kDialog.setView(binding.root) + kDialog.show() + } + + fun showNotification( + context: Context, + notifyId: Int, + title: String, + text: String, + ) { + val channelId = "Freedom+" + val channelName = "Freedom+ Message" + KNotifiUtils.showNotification( + context = context, + notifyId = notifyId, + channelId = channelId, + channelName = channelName, + title = title, + text = text, + ) + } + + fun showDownloadNotification( + context: Context, + notifyId: Int, + title: String, + inProgressText: String = "%d%%", + finishedText: String = "下载完成!", + listener: (notify: KNotifiUtils.ProgressNotification) -> Unit, + ) { + val channelId = "Freedom+" + val channelName = "Freedom+ Message" + KNotifiUtils.showProgressNotification( + context = context, + notifyId = notifyId, + channelId = channelId, + channelName = channelName, + title = title, + inProgressText = inProgressText, + finishedText = finishedText, + listener = listener, + ) + } +} \ No newline at end of file diff --git a/core/src/main/java/com/freegang/config/Config.kt b/core/src/main/java/com/freegang/config/Config.kt new file mode 100644 index 0000000..be8ce5c --- /dev/null +++ b/core/src/main/java/com/freegang/config/Config.kt @@ -0,0 +1,100 @@ +package com.freegang.config + +import android.content.Context +import android.os.Environment +import android.util.JsonWriter +import com.freegang.xpler.utils.io.KFileUtils.child +import com.freegang.xpler.utils.io.KFileUtils.need +import com.freegang.xpler.utils.io.KStorageUtils.storageRootFile +import com.freegang.xpler.utils.json.KJSONUtils +import com.freegang.xpler.utils.json.KJSONUtils.getBooleanOrDefault +import com.freegang.xpler.utils.json.KJSONUtils.getLongOrDefault +import com.freegang.xpler.utils.json.KJSONUtils.getStringOrDefault +import java.io.File +import java.io.FileWriter + +data class Config( + var isSupportHint: Boolean = true, //是否显示兼容 + var isOwnerDir: Boolean = false, //是否按视频创作者单独创建文件夹 + var isDownload: Boolean = false, //是否开启视频/图文/音乐下载 + var isEmoji: Boolean = false, //是否开启评论区图片/表情包保存 + var isHideTab: Boolean = false, //是否开启隐藏顶部tab + var hideTabKeyword: String = "探索, 商城", //隐藏顶部tab包含的关键字, 逗号隔开 + var versionName: String = "", //版本名称 + var versionCode: Long = 0L, //版本代码 + var dyVersionName: String = "", //抖音版本名称 + var dyVersionCode: Long = 0L, //抖音版本代码 +) { + companion object { + private var config: Config? = null + + private fun getSettingFile(context: Context): File { + return getConfigDir(context) + .child("setting.json") + .need(true) + } + + fun read(context: Context): Config { + val settingFile = getSettingFile(context) + + val setting = settingFile.readText() + if (setting.isBlank()) return Config() + + val json = KJSONUtils.parse(setting) + config = Config( + isSupportHint = json.getBooleanOrDefault("isSupportHint"), + isOwnerDir = json.getBooleanOrDefault("isOwnerDir"), + isDownload = json.getBooleanOrDefault("isDownload"), + isEmoji = json.getBooleanOrDefault("isEmoji"), + isHideTab = json.getBooleanOrDefault("isHideTab"), + hideTabKeyword = json.getStringOrDefault("hideTabKeyword"), + versionName = json.getStringOrDefault("versionName"), + versionCode = json.getLongOrDefault("versionCode"), + dyVersionName = json.getStringOrDefault("dyVersionName"), + dyVersionCode = json.getLongOrDefault("dyVersionCode"), + ) + + return config!! + } + + fun get(): Config { + if (config == null) throw Exception("模块配置未加载, 请先调用read方法!") + return config!! + } + + fun getConfigDir(context: Context): File { + return getFreedomDir(context) + .child(".config") + } + + fun getFreedomDir(context: Context): File { + return context.applicationContext.storageRootFile + .child(Environment.DIRECTORY_DCIM) + .child("Freedom") + } + } + + fun save(context: Context) { + val settingFile = getSettingFile(context) + + val jsonWriter = JsonWriter(FileWriter(settingFile)) + .beginObject() + .name("isSupportHint").value(isSupportHint) + .name("isOwnerDir").value(isOwnerDir) + .name("isDownload").value(isDownload) + .name("isEmoji").value(isEmoji) + .name("isHideTab").value(isHideTab) + .name("hideTabKeyword").value(hideTabKeyword) + .name("versionName").value(versionName) + .name("versionCode").value(versionCode) + .name("dyVersionName").value(dyVersionName) + .name("dyVersionCode").value(dyVersionCode) + .endObject() + jsonWriter.flush() + jsonWriter.close() + } + + fun remove(context: Context) { + getSettingFile(context).delete() + } +} diff --git a/core/src/main/java/com/freegang/config/Version.kt b/core/src/main/java/com/freegang/config/Version.kt new file mode 100644 index 0000000..ac5ca14 --- /dev/null +++ b/core/src/main/java/com/freegang/config/Version.kt @@ -0,0 +1,48 @@ +package com.freegang.config + +import com.freegang.xpler.utils.json.KJSONUtils.firstJsonObject +import com.freegang.xpler.utils.json.KJSONUtils.getLongOrDefault +import com.freegang.xpler.utils.json.KJSONUtils.getStringOrDefault +import com.freegang.xpler.utils.json.KJSONUtils.parseJSON +import com.freegang.xpler.utils.net.KHttpUtils + +object Version { + // Api + private const val githubReleasesApi = "https://api.github.com/repos/GangJust/FreedomPlus/releases/latest" + //private const val githubReleasesApi = "https://api.github.com/repos/GangJust/Freedom/releases/latest" + + // 获取Github最后一次 releases + fun getRemoteReleasesLatest(): VersionConfig? { + val get = KHttpUtils.get(githubReleasesApi) + if (get.isEmpty()) return null + if (!get.contains("browser_download_url")) return null + return parseVersionConfig(get) + } + + // 解析出版本信息 + private fun parseVersionConfig(s: String): VersionConfig { + val json = s.parseJSON() + return VersionConfig( + htmlUrl = json.getStringOrDefault("html_url"), + tagName = json.getStringOrDefault("tag_name"), + name = json.getStringOrDefault("name"), + body = json.getStringOrDefault("body"), + size = json.getJSONArray("assets").firstJsonObject().getLongOrDefault("size"), + createdAt = json.getJSONArray("assets").firstJsonObject().getStringOrDefault("created_at"), + updatedAt = json.getJSONArray("assets").firstJsonObject().getStringOrDefault("updated_at"), + browserDownloadUrl = json.getJSONArray("assets").firstJsonObject().getStringOrDefault("browser_download_url"), + ) + } +} + +// 版本信息 +data class VersionConfig( + val htmlUrl: String, + val tagName: String, + val name: String, + val body: String, + val size: Long, + val createdAt: String, + val updatedAt: String, + val browserDownloadUrl: String, +) \ No newline at end of file diff --git a/core/src/main/java/com/freegang/douyin/DouYinMain.kt b/core/src/main/java/com/freegang/douyin/DouYinMain.kt new file mode 100644 index 0000000..b2a9161 --- /dev/null +++ b/core/src/main/java/com/freegang/douyin/DouYinMain.kt @@ -0,0 +1,49 @@ +package com.freegang.douyin + +import android.app.Application +import com.freegang.base.BaseHook +import com.freegang.config.Config +import com.freegang.xpler.utils.app.KAppCrashUtils +import com.freegang.xpler.utils.io.KStorageUtils.hasOperationStorage +import com.freegang.xpler.utils.log.KLogCat +import com.freegang.xpler.xp.hookClass +import com.ss.android.ugc.aweme.app.host.AwemeHostApplication +import de.robv.android.xposed.callbacks.XC_LoadPackage + +class DouYinMain( + lpparam: XC_LoadPackage.LoadPackageParam, + private val application: Application, +) : BaseHook(lpparam) { + override fun onHook() { + appInit() + } + + private fun appInit() { + lpparam.hookClass(AwemeHostApplication::class.java) + .method("onCreate") { + onBefore { + val app = it.thisObject as Application + + //日志工具 + KLogCat.init(app) + KLogCat.openStorage() + + //全局异常捕获工具 + KAppCrashUtils.instance.init(app, "抖音异常崩溃!") + + //文件读写权限检查 + if (!application.hasOperationStorage) { + showToast(application, "抖音没有文件读写权限!") + return@onBefore + } + + //Hook + Config.read(app) + HMainActivity(lpparam, app) + HAbsActivity(lpparam) + HMainFragment(lpparam) + HEmojiDetailDialogNew(lpparam) + } + } + } +} \ No newline at end of file diff --git a/core/src/main/java/com/freegang/douyin/HAbsActivity.kt b/core/src/main/java/com/freegang/douyin/HAbsActivity.kt new file mode 100644 index 0000000..6adb7df --- /dev/null +++ b/core/src/main/java/com/freegang/douyin/HAbsActivity.kt @@ -0,0 +1,299 @@ +package com.freegang.douyin + +import android.content.ClipboardManager +import android.content.Context +import android.os.Bundle +import android.view.View +import android.view.ViewGroup +import android.widget.RelativeLayout +import com.bytedance.ies.uikit.base.AbsActivity +import com.freegang.base.BaseHook +import com.freegang.config.Config +import com.freegang.douyin.logic.DownloadLogic +import com.freegang.douyin.logic.SaveLogic +import com.freegang.douyin.model.DeAweme +import com.freegang.douyin.model.ImageUrlStruct +import com.freegang.xpler.R +import com.freegang.xpler.databinding.HookAppbarLayoutBinding +import com.freegang.xpler.utils.json.KJSONUtils.getStringOrDefault +import com.freegang.xpler.utils.json.KJSONUtils.isEmpties +import com.freegang.xpler.utils.json.KJSONUtils.parseJSON +import com.freegang.xpler.utils.json.KJSONUtils.toJSONObjectArray +import com.freegang.xpler.utils.log.KLogCat +import com.freegang.xpler.utils.net.KHttpUtils +import com.freegang.xpler.utils.other.KResourceUtils +import com.freegang.xpler.xp.* +import com.ss.android.ugc.aweme.detail.ui.DetailActivity +import com.ss.android.ugc.aweme.feed.model.Aweme +import com.ss.android.ugc.aweme.main.MainActivity +import de.robv.android.xposed.XC_MethodHook +import de.robv.android.xposed.callbacks.XC_LoadPackage +import kotlinx.coroutines.* +import org.json.JSONException +import org.json.JSONObject + + +/// 基类Activity +class HAbsActivity( + lpparam: XC_LoadPackage.LoadPackageParam, +) : BaseHook(lpparam) { + private var primaryClipChangedListener: ClipboardManager.OnPrimaryClipChangedListener? = null + + override fun onHook() { + val config = Config.get() + + lpparam.hookClass(AbsActivity::class.java) + .method("onCreate", Bundle::class.java) { + onAfter { + if (!config.isEmoji) return@onAfter + getCommentImage(it) + } + } + .method("onResume") { + onAfter { + if (!config.isDownload) return@onAfter + addClipboardListener(it, config) + } + } + .method("onPause") { + onBefore { + if (!config.isDownload) return@onBefore + removeClipboardListener(it) + } + } + } + + // 添加剪贴板监听 + private fun addClipboardListener(it: XC_MethodHook.MethodHookParam, config: Config) { + val absActivity = it.thisObject as AbsActivity + val clipboardManager = absActivity.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + + primaryClipChangedListener = ClipboardManager.OnPrimaryClipChangedListener { + val clipData = clipboardManager.primaryClip + if (!clipboardManager.hasPrimaryClip() || clipData!!.itemCount <= 0) return@OnPrimaryClipChangedListener + + //获取剪贴板内容 + val clipDataItem = clipData.getItemAt(0) + val shareText = clipDataItem.text.toString() + if (!shareText.contains("http")) return@OnPrimaryClipChangedListener + + //跳过直播链接, 按文本检查 + if (shareText.contains("【抖音】") && shareText.contains("正在直播") && shareText.contains("一起支持")) { + showToast(absActivity, "不支持直播!") + return@OnPrimaryClipChangedListener + } + + // @Deprecated + //showToast(absActivity, "复制成功!\n$shareText") + + //截取短链接, 一般这个截取逻辑能用到死, 但是不排除抖音更新分享文本格式, 如果真更新再说. + //val urlIndexOf = shareText.indexOf("http") + //val sortUrl = shareText.substring(urlIndexOf) + //mainLogic(absActivity, sortUrl, config) + + if (absActivity is DetailActivity || absActivity is MainActivity) { + val methods = absActivity.findMethodsByReturnType(Aweme::class.java) + if (methods.isNotEmpty()) { + val aweme = methods.first().call(absActivity) + DownloadLogic(this@HAbsActivity, absActivity, aweme, config.isOwnerDir) + } + } + } + clipboardManager.addPrimaryClipChangedListener(primaryClipChangedListener) + } + + // 移除剪贴板监听 + private fun removeClipboardListener(it: XC_MethodHook.MethodHookParam) { + val absActivity = it.thisObject as Context + val clipboardManager = absActivity.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + clipboardManager.removePrimaryClipChangedListener(primaryClipChangedListener) + } + + // 获取评论区图片 + private fun getCommentImage(it: XC_MethodHook.MethodHookParam) { + val absActivity = it.thisObject as AbsActivity + if (absActivity !is DetailActivity) return + + launch { + delay(200L) + + //获取内容 + var urlList: List = listOf() + val methods = absActivity.findMethodsByReturnType(Aweme::class.java) + if (methods.isNotEmpty()) { + val aweme = methods.first().call(absActivity) ?: return@launch + + //不处理评论区视频(如果是视频的话) + val video = aweme.getObjectField("video") + if (video != null) return@launch + + //如果没有背景音乐, 代表是评论区图片(图文也可能没有背景音乐, 但是少, 就这么判断了) + val music = aweme.getObjectField("music") + if (music == null) { + val image = aweme.getObjectField>("images")?.first() ?: return@launch + urlList = image.getObjectField>("urlList") ?: return@launch + } + } + + if (urlList.isEmpty()) return@launch + + //重新构建顶部操作栏 + val contentView: View = absActivity.window.decorView.findViewById(android.R.id.content) + val outViews = ArrayList() + contentView.findViewsWithText(outViews, "返回", View.FIND_VIEWS_WITH_CONTENT_DESCRIPTION) + val backBtn = outViews.first { it.contentDescription.equals("返回") } + + //清空旧视图 + val viewGroup = backBtn.parent as ViewGroup + viewGroup.removeAllViews() + + //重新构建视图 + val appbar = KResourceUtils.inflateView(viewGroup.context, R.layout.hook_appbar_layout) + val binding = HookAppbarLayoutBinding.bind(appbar) + binding.backBtn.setOnClickListener { + backBtn.performClick() + } + binding.saveBtn.setOnClickListener { + SaveLogic(this@HAbsActivity, it.context, urlList) + } + viewGroup.addView(appbar) + } + } + + + ///// @Deprecated + // 获取接口中的视频信息 + @Deprecated("Deprecated") + private fun mainLogic(absActivity: AbsActivity, sortUrl: String, config: Config) { + launch { + try { + val originUrl = getOriginalUrl(sortUrl) + val detailId = getDetailId(originUrl) + if (detailId.isBlank()) { + showToast(absActivity, "未获取到视频ID!") + return@launch + } + + var detailJson = getDetailJson(detailId) + for (i in 0 until 10) { + if (detailJson != "blocked") break //出错重试, 最多10次 + detailJson = getDetailJson(detailId) + } + + val json = detailJson.parseJSON().apply { + if (isEmpties) { + showToast(absActivity, detailJson) //json解析失败, 直接返回失败文本 + return@launch + } else if (isNull("aweme_detail")) { + showToast(absActivity, "未获取到视频信息!") + return@launch + } + } + + val detail = parseDetailInfo(json) + DownloadLogic(this@HAbsActivity, absActivity, detail, config.isOwnerDir) + } catch (e: JSONException) { + showToast(absActivity, "视频信息解析失败!") + KLogCat.e("视频信息解析失败:\n${e.stackTraceToString()}") + } + } + } + + /** + * 获取原始地址 + * @param sortUrl 短链接 + */ + private suspend fun getOriginalUrl(sortUrl: String): String { + return withContext(Dispatchers.IO) { + KHttpUtils.getRedirectsUrl(sortUrl) + } + } + + /** + * 获取视频/图文ID + * @param originUrl 原始url地址 + */ + private fun getDetailId(originUrl: String): String { + val find = Regex("/(\\d+)/").find(originUrl) + return find?.groupValues?.last() ?: "" + } + + /** + * + * 获取单个视频/图文详情 Json + * + * @param videoId 视频/图文ID + */ + private suspend fun getDetailJson(videoId: String): String { + // thanks for: https://github.com/Evil0ctal/Douyin_TikTok_Download_API + val url = "https://www.iesdouyin.com/aweme/v1/web/aweme/detail/" + val params = "aid=1128&version_name=23.5.0&device_platform=android&os_version=2333&aweme_id=$videoId" + return withContext(Dispatchers.IO) { + KHttpUtils.get(url, params) + } + } + + /** + * 解析视频/图文基本信息 + * @param json + */ + @Throws(JSONException::class) + private fun parseDetailInfo(json: JSONObject): DeAweme { + val deAweme = DeAweme() + /// 视频/图文基本信息 Json + val awemeDetail = json.getJSONObject("aweme_detail") + //描述 + deAweme.desc = awemeDetail.getStringOrDefault("desc") + + /// 用户公开的基本信息 Json + val author = awemeDetail.getJSONObject("author") + //昵称 + deAweme.nickname = author.getStringOrDefault("nickname") + //用户唯一(账号)ID + val uniqueId = author.getStringOrDefault("unique_id") + //目前来看, uniqueId为空, shortId即用户Id + deAweme.shortId = author.getStringOrDefault("short_id", uniqueId) + + if (awemeDetail.isNull("images")) { + /// 视频基本信息 Json + val video = awemeDetail.getJSONObject("video") + val playAddrH264 = video.getJSONObject("play_addr_h264") + + val videoUrlList = mutableListOf() + val urlList = playAddrH264.getJSONArray("url_list") + for (index in 0 until urlList.length()) { + videoUrlList.add(urlList.getStringOrDefault(index)) + } + + + deAweme.videoUrlList = videoUrlList + } else { + /// 图文基本信息 Json + val imageUrlStructList = mutableListOf() + val images = awemeDetail.getJSONArray("images").toJSONObjectArray() + for (image in images) { + val imageUrlList = mutableListOf() + val urlList = image.getJSONArray("url_list") + for (index in 0 until urlList.length()) { + imageUrlList.add(urlList.getStringOrDefault(index)) + } + imageUrlStructList.add(ImageUrlStruct(imageUrlList)) + } + + deAweme.imageUrlStructList = imageUrlStructList + } + + /// 背景音乐基本信息 Json + val music = awemeDetail.getJSONObject("music") + val playUrl = music.getJSONObject("play_url") + + val musicUrlList = mutableListOf() + val urlList = playUrl.getJSONArray("url_list") + for (index in 0 until urlList.length()) { + musicUrlList.add(urlList.getStringOrDefault(index)) + } + deAweme.musicUrlList = musicUrlList + + return deAweme + } +} \ No newline at end of file diff --git a/core/src/main/java/com/freegang/douyin/HEmojiDetailDialogNew.kt b/core/src/main/java/com/freegang/douyin/HEmojiDetailDialogNew.kt new file mode 100644 index 0000000..9deb6f6 --- /dev/null +++ b/core/src/main/java/com/freegang/douyin/HEmojiDetailDialogNew.kt @@ -0,0 +1,102 @@ +package com.freegang.douyin + +import android.graphics.Color +import android.os.Bundle +import android.view.View +import android.view.ViewGroup +import android.widget.LinearLayout +import com.freegang.base.BaseHook +import com.freegang.config.Config +import com.freegang.douyin.logic.SaveLogic +import com.freegang.xpler.R +import com.freegang.xpler.databinding.HookAddSaveEmojiLayoutBinding +import com.freegang.xpler.utils.other.KResourceUtils +import com.freegang.xpler.xp.findFieldByType +import com.freegang.xpler.xp.getObjectField +import com.freegang.xpler.xp.hookClass +import com.ss.android.ugc.aweme.emoji.model.Emoji +import com.ss.android.ugc.aweme.emoji.similaremoji.EmojiDetailDialogNew +import com.ss.android.ugc.aweme.emoji.store.view.EmojiBottomSheetDialog +import de.robv.android.xposed.XC_MethodHook +import de.robv.android.xposed.callbacks.XC_LoadPackage +import kotlinx.coroutines.delay + +class HEmojiDetailDialogNew( + lpparam: XC_LoadPackage.LoadPackageParam, +) : BaseHook(lpparam) { + var urlList: List = listOf() + + override fun onHook() { + val config = Config.get() + + lpparam.hookClass(EmojiDetailDialogNew::class.java) + .constructorsAll { + onAfter { + if (!config.isEmoji) return@onAfter + getEmojiDetail(it) + } + } + + lpparam.hookClass(EmojiBottomSheetDialog::class.java) + .method("onCreate", Bundle::class.java) { + onAfter { + if (!config.isEmoji) return@onAfter + rebuildEmojiView(it) + } + } + } + + //获取表情内容 + private fun getEmojiDetail(it: XC_MethodHook.MethodHookParam) { + if (it.args.isEmpty()) return + + val first = it.args.first() + val emojiList = first.findFieldByType(Emoji::class.java) + + if (emojiList.isEmpty()) return + val firstEmoji = emojiList.first().get(first) ?: return + + urlList = firstEmoji.getObjectField("animateUrl")?.getObjectField>("urlList") ?: listOf() + } + + //重构表情布局 + private fun rebuildEmojiView(it: XC_MethodHook.MethodHookParam) { + launch { + delay(200L) + + //重新构建当前视图 + val emojiDialog = it.thisObject as EmojiDetailDialogNew + val contentView: View = emojiDialog.window?.decorView?.findViewById(android.R.id.content) ?: return@launch + + val outViews = ArrayList() + contentView.findViewsWithText(outViews, "添加表情", View.FIND_VIEWS_WITH_TEXT) + if (outViews.isEmpty()) return@launch + + val addEmoji = outViews.first() + + //重构添加表情按钮, 增加保存表情按钮 + val parent = addEmoji.parent as ViewGroup + val rootParent = parent.parent as ViewGroup + rootParent.removeView(parent) //移除旧布局 + + val view = KResourceUtils.inflateView(parent.context, R.layout.hook_add_save_emoji_layout) + val binding = HookAddSaveEmojiLayoutBinding.bind(view) + binding.addEmoji.apply { + setTextColor(Color.WHITE) + background = KResourceUtils.getDrawable(R.drawable.hook_add_button) + setOnClickListener { + addEmoji.performClick() + } + } + binding.saveEmoji.apply { + setTextColor(Color.WHITE) + background = KResourceUtils.getDrawable(R.drawable.hook_save_button) + setOnClickListener { v -> + SaveLogic(this@HEmojiDetailDialogNew, v.context, urlList) + } + } + + rootParent.addView(view, 1) //重新添加新布局 + } + } +} \ No newline at end of file diff --git a/core/src/main/java/com/freegang/douyin/HMainActivity.kt b/core/src/main/java/com/freegang/douyin/HMainActivity.kt new file mode 100644 index 0000000..cb64e1a --- /dev/null +++ b/core/src/main/java/com/freegang/douyin/HMainActivity.kt @@ -0,0 +1,73 @@ +package com.freegang.douyin + +import android.app.Application +import android.os.Bundle +import android.text.Html +import com.freegang.base.BaseHook +import com.freegang.config.Config +import com.freegang.xpler.utils.app.KAppVersionUtils.appVersionCode +import com.freegang.xpler.utils.app.KAppVersionUtils.appVersionName +import com.freegang.xpler.utils.io.KStorageUtils.hasOperationStorage +import com.freegang.xpler.xp.hookClass +import com.ss.android.ugc.aweme.main.MainActivity +import de.robv.android.xposed.XC_MethodHook +import de.robv.android.xposed.callbacks.XC_LoadPackage +import kotlinx.coroutines.delay + +class HMainActivity( + lpparam: XC_LoadPackage.LoadPackageParam, + private val application: Application, +) : BaseHook(lpparam) { + + //当前适配版本列表 + private val supportVersions = listOf("24.4.0", "24.5.0") + + override fun onHook() { + val config = Config.get() + lpparam.hookClass(MainActivity::class.java) + .method("onCreate", Bundle::class.java) { + onAfter { + val activity = it.thisObject as MainActivity + showToast(activity, "Freedom+ Attach!") + } + } + .method("onResume") { + onAfter { + showSupportDialog(it, config) + } + } + } + + //抖音版本(版本是否兼容提示) + private fun showSupportDialog(it: XC_MethodHook.MethodHookParam, config: Config) { + val activity = it.thisObject as MainActivity + val versionName = application.appVersionName + val versionCode = application.appVersionCode + + //此版本是否继续提示 + if (!config.isSupportHint && versionCode == config.dyVersionCode && versionName == config.dyVersionName) return + + launch { + delay(2000L) + showMessageDialog( + activity = activity, + title = "Freedom+", + content = Html.fromHtml( + """当前抖音版本为: ${versionName}
+ Freedom+已经兼容适配以下版本:
+ ${supportVersions.joinToString(", ") { s -> if (s == versionName) "$s" else s }}
+ ${if (supportVersions.contains(versionName)) "当前版本已兼容适配!" else "当前版本不能保证所有功能都正常使用!"} + """.trimIndent() + ), + cancel = "此版本不再提示", + confirm = "确定", + onCancel = { + config.isSupportHint = false + config.dyVersionName = versionName + config.dyVersionCode = versionCode + config.save(application) + } + ) + } + } +} \ No newline at end of file diff --git a/core/src/main/java/com/freegang/douyin/HMainFragment.kt b/core/src/main/java/com/freegang/douyin/HMainFragment.kt new file mode 100644 index 0000000..32547b3 --- /dev/null +++ b/core/src/main/java/com/freegang/douyin/HMainFragment.kt @@ -0,0 +1,55 @@ +package com.freegang.douyin + +import android.os.Bundle +import android.view.View +import android.view.ViewGroup +import androidx.core.view.isVisible +import com.freegang.base.BaseHook +import com.freegang.config.Config +import com.freegang.xpler.utils.view.KViewUtils +import com.freegang.xpler.xp.hookClass +import com.ss.android.ugc.aweme.homepage.ui.view.MainFlippableViewPager +import com.ss.android.ugc.aweme.homepage.ui.view.MainTabStripScrollView +import com.ss.android.ugc.aweme.main.MainFragment +import de.robv.android.xposed.callbacks.XC_LoadPackage + +class HMainFragment( + lpparam: XC_LoadPackage.LoadPackageParam, +) : BaseHook(lpparam) { + override fun onHook() { + val config = Config.get() + + lpparam.hookClass(MainFragment::class.java) + .method("onViewCreated", View::class.java, Bundle::class.java) { + onAfter { + if (!config.isHideTab) return@onAfter + val viewGroup = it.args[0] as ViewGroup + hideTabItem(viewGroup, config) + } + } + + //禁止ViewPager左右滑动 + lpparam.hookClass(MainFlippableViewPager::class.java) + .methodAll { + onBefore { + if (!config.isHideTab) return@onBefore + + if (it.method.name.contains("onInterceptTouchEvent|onTouchEvent|dispatchHoverEvent".toRegex())) { + it.result = false + } + } + } + } + + //隐藏tab + private fun hideTabItem(viewGroup: ViewGroup, config: Config) { + val views = KViewUtils.findViews(viewGroup, MainTabStripScrollView::class.java) + if (views.isEmpty()) return + val hideTabKeyword = config.hideTabKeyword + .replace("\\s".toRegex(), "") + .replace(",|,".toRegex(), "|") + KViewUtils.findViewsByDesc(views.first() as ViewGroup, View::class.java, hideTabKeyword.toRegex()).forEach { v -> + v.isVisible = false + } + } +} \ No newline at end of file diff --git a/core/src/main/java/com/freegang/douyin/logic/DownloadLogic.kt b/core/src/main/java/com/freegang/douyin/logic/DownloadLogic.kt new file mode 100644 index 0000000..debe31a --- /dev/null +++ b/core/src/main/java/com/freegang/douyin/logic/DownloadLogic.kt @@ -0,0 +1,260 @@ +package com.freegang.douyin.logic + +import android.app.Activity +import com.freegang.base.BaseHook +import com.freegang.config.Config +import com.freegang.douyin.model.DeAweme +import com.freegang.douyin.model.ImageUrlStruct +import com.freegang.xpler.utils.app.KAlbumUtils +import com.freegang.xpler.utils.io.KFileUtils.child +import com.freegang.xpler.utils.io.KFileUtils.need +import com.freegang.xpler.utils.io.KFileUtils.pureFileName +import com.freegang.xpler.utils.net.KHttpUtils +import com.freegang.xpler.xp.getObjectField +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import java.io.File +import java.io.FileOutputStream + +/// 下载(视频/图文/音乐)逻辑 +class DownloadLogic( + private val hook: BaseHook, + private val activity: Activity, + private val aweme: Any?, + private val isOwnerDir: Boolean = false, +) { + + companion object { + private var downloadNotifyId = 1 + } + + init { + if (aweme is DeAweme) { + showChoiceDialog(aweme) + } else { + deAweme(activity) + } + } + + /** + * 解析Aweme类 + */ + private fun deAweme(activity: Activity) { + if (aweme == null) { + hook.showToast(activity, "未获取到基本信息") + return + } + val deAweme = DeAweme() + + //视频描述 + deAweme.desc = aweme.getObjectField("desc") ?: "" + + //用户信息 + val user = aweme.getObjectField("author") + deAweme.nickname = user?.getObjectField("nickname") ?: "" + deAweme.shortId = user?.getObjectField("uniqueId") ?: "" + if (deAweme.shortId.isEmpty()) deAweme.shortId = user?.getObjectField("shortId") ?: "" //如果uniqueId为空, shortId为账号 + + //视频 + deAweme.videoUrlList = aweme + .getObjectField("video") + ?.getObjectField("h264PlayAddr") + ?.getObjectField>("urlList") + ?: listOf() + + //图文列表 + val imageUrlList = mutableListOf() + val imagesStructList = aweme.getObjectField>("images") + imagesStructList?.forEach { imagesStruct -> + val urlList = imagesStruct.getObjectField>("urlList") + val imageUrlStruct = ImageUrlStruct(urlList ?: listOf()) + imageUrlList.add(imageUrlStruct) + } + deAweme.imageUrlStructList = imageUrlList + + //背景音乐 + deAweme.musicUrlList = aweme + .getObjectField("music") + ?.getObjectField("playUrl") + ?.getObjectField>("urlList") + ?: listOf() + + + if (deAweme.isEmpty()) { + hook.showToast(activity, "未获取到视频信息!") + return + } + + showChoiceDialog(deAweme) + } + + /** + * 显示下载选择弹层 + * @param deAweme + */ + private fun showChoiceDialog(deAweme: DeAweme) { + val items = arrayOf(if (deAweme.videoUrlList.isNotEmpty()) "视频" else "图片", "背景音乐") + hook.showChoiceDialog( + activity = activity, + title = "Freedom+", + items = items, + onChoice = { _, item, _ -> + when (item) { + "视频" -> downloadVideo(activity, deAweme) + "图片" -> downloadImages(activity, deAweme) + "背景音乐" -> downloadMusic(activity, deAweme) + } + } + ) + } + + /** + * 下载视频 + * @param deAweme + */ + private fun downloadVideo(activity: Activity, deAweme: DeAweme) { + //构建视频文件名 + val pureFileName = if (deAweme.desc.isBlank()) { + "${deAweme.nickname.pureFileName}_${deAweme.shortId}_${System.currentTimeMillis() / 1000}" + } else { + "${deAweme.nickname.pureFileName}_${deAweme.shortId}_${deAweme.desc.pureFileName}" + }.plus(".mp4") + + //默认下载路径: `/外置存储器/DCIM/Freedom/video` + var parentPath = Config.getFreedomDir(activity).child("video") + + //如果需要按视频创作者单独创建文件夹: `/外置存储器/DCIM/Freedom/video/昵称(账号)` + if (isOwnerDir) parentPath = parentPath.child("${deAweme.nickname}(${deAweme.shortId})") + + //构建下载文件名 + val videoFile = File(parentPath.need(), pureFileName) + + //发送通知 + hook.showDownloadNotification( + context = activity, + notifyId = downloadNotifyId++, + title = pureFileName, + listener = { + //下载逻辑 + hook.launch { + withContext(Dispatchers.IO) { + //KLogCat.d("RealDownloadLogic#video: ${deAweme.videoUrlList}") + val outputStream = FileOutputStream(videoFile) + KHttpUtils.download(deAweme.videoUrlList.first(), outputStream) { real, total -> + it.notifyProgress(real * 100 / total) + if (real == total) { + KAlbumUtils.refresh(activity, videoFile.absolutePath) { _, _ -> + it.setFinishedText("下载成功!") + hook.showToast(activity, "下载成功!") + } + } + } + } + } + } + ) + } + + /** + * 下载图片 (标题作为文件夹) + * @param deAweme + */ + private fun downloadImages(activity: Activity, deAweme: DeAweme) { + //构建图片文件夹名 + val pureFileName = if (deAweme.desc.isBlank()) { + "${deAweme.nickname.pureFileName}_${deAweme.shortId}_${System.currentTimeMillis() / 1000}" + } else { + "${deAweme.nickname.pureFileName}_${deAweme.shortId}_${deAweme.desc.pureFileName}" + } + + //默认下载路径: `/外置存储器/DCIM/Freedom/picture` + var parentPath = Config.getFreedomDir(activity).child("picture") + + //如果需要按视频创作者单独创建文件夹: `/外置存储器/DCIM/Freedom/picture/昵称(账号)` + if (isOwnerDir) parentPath = parentPath.child("${deAweme.nickname}(${deAweme.shortId})") + + //构建下载文件名(文件夹) + val pictureDir = File(parentPath.need(), pureFileName).need() + + //发送通知 + hook.showDownloadNotification( + context = activity, + notifyId = downloadNotifyId++, + title = pureFileName, + listener = { + //下载逻辑 + hook.launch { + var downloadCount = 0 //下载计数器 + deAweme.imageUrlStructList.forEachIndexed { index, url -> + downloadCount++ //进循环即开始计数, 如果中途有失败, 直接跳过编号, 方便定位 + withContext(Dispatchers.IO) { + //KLogCat.d("RealDownloadLogic#images: ${url.imageUrlList}") + val file = File(pictureDir, "${index + 1}.jpg") + val outputStream = FileOutputStream(file) + KHttpUtils.download(url.imageUrlList.first(), outputStream) { real, total -> + it.notifyProgress(real * 100 / total, "$index/${deAweme.imageUrlStructList.size} %s%%") + if (real == total) { + KAlbumUtils.refresh(activity, file.absolutePath) + } + } + } + } + //结束提示 + if (downloadCount == deAweme.imageUrlStructList.size) { + it.setFinishedText("下载成功!") + hook.showToast(activity, "下载成功!") + } else { + it.setFinishedText("成功${downloadCount}, 失败${deAweme.imageUrlStructList.size - downloadCount}!") + hook.showToast(activity, "成功${downloadCount}, 失败${deAweme.imageUrlStructList.size - downloadCount}!") + } + } + } + ) + + } + + /** + * 下载背景音乐 + * @param deAweme + */ + private fun downloadMusic(activity: Activity, deAweme: DeAweme) { + //构建背景音乐文件名 + val pureFileName = if (deAweme.desc.isBlank()) { + "${deAweme.nickname.pureFileName}_${deAweme.shortId}_${System.currentTimeMillis() / 1000}" + } else { + "${deAweme.nickname.pureFileName}_${deAweme.shortId}_${deAweme.desc.pureFileName}" + }.plus(".mp3") + + //默认下载路径: `/外置存储器/DCIM/Freedom/music` + var parentPath = Config.getFreedomDir(activity).child("music") + + //如果需要按视频创作者单独创建文件夹: `/外置存储器/DCIM/Freedom/music/昵称(账号)` + if (isOwnerDir) parentPath = parentPath.child("${deAweme.nickname}(${deAweme.shortId})") + + //构建下载文件名 + val musicFile = File(parentPath.need(), pureFileName) + + //发送通知 + hook.showDownloadNotification( + context = activity, + notifyId = downloadNotifyId++, + title = pureFileName, + listener = { + //下载逻辑 + hook.launch { + withContext(Dispatchers.IO) { + //KLogCat.d("RealDownloadLogic#music: ${deAweme.videoUrlList}") + val outputStream = FileOutputStream(musicFile) + KHttpUtils.download(deAweme.musicUrlList.first(), outputStream) { real, total -> + it.notifyProgress(real * 100 / total) + if (real == total) { + it.setFinishedText("下载成功!") + hook.showToast(activity, "下载成功!") + } + } + } + } + } + ) + } +} \ No newline at end of file diff --git a/core/src/main/java/com/freegang/douyin/logic/SaveLogic.kt b/core/src/main/java/com/freegang/douyin/logic/SaveLogic.kt new file mode 100644 index 0000000..85876c7 --- /dev/null +++ b/core/src/main/java/com/freegang/douyin/logic/SaveLogic.kt @@ -0,0 +1,46 @@ +package com.freegang.douyin.logic + +import android.content.Context +import com.freegang.base.BaseHook +import com.freegang.config.Config +import com.freegang.xpler.utils.io.KFileUtils.child +import com.freegang.xpler.utils.io.KFileUtils.need +import com.freegang.xpler.utils.net.KHttpUtils +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import java.io.File +import java.io.FileOutputStream + +//保存(图片/表情)逻辑 +class SaveLogic( + private val hook: BaseHook, + private val context: Context, + private val urlList: List, + private val isDCIM: Boolean = false, +) { + + init { + onSaveCommentImageOrEmoji(context, urlList) + } + + // 保存评论区图片 + private fun onSaveCommentImageOrEmoji(context: Context, urlList: List) { + hook.launch { + //默认保存路径: `/外置存储器/DCIM/Freedom/emoji` + val parentPath = Config.getFreedomDir(context) + .child("emoji") + .need() + + //构建保存文件名 + val file = File(parentPath, "${System.currentTimeMillis() / 1000}.gif") + withContext(Dispatchers.IO) { + KHttpUtils.download(urlList.first(), FileOutputStream(file)) { real, total -> + if (real == total) { + hook.showToast(context, "保存成功!") + hook.vibrate(context, 100L) + } + } + } + } + } +} \ No newline at end of file diff --git a/core/src/main/java/com/freegang/douyin/model/DeAweme.kt b/core/src/main/java/com/freegang/douyin/model/DeAweme.kt new file mode 100644 index 0000000..7951ce7 --- /dev/null +++ b/core/src/main/java/com/freegang/douyin/model/DeAweme.kt @@ -0,0 +1,23 @@ +package com.freegang.douyin.model + +data class DeAweme( + var desc: String = "", + var nickname: String = "", + var shortId: String = "", + var videoUrlList: List = listOf(), + var imageUrlStructList: List = listOf(), + var musicUrlList: List = listOf(), +) { + fun isEmpty(): Boolean { + return desc.isEmpty() + && nickname.isEmpty() + && shortId.isEmpty() + && videoUrlList.isEmpty() + && imageUrlStructList.isEmpty() + && musicUrlList.isEmpty() + } +} + +data class ImageUrlStruct( + val imageUrlList: List, +) \ No newline at end of file diff --git a/core/src/main/java/com/freegang/view/KDialog.kt b/core/src/main/java/com/freegang/view/KDialog.kt new file mode 100644 index 0000000..c43b1e6 --- /dev/null +++ b/core/src/main/java/com/freegang/view/KDialog.kt @@ -0,0 +1,60 @@ +package com.freegang.view + +import android.app.Activity +import android.content.Context +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.view.Gravity +import android.view.View +import android.view.WindowManager +import android.widget.PopupWindow +import androidx.annotation.IntDef +import com.freegang.xpler.utils.log.KLogCat + + +class KDialog(private val context: Context) : PopupWindow(context) { + + init { + this.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) //取消默认背景色(设置透明) + width = WindowManager.LayoutParams.MATCH_PARENT + height = WindowManager.LayoutParams.MATCH_PARENT + isFocusable = true //允许响应焦点 + isClippingEnabled = false //扩展到状态栏 + animationStyle = android.R.style.Animation_Dialog //dialog动画 + } + + fun setView(contentView: View): KDialog { + super.setContentView(contentView) + return this + } + + override fun dismiss() { + super.dismiss() + } + + fun show() { + this.show(Gravity.CENTER, 0, 0) + } + + fun show(@PopupWindowGravity gravity: Int, offsetX: Int, offsetY: Int) { + //父布局, 默认为Android根布局 + try { + val parentView: View = (context as Activity).window.decorView.findViewById(android.R.id.content) + show(parentView, gravity, offsetX, offsetY) + } catch (e: Exception) { + KLogCat.e("`${this::class.java.name}#show()`错误:\n${e.stackTraceToString()}") + } + } + + fun show(parentView: View, @PopupWindowGravity gravity: Int, x: Int, y: Int) { + try { + showAtLocation(parentView, gravity, x, y) + } catch (e: Exception) { + KLogCat.e("`${this::class.java.name}#show()`错误:\n${e.stackTraceToString()}") + } + } + + //定位注解 + @IntDef(*[Gravity.TOP, Gravity.BOTTOM, Gravity.START, Gravity.END, Gravity.CENTER_VERTICAL, Gravity.FILL_VERTICAL, Gravity.CENTER_HORIZONTAL, Gravity.FILL_HORIZONTAL, Gravity.CENTER, Gravity.FILL, Gravity.CLIP_VERTICAL, Gravity.CLIP_HORIZONTAL]) + annotation class PopupWindowGravity +} \ No newline at end of file diff --git a/core/src/main/java/com/freegang/view/adapter/DialogChoiceAdapter.kt b/core/src/main/java/com/freegang/view/adapter/DialogChoiceAdapter.kt new file mode 100644 index 0000000..efcaf54 --- /dev/null +++ b/core/src/main/java/com/freegang/view/adapter/DialogChoiceAdapter.kt @@ -0,0 +1,46 @@ +package com.freegang.view.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.BaseAdapter +import android.widget.TextView +import com.freegang.xpler.R +import com.freegang.xpler.utils.other.KResourceUtils + +class DialogChoiceAdapter( + private val context: Context, + private val items: Array, +) : BaseAdapter() { + + override fun getCount(): Int = items.size + + override fun getItem(position: Int): T = items[position] + + override fun getItemId(position: Int): Long = position.toLong() + + override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View { + val item = getItem(position) + + val view: View + val viewHolder: ViewHolder + + if (convertView == null) { + viewHolder = ViewHolder() + view = KResourceUtils.inflateView(context, R.layout.dialog_choice_item) + viewHolder.itemTextView = view.findViewById(R.id.choiceItemText) + view.tag = viewHolder + } else { + view = convertView + viewHolder = view.tag as ViewHolder + } + + viewHolder.itemTextView?.text = item + return view + } + + class ViewHolder { + var itemTextView: TextView? = null + } +} \ No newline at end of file diff --git a/core/src/main/java/com/freegang/xpler/HookInit.kt b/core/src/main/java/com/freegang/xpler/HookInit.kt new file mode 100644 index 0000000..6fa248d --- /dev/null +++ b/core/src/main/java/com/freegang/xpler/HookInit.kt @@ -0,0 +1,90 @@ +package com.freegang.xpler + +import android.app.Application +import android.content.Context +import android.content.res.XModuleResources +import android.os.Bundle +import com.freegang.xpler.loader.HybridClassLoader +import com.freegang.xpler.xp.KtXposedHelpers +import com.freegang.xpler.xp.hookClass +import com.freegang.xpler.xp.initModule +import de.robv.android.xposed.IXposedHookLoadPackage +import de.robv.android.xposed.IXposedHookZygoteInit +import de.robv.android.xposed.XposedBridge +import de.robv.android.xposed.callbacks.XC_LoadPackage + +// Hook init entrance +class HookInit : IXposedHookLoadPackage, IXposedHookZygoteInit { + private val hookMain: HookMain = HookMain() + + private fun injectClassLoader(lpparam: XC_LoadPackage.LoadPackageParam, classLoader: ClassLoader) { + val fParent = ClassLoader::class.java.declaredFields.first { it.name == "parent" } + fParent.isAccessible = true + val mine = HookInit::class.java.classLoader + val curr = fParent.get(mine) as (ClassLoader?) ?: XposedBridge::class.java.classLoader + if (!curr::class.java.name.equals(HybridClassLoader::class.java.name)) { + lpparam.classLoader = HybridClassLoader(classLoader, curr) + fParent.set(mine, lpparam.classLoader) + } + } + + override fun initZygote(sparam: IXposedHookZygoteInit.StartupParam) { + val modulePath = sparam.modulePath + val moduleRes = XModuleResources.createInstance(sparam.modulePath, null) + KtXposedHelpers.initModule(modulePath, moduleRes) + } + + override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) { + if (!HookPackages.packages.contains(lpparam.packageName)) return + + val kXposedBridge: Class<*> = XposedBridge::class.java + if ("de.robv.android.xposed.XposedBridge" != kXposedBridge.name) { + val className = kXposedBridge.name + val pkgName = className.substring(0, className.lastIndexOf('.')) + HybridClassLoader.setObfuscatedXposedApiPackage(pkgName) + } + + KtXposedHelpers + .hookClass(Application::class.java) + .method("attach", Context::class.java) { + onAfter { + val context = it.args[0] as Context + val targetLoader = context.classLoader + injectClassLoader(lpparam, targetLoader) + + // starter hook main + if (lpparam.packageName == HookPackages.appPackageName) { + moduleInit(lpparam) + } else { + pluginInit(lpparam, it.thisObject as Application) + } + } + } + } + + + /// + // module hint hook!! + private fun moduleInit(lpparam: XC_LoadPackage.LoadPackageParam) { + val moduleMainActivity = HookPackages.appPackageName.plus(".activity.HomeActivity") + lpparam.hookClass(moduleMainActivity) + .method("onCreate", Bundle::class.java) { + onAfter { + // classloader不一致导致无法进行类型转换 + //val mainActivity = it.thisObject as MainActivity + + // 反射调用, 模块加载成功 + val thisObject = it.thisObject + val hookHintMethod = thisObject::class.java.getDeclaredMethod("hookHint") + hookHintMethod.isAccessible = true + hookHintMethod.invoke(thisObject) + } + } + } + + // starter main hook!! + private fun pluginInit(lpparam: XC_LoadPackage.LoadPackageParam, application: Application) { + hookMain.handleLoadPackage(lpparam) + hookMain.handleLoadPackage(lpparam, application) + } +} \ No newline at end of file diff --git a/core/src/main/java/com/freegang/xpler/HookMain.kt b/core/src/main/java/com/freegang/xpler/HookMain.kt new file mode 100644 index 0000000..dc573c8 --- /dev/null +++ b/core/src/main/java/com/freegang/xpler/HookMain.kt @@ -0,0 +1,21 @@ +package com.freegang.xpler + +import android.app.Application +import com.freegang.douyin.DouYinMain +import de.robv.android.xposed.IXposedHookLoadPackage +import de.robv.android.xposed.callbacks.XC_LoadPackage + +class HookMain : IXposedHookLoadPackage { + + override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) { + // do`t write the same hook logic in two methods at the same time, and do not call each other in the same way. + } + + fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam, application: Application) { + when (lpparam.packageName) { + HookPackages.douYinPackageName -> { + DouYinMain(lpparam, application) + } + } + } +} \ No newline at end of file diff --git a/core/src/main/java/com/freegang/xpler/HookPackages.kt b/core/src/main/java/com/freegang/xpler/HookPackages.kt new file mode 100644 index 0000000..d69052b --- /dev/null +++ b/core/src/main/java/com/freegang/xpler/HookPackages.kt @@ -0,0 +1,19 @@ +package com.freegang.xpler + + +/** + * 你只需要在下面增加需要被hook的app包名, + * 然后在 `HookMain` 中写你的Hook逻辑 + */ +object HookPackages { + const val appPackageName = "com.freegang.fplus" + const val corePackageName = "com.freegang.xpler" + const val douYinPackageName = "com.ss.android.ugc.aweme" + + + val packages = listOf( + appPackageName, + corePackageName, + douYinPackageName, + ) +} \ No newline at end of file diff --git a/core/src/main/java/com/freegang/xpler/loader/HybridClassLoader.java b/core/src/main/java/com/freegang/xpler/loader/HybridClassLoader.java new file mode 100644 index 0000000..90767da --- /dev/null +++ b/core/src/main/java/com/freegang/xpler/loader/HybridClassLoader.java @@ -0,0 +1,121 @@ +/* + * QAuxiliary - An Xposed module for QQ/TIM + * Copyright (C) 2019-2022 qwq233@qwq2333.top + * https://github.com/cinit/QAuxiliary + * + * This software is non-free but opensource software: you can redistribute it + * and/or modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation; either + * version 3 of the License, or any later version and our eula as published + * by QAuxiliary contributors. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * and eula along with this software. If not, see + * + * . + */ + +package com.freegang.xpler.loader; + +import android.content.Context; + +import java.net.URL; + +/** + * NOTICE: Do NOT use any androidx annotations here. + */ +public class HybridClassLoader extends ClassLoader { + + private static String sObfuscatedPackageName = null; + private static String sProbeLsposedNativeApiClassName = "Lorg/lsposed/lspd/nativebridge/NativeAPI;"; + private static final ClassLoader sBootClassLoader = Context.class.getClassLoader(); + private final ClassLoader clPreload; + private final ClassLoader clBase; + + public HybridClassLoader(ClassLoader x, ClassLoader ctx) { + clPreload = x; + clBase = ctx; + } + + /** + * 把宿主和模块共有的 package 扔这里. + * + * @param name NonNull, class name + * @return true if conflicting + */ + public static boolean isConflictingClass(String name) { + return name.startsWith("androidx.") || name.startsWith("android.support.") + || name.startsWith("kotlin.") || name.startsWith("kotlinx.") + || name.startsWith("com.tencent.mmkv.") + || name.startsWith("com.android.tools.r8.") + || name.startsWith("com.google.android.") + || name.startsWith("com.google.gson.") + || name.startsWith("com.google.common.") + || name.startsWith("com.microsoft.appcenter.") + || name.startsWith("org.intellij.lang.annotations.") + || name.startsWith("org.jetbrains.annotations."); + } + + @Override + protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + try { + return sBootClassLoader.loadClass(name); + } catch (ClassNotFoundException ignored) { + } + if (name != null && isConflictingClass(name)) { + //Nevertheless, this will not interfere with the host application, + //classes in host application SHOULD find with their own ClassLoader, eg Class.forName() + //use shipped androidx and kotlin lib. + throw new ClassNotFoundException(name); + } + // The ClassLoader for some apk-modifying frameworks are terrible, XposedBridge.class.getClassLoader() + // is the sane as Context.getClassLoader(), which mess up with 3rd lib, can cause the ART to crash. + if (clPreload != null) { + try { + return clPreload.loadClass(name); + } catch (ClassNotFoundException ignored) { + } + } + if (clBase != null) { + try { + return clBase.loadClass(name); + } catch (ClassNotFoundException ignored) { + } + } + throw new ClassNotFoundException(name); + } + + @Override + public URL getResource(String name) { + URL ret = clPreload.getResource(name); + if (ret != null) { + return ret; + } + return clBase.getResource(name); + } + + public static void setObfuscatedXposedApiPackage(String packageName) { + sObfuscatedPackageName = packageName; + } + + public static String getObfuscatedXposedApiPackage() { + return sObfuscatedPackageName; + } + + public static String getObfuscatedLsposedNativeApiClassName() { + return sProbeLsposedNativeApiClassName.replace('.', '/').substring(1, sObfuscatedPackageName.length() - 1); + } + + public static String getXposedBridgeClassName() { + if (sObfuscatedPackageName == null) { + return "de.robv.android.xposed.XposedBridge"; + } else { + return sObfuscatedPackageName + ".XposedBridge"; + } + } +} \ No newline at end of file diff --git a/core/src/main/java/com/freegang/xpler/utils/app/KAlbumUtils.kt b/core/src/main/java/com/freegang/xpler/utils/app/KAlbumUtils.kt new file mode 100644 index 0000000..6297f28 --- /dev/null +++ b/core/src/main/java/com/freegang/xpler/utils/app/KAlbumUtils.kt @@ -0,0 +1,36 @@ +package com.freegang.xpler.utils.app + +import android.content.ContentValues +import android.content.Context +import android.content.Intent +import android.media.MediaScannerConnection +import android.net.Uri +import android.os.Build +import android.provider.MediaStore + +/// 相册工具类 +object KAlbumUtils { + + /** + * 刷新相册, 通知相册更新部分照片文件 + * @param context context + * @param path 保存路径, 可能会限制: Environment.DIRECTORY_DCIM、Environment.DIRECTORY_PICTURES 等文件夹(作为父文件夹) + * @param callback 回调方法, 刷新成功才进行回调 + */ + fun refresh(context: Context, path: String, callback: ((path: String, uri: Uri) -> Unit)? = null) { + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q) { + MediaScannerConnection.scanFile(context, arrayOf(path), null) { resultPath, uri -> + callback?.invoke(resultPath, uri) + } + } else { + val values = ContentValues() + values.put(MediaStore.Images.Media.DATA, path) + values.put(MediaStore.Images.Media.MIME_TYPE, "*/*") + values.put(MediaStore.Images.ImageColumns.DATE_TAKEN, System.currentTimeMillis()) + val uri = context.contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values) + context.sendBroadcast(Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri)) + + if (uri != null) callback?.invoke(path, uri) //插入成功才回调方法 + } + } +} \ No newline at end of file diff --git a/core/src/main/java/com/freegang/xpler/utils/app/KAppCrashUtils.kt b/core/src/main/java/com/freegang/xpler/utils/app/KAppCrashUtils.kt new file mode 100644 index 0000000..665f460 --- /dev/null +++ b/core/src/main/java/com/freegang/xpler/utils/app/KAppCrashUtils.kt @@ -0,0 +1,64 @@ +package com.freegang.xpler.utils.app + +import android.app.Application +import android.os.Looper +import android.widget.Toast +import com.freegang.xpler.utils.log.KLogCat +import kotlin.system.exitProcess + +/// 全局未捕获异常工具 +/// 请在 Application.onCrate() 中初始化 +class KAppCrashUtils : Thread.UncaughtExceptionHandler { + private var mDefaultHandler: Thread.UncaughtExceptionHandler? = null + + private var mApp: Application? = null + private var mMessage: String = "" + + companion object { + val instance: KAppCrashUtils = KAppCrashUtils() + } + + @JvmOverloads + fun init(app: Application, message: String = "程序崩溃!") { + this.mApp = app + this.mMessage = message + //获取系统默认的UncaughtException处理器 + mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler(); + //设置该MyCrashHandler为程序的默认处理器 + Thread.setDefaultUncaughtExceptionHandler(this); + } + + /// 全局异常处理方法 + override fun uncaughtException(t: Thread, e: Throwable) { + if (!handlerException(e) && mDefaultHandler != null) { + mDefaultHandler?.uncaughtException(t, e); + } else { + try { + //Sleep 来让线程停止一会是为了显示Toast信息给用户,然后Kill程序 + Thread.sleep(2000) + } catch (e: Exception) { + e.printStackTrace() + } + exitApp() + } + } + + /// 异常处理 + private fun handlerException(e: Throwable): Boolean { + Thread { + Looper.prepare() + Toast.makeText(mApp, mMessage, Toast.LENGTH_SHORT).show() + Looper.loop() + }.start() + + //记录日志 + KLogCat.e("$mMessage\n${e.stackTraceToString()}") + return true + } + + /// 结束应用 + private fun exitApp() { + android.os.Process.killProcess(android.os.Process.myPid()); + exitProcess(1); + } +} \ No newline at end of file diff --git a/core/src/main/java/com/freegang/xpler/utils/app/KAppVersionUtils.kt b/core/src/main/java/com/freegang/xpler/utils/app/KAppVersionUtils.kt new file mode 100644 index 0000000..e1b8470 --- /dev/null +++ b/core/src/main/java/com/freegang/xpler/utils/app/KAppVersionUtils.kt @@ -0,0 +1,106 @@ +package com.freegang.xpler.utils.app + +import android.app.Application +import android.content.pm.PackageInfo +import android.content.pm.PackageManager +import android.os.Build +import java.io.File + +object KAppVersionUtils { + + /** + * 返回某个App的版本名 + * + * @param application application + * @param packageName 包名(需要App已安装) + * @return String + */ + fun getVersionName(application: Application, packageName: String = application.packageName): String { + return getPackageInfo(application, packageName).versionName + } + + /** + * 返回某个App的版本号 + * + * @param application application + * @param packageName 包名(需要App已安装) + * @return Long + */ + fun getVersionCode(application: Application, packageName: String = application.packageName): Long { + val packageInfo = getPackageInfo(application, packageName) + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + packageInfo.longVersionCode + } else { + packageInfo.versionCode.toLong() + } + } + + /** + * 返回某个App的基本信息 + * + * @param application application + * @param packageName 包名(需要App已安装) + * @return PackageInfo + */ + fun getPackageInfo(application: Application, packageName: String = application.packageName): PackageInfo { + return application.packageManager.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES) + } + + /** + * 返回某个Apk的版本名 + * + * @param application application + * @param apkFile apk文件路径 + * @return String + */ + fun getApkVersionName(application: Application, apkFile: File): String? { + return getApkPackageInfo(application, apkFile)?.versionName + } + + /** + * 返回某个Apk的版本号 + * + * @param application application + * @param apkFile apk文件路径 + * @return Long + */ + fun getApkVersionCode(application: Application, apkFile: File): Long? { + val packageInfo = getApkPackageInfo(application, apkFile) + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + packageInfo?.longVersionCode + } else { + packageInfo?.versionCode?.toLong() + } + } + + /** + * 返回某个Apk的基本信息 + * + * @param application application + * @param apkFile apk文件路径 + * @return PackageInfo + */ + fun getApkPackageInfo(application: Application, apkFile: File): PackageInfo? { + return application.packageManager.getPackageArchiveInfo(apkFile.absolutePath, PackageManager.GET_ACTIVITIES) + } + + /** + * 如果[versionName1]等于[versionName2],则返回一个等于 0 的值; + * 如果[versionName1]小于[versionName2],则返回一个小于 0 的值; + * 如果[versionName1]大于[versionName2],则返回一个大于 0 的值; + * + * @param versionName1 versionName1 + * @param versionName2 versionName2 + * @return Int + */ + fun compareVersionName(versionName1: String, versionName2: String): Int { + return versionName1.compareTo(versionName2, true) + } + + /// + val Application.appVersionName + get() = getVersionName(this) + + val Application.appVersionCode + get() = getVersionCode(this) +} \ No newline at end of file diff --git a/core/src/main/java/com/freegang/xpler/utils/app/KNotifiUtils.kt b/core/src/main/java/com/freegang/xpler/utils/app/KNotifiUtils.kt new file mode 100644 index 0000000..a97770e --- /dev/null +++ b/core/src/main/java/com/freegang/xpler/utils/app/KNotifiUtils.kt @@ -0,0 +1,167 @@ +package com.freegang.xpler.utils.app + +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.PendingIntent +import android.content.Context +import android.os.Build +import androidx.core.app.NotificationCompat + +object KNotifiUtils { + + /** + * 显示普通消息通知 + */ + @JvmStatic + @JvmOverloads + fun showNotification( + context: Context, + notifyId: Int, + channelId: String = "渠道ID", + channelName: String = "渠道名", + title: String, + text: String, + intent: PendingIntent? = null, + ) { + val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) { + val notificationChannel = NotificationChannel( + channelId, + channelName, + NotificationManager.IMPORTANCE_DEFAULT, //默认, 酌情修改 + ) + manager.createNotificationChannel(notificationChannel) + } + + val notify = NotificationCompat.Builder(context, channelId).apply { + setAutoCancel(true) //自动取消 + setSmallIcon(context.applicationInfo.icon) + setContentTitle(title) + setContentText(text) + if (intent != null) { + setContentIntent(intent) + } + } + + manager.notify(notifyId, notify.build()) + } + + /** + * 显示进度条通知 + */ + @JvmStatic + @JvmOverloads + fun showProgressNotification( + context: Context, + notifyId: Int, + channelId: String = "渠道ID", + channelName: String = "渠道名", + title: String = "正在下载..", + inProgressText: String = "下载中%s%%", + finishedText: String = "下载完成!", + intent: PendingIntent? = null, + listener: KNotifiUtils.ProgressNotificationListener, + ) { + val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) { + val notificationChannel = NotificationChannel( + channelId, + channelName, + NotificationManager.IMPORTANCE_DEFAULT //默认, 酌情修改 + ) + manager.createNotificationChannel(notificationChannel) + } + + //构建通知 + val notify = NotificationCompat.Builder(context, channelId).apply { + setAutoCancel(true) //自动取消 + setSmallIcon(context.applicationInfo.icon) + setContentTitle(title) + setContentText(inProgressText.format(0)) + setProgress(100, 0, false) + //notify.setProgress(0, 0, true) //不确定状态 + if (intent != null) { + setContentIntent(intent) + } + } + + //回调,由调用者设置进度 + listener.on(ProgressNotification(notifyId, inProgressText, finishedText, manager, notify)) + } + + + //kotlin + fun showProgressNotification( + context: Context, + notifyId: Int, + channelId: String = "渠道ID", + channelName: String = "渠道名", + title: String = "正在下载..", + inProgressText: String = "下载中%s%%", + finishedText: String = "下载完成!", + intent: PendingIntent? = null, + listener: (notify: KNotifiUtils.ProgressNotification) -> Unit, + ) { + KNotifiUtils.showProgressNotification( + context = context, + notifyId = notifyId, + channelId = channelId, + channelName = channelName, + title = title, + inProgressText = inProgressText, + finishedText = finishedText, + intent = intent, + listener = object : ProgressNotificationListener { + override fun on(notify: ProgressNotification) { + listener.invoke(notify) + } + }, + ) + } + + + //控制类 + class ProgressNotification( + private val notifyId: Int = 1, + private var inProgressText: String, + private var finishedText: String, + private val manager: NotificationManager, + private val notify: NotificationCompat.Builder, + ) { + /** + * 由调用者主动设置完成文本 + * @param finishedText 下载完成后展示的文本, 默认为: "下载完成!" + */ + @JvmOverloads + fun setFinishedText( + finishedText: String = "下载完成!", + ) { + this.finishedText = finishedText + notify.setProgress(100, 100, false) + notify.setContentText(this.finishedText) + manager.notify(notifyId, notify.build()) + } + + /** + * 调用者设置进度, 应该由它实时更新 + * @param step 当前进度 + * @param inProgressText 进度文本, 应该预留一个`%s`或者`%d`作为[step]的展示, 默认为: "下载中%s%%" + */ + @JvmOverloads + fun notifyProgress( + step: Int, + inProgressText: String = "下载中%s%%", + ) { + this.inProgressText = inProgressText + notify.setContentText(this.inProgressText.format(step)) + notify.setProgress(100, step, false) + manager.notify(notifyId, notify.build()) + } + } + + @FunctionalInterface + interface ProgressNotificationListener { + fun on(notify: KNotifiUtils.ProgressNotification) + } +} diff --git a/core/src/main/java/com/freegang/xpler/utils/io/KFileUtils.kt b/core/src/main/java/com/freegang/xpler/utils/io/KFileUtils.kt new file mode 100644 index 0000000..53d63db --- /dev/null +++ b/core/src/main/java/com/freegang/xpler/utils/io/KFileUtils.kt @@ -0,0 +1,78 @@ +package com.freegang.xpler.utils.io + +import java.io.File + + +object KFileUtils { + /** + * 删除所有内容, 如果文件夹不为空, 则递归遍历删除其子项, 直到将它自身删除结束 + * see at [File.deleteRecursively()] + */ + fun File.forceDelete() { + //文件直接删除 + if (this.isFile) { + this.delete() + return + } + //遍历删除文件夹 + this.listFiles()?.forEach { + if (it.isFile) it.delete() + if (it.isDirectory) it.forceDelete() + } + //删除文件夹本身 + this.delete() + } + + /** + * 从当前file的基础上, 增加一个 child, 当前file如果是文件, 则返回抛出异常 + * @param name 子文件名 + */ + fun File.child(name: String): File { + if (this.isFile) { + throw Exception("`File.child(\"$name\")` trying to add a child to a file.") + } + return File(this, name) + } + + /** + * 必要的路径地址, 如果该路径不存在, 则为其创建 + */ + fun File.need(isFile: Boolean = false): File { + if (isFile) { + val parent = this.parentFile!! + if (!parent.exists()) parent.mkdirs() + if (!this.exists()) this.createNewFile() + return this + } + + if (!this.exists()) this.mkdirs() + return this + } + + /** + * 将一个字符串转换为文件, 该字符串必须是一个正确的路径地址 + */ + fun String.toFile() = File(this) + + /** + * 获取纯净的文件名, 替换掉部分特殊符号 + */ + val File.pureName: String + get() = name.pureFileName + + val String.pureFileName: String + get() { + return this.replace("\\s".toRegex(), "") + .replace("<", "‹") + .replace(">", "›") + .replace("\"", "”") + .replace("\'", "’") + .replace("\\", "-") + .replace("\$", "¥") + .replace("/", "-") + .replace("|", "-") + .replace("*", "-") + .replace(":", "-") + .replace("?", "?") + } +} diff --git a/core/src/main/java/com/freegang/xpler/utils/io/KStorageUtils.kt b/core/src/main/java/com/freegang/xpler/utils/io/KStorageUtils.kt new file mode 100644 index 0000000..a672ef5 --- /dev/null +++ b/core/src/main/java/com/freegang/xpler/utils/io/KStorageUtils.kt @@ -0,0 +1,41 @@ +package com.freegang.xpler.utils.io + +import android.content.Context +import com.freegang.xpler.utils.io.KFileUtils.child +import com.freegang.xpler.utils.io.KFileUtils.toFile +import java.io.File + +object KStorageUtils { + + /** + * 需要部分权限 + * 获取外置存储器的根地址, 通常是: /storage/emulated/0/ + * @param context context + */ + fun getStoragePath(context: Context): String { + var externalFilesDir = context.getExternalFilesDir(null) ?: return "" + do { + externalFilesDir = externalFilesDir.parentFile ?: return "" + } while (externalFilesDir.absolutePath.contains("/Android")) + + return externalFilesDir.absolutePath.plus("/") + } + + val Context.storageRootPath: String + get() = getStoragePath(this) + + val Context.storageRootFile: File + get() = getStoragePath(this).toFile() + + val Context.hasOperationStorage: Boolean + get() { + try { + val test = storageRootFile.child(".hasOperationStorage") + val created = test.createNewFile() + if (created) return test.delete() + return true + } catch (e: Exception) { + return false + } + } +} \ No newline at end of file diff --git a/core/src/main/java/com/freegang/xpler/utils/json/KJSONUtils.kt b/core/src/main/java/com/freegang/xpler/utils/json/KJSONUtils.kt new file mode 100644 index 0000000..6a83be0 --- /dev/null +++ b/core/src/main/java/com/freegang/xpler/utils/json/KJSONUtils.kt @@ -0,0 +1,330 @@ +package com.freegang.xpler.utils.json + +import org.json.JSONArray +import org.json.JSONException +import org.json.JSONObject + +object KJSONUtils { + + /// Json /// + @JvmStatic + fun parse(json: String): JSONObject { + return try { + JSONObject(json) + } catch (e: JSONException) { + JSONObject() + } + } + + @JvmStatic + @JvmOverloads + fun getString(json: JSONObject, key: String, default: String = ""): String { + return try { + json.getString(key) + } catch (e: JSONException) { + default + } + } + + @JvmStatic + @JvmOverloads + fun getInt(json: JSONObject, key: String, default: Int = 0): Int { + return try { + json.getInt(key) + } catch (e: JSONException) { + default + } + } + + @JvmStatic + @JvmOverloads + fun getLong(json: JSONObject, key: String, default: Long = 0L): Long { + return try { + json.getLong(key) + } catch (e: JSONException) { + default + } + } + + @JvmStatic + @JvmOverloads + fun getDouble(json: JSONObject, key: String, default: Double = 0.0): Double { + return try { + json.getDouble(key) + } catch (e: JSONException) { + default + } + } + + @JvmStatic + @JvmOverloads + fun getBoolean(json: JSONObject, key: String, default: Boolean = false): Boolean { + return try { + json.getBoolean(key) + } catch (e: JSONException) { + default + } + } + + @JvmStatic + fun getJSONObject(json: JSONObject, key: String): JSONObject { + return try { + json.getJSONObject(key) + } catch (e: JSONException) { + JSONObject() + } + } + + @JvmStatic + fun getJSONArray(json: JSONObject, key: String): JSONArray { + return try { + json.getJSONArray(key) + } catch (e: JSONException) { + JSONArray() + } + } + + @JvmStatic + fun isNull(json: JSONObject, key: String): Boolean { + return json.isNull(key) + } + + @JvmStatic + fun hasKey(json: JSONObject, key: String): Boolean { + return json.has(key) + } + + @JvmStatic + fun isEmpty(json: JSONObject): Boolean { + return json.toString() == "{}" || json.toString() == "" + } + + /// Json Array /// + @JvmStatic + fun parseArray(json: String): JSONArray { + return try { + JSONArray(json) + } catch (e: JSONException) { + JSONArray() + } + } + + @JvmStatic + @JvmOverloads + fun getString(array: JSONArray, index: Int, default: String = ""): String { + return try { + array.getString(index) + } catch (e: JSONException) { + default + } + } + + @JvmStatic + @JvmOverloads + fun getInt(array: JSONArray, index: Int, default: Int = 0): Int { + return try { + array.getInt(index) + } catch (e: JSONException) { + default + } + } + + @JvmStatic + @JvmOverloads + fun getLong(array: JSONArray, index: Int, default: Long = 0): Long { + return try { + array.getLong(index) + } catch (e: JSONException) { + default + } + } + + @JvmStatic + @JvmOverloads + fun getDouble(array: JSONArray, index: Int, default: Double = 0.0): Double { + return try { + array.getDouble(index) + } catch (e: JSONException) { + default + } + } + + @JvmStatic + @JvmOverloads + fun getBoolean(array: JSONArray, index: Int, default: Boolean = false): Boolean { + return try { + array.getBoolean(index) + } catch (e: JSONException) { + default + } + } + + @JvmStatic + fun getJSONObject(array: JSONArray, index: Int): JSONObject { + return try { + array.getJSONObject(index) + } catch (e: JSONException) { + JSONObject() + } + } + + @JvmStatic + fun getJSONArray(array: JSONArray, index: Int): JSONArray { + return try { + array.getJSONArray(index) + } catch (e: JSONException) { + JSONArray() + } + } + + @JvmStatic + fun isNull(array: JSONArray, index: Int): Boolean { + return array.isNull(index) + } + + @JvmStatic + fun isEmpty(json: JSONArray): Boolean { + return json.toString() == "[]" || json.toString() == "" + } + + + /// Extended Json /// + fun String.parseJSON(): JSONObject { + return KJSONUtils.parse(this) + } + + fun JSONObject.getStringOrDefault(key: String, default: String = ""): String { + return KJSONUtils.getString(this, key, default) + } + + fun JSONObject.getIntOrDefault(key: String, default: Int = 0): Int { + return KJSONUtils.getInt(this, key, default) + } + + fun JSONObject.getLongOrDefault(key: String, default: Long = 0L): Long { + return KJSONUtils.getLong(this, key, default) + } + + fun JSONObject.getDoubleOrDefault(key: String, default: Double = 0.0): Double { + return KJSONUtils.getDouble(this, key, default) + } + + fun JSONObject.getBooleanOrDefault(key: String, default: Boolean = false): Boolean { + return KJSONUtils.getBoolean(this, key, default) + } + + fun JSONObject.getJSONObjectOrDefault(key: String, default: JSONObject = JSONObject()): JSONObject { + return KJSONUtils.getJSONObject(this, key) + } + + fun JSONObject.getJSONArrayOrDefault(key: String, default: JSONArray = JSONArray()): JSONArray { + return KJSONUtils.getJSONArray(this, key) + } + + val JSONObject.isEmpties: Boolean + get() = KJSONUtils.isEmpty(this) + + /// Extended Json Array /// + fun String.parseJSONArray(): JSONArray { + return KJSONUtils.parseArray(this) + } + + fun JSONArray.getStringOrDefault(index: Int, default: String = ""): String { + return KJSONUtils.getString(this, index, default) + } + + fun JSONArray.getIntOrDefault(index: Int, default: Int = 0): Int { + return KJSONUtils.getInt(this, index, default) + } + + fun JSONArray.getLongOrDefault(index: Int, default: Long = 0L): Long { + return KJSONUtils.getLong(this, index, default) + } + + fun JSONArray.getDoubleOrDefault(index: Int, default: Double = 0.0): Double { + return KJSONUtils.getDouble(this, index, default) + } + + fun JSONArray.getBooleanOrDefault(index: Int, default: Boolean = false): Boolean { + return KJSONUtils.getBoolean(this, index, default) + } + + fun JSONArray.getJSONObjectOrDefault(index: Int, default: JSONObject = JSONObject()): JSONObject { + return KJSONUtils.getJSONObject(this, index) + } + + fun JSONArray.getJSONArrayOrDefault(index: Int, default: JSONArray = JSONArray()): JSONArray { + return KJSONUtils.getJSONArray(this, index) + } + + val JSONArray.isEmpties: Boolean + get() = KJSONUtils.isEmpty(this) + + fun JSONArray.firstJsonObject(default: JSONObject = JSONObject()): JSONObject { + if (this.length() == 0 || this.isEmpties) return default + return KJSONUtils.getJSONObject(this, 0) + } + + fun JSONArray.firstStringOrDefault(default: String = ""): String { + if (this.length() == 0 || this.isEmpties) return default + return KJSONUtils.getString(this, 0, default) + } + + fun JSONArray.firstIntOrDefault(default: Int = 0): Int { + if (this.length() == 0 || this.isEmpties) return default + return KJSONUtils.getInt(this, 0, default) + } + + fun JSONArray.firstLongOrDefault(default: Long = 0L): Long { + if (this.length() == 0 || this.isEmpties) return default + return KJSONUtils.getLong(this, 0, default) + } + + fun JSONArray.firstDoubleOrDefault(default: Double = 0.0): Double { + if (this.length() == 0 || this.isEmpties) return default + return KJSONUtils.getDouble(this, 0, default) + } + + fun JSONArray.firstBooleanOrDefault(default: Boolean = false): Boolean { + if (this.length() == 0 || this.isEmpties) return default + return KJSONUtils.getBoolean(this, 0, default) + } + + fun JSONArray.lastJsonObject(default: JSONObject = JSONObject()): JSONObject { + if (this.length() == 0 || this.isEmpties) return default + return KJSONUtils.getJSONObject(this, this.length() - 1) + } + + fun JSONArray.lastStringOrDefault(default: String = ""): String { + if (this.length() == 0 || this.isEmpties) return default + return KJSONUtils.getString(this, this.length() - 1, default) + } + + fun JSONArray.lastIntOrDefault(default: Int = 0): Int { + if (this.length() == 0 || this.isEmpties) return default + return KJSONUtils.getInt(this, this.length() - 1, default) + } + + fun JSONArray.lastLongOrDefault(default: Long = 0L): Long { + if (this.length() == 0 || this.isEmpties) return default + return KJSONUtils.getLong(this, this.length() - 1, default) + } + + fun JSONArray.lastDoubleOrDefault(default: Double = 0.0): Double { + if (this.length() == 0 || this.isEmpties) return default + return KJSONUtils.getDouble(this, this.length() - 1, default) + } + + fun JSONArray.lastBooleanOrDefault(default: Boolean = false): Boolean { + if (this.length() == 0 || this.isEmpties) return default + return KJSONUtils.getBoolean(this, this.length() - 1, default) + } + + fun JSONArray.toJSONObjectArray(): Array { + if (this.length() == 0 || this.isEmpties) return emptyArray() + + return Array(this.length()) { + this.getJSONObject(it) + } + } +} diff --git a/core/src/main/java/com/freegang/xpler/utils/log/KLogCat.kt b/core/src/main/java/com/freegang/xpler/utils/log/KLogCat.kt new file mode 100644 index 0000000..6fb4253 --- /dev/null +++ b/core/src/main/java/com/freegang/xpler/utils/log/KLogCat.kt @@ -0,0 +1,294 @@ +package com.freegang.xpler.utils.log + +import android.app.Application +import android.util.Log +import com.freegang.xpler.utils.io.KFileUtils.forceDelete +import com.freegang.xpler.utils.other.forCalc +import java.io.File +import java.io.FileReader +import java.io.FileWriter +import java.text.SimpleDateFormat +import java.util.* + +/// 改由Kotlin实现 +class KLogCat { + private var application: Application? = null + private var localPath: File? = null + + private var tag: String = "GLogCat" + private var maxBorderSize = 64 + private var showTitle = false + private var showDivider = false + private var saveToStorage = false + private var silence = false + + // border + private var topBorderStart: Char = '╭' + private var topBorderEnd: Char = '╮' + private var bottomBorderStart: Char = '╰' + private var bottomBorderEnd: Char = '╯' + + private var borderBar: Char = '│' + private var borderStart: Char = '├' + private var borderEnd: Char = '┤' + private var borderSolid: Char = '─' + private var borderDotted: Char = '┄' + + // symbol char + private var aggravateChar: Char = '•' + private var filledCircularChar: Char = '●' + private var outlineCircularChar: Char = '○' + + /** + * print logcat + */ + private fun println(priority: Int, tag: String, vararg msg: String) { + /// silence mode + if (silence) return + + //max length string + val maxReduce = msg.reduce { acc, s -> if (acc.length > s.length) acc else s } + + // border builder + val border = if (maxReduce.length >= maxBorderSize) { + maxBorderSize.forCalc(0, "") { "$it$borderDotted" } + } else { + maxReduce.map { borderSolid }.joinToString("") + } + val divider = if (maxReduce.length >= 64) { + maxBorderSize.forCalc(0, "") { "$it$borderDotted" } + } else { + maxReduce.map { borderDotted }.joinToString("") + } + + + //final border + val topBorder = "$topBorderStart$border" + val contentLeftBorder = "$borderStart$borderSolid" + val bottomBorder = "$bottomBorderStart$border" + + /// print Log + //top border + Log.println(priority, tag, topBorder) + //title + if (showTitle) { + Log.println(priority, tag, "$borderBar $tag $borderSolid Level[${getLevelString(priority)}]") + Log.println(priority, tag, "$borderStart$border") + } + //content + msg.forEach { + writeStorage(priority, tag, it) + Log.println(priority, tag, "$contentLeftBorder$it") + //middle border + if (msg[msg.lastIndex] != it && showDivider) { + Log.println(priority, tag, "$contentLeftBorder$divider") + } + } + //bottom border + Log.println(priority, tag, bottomBorder) + } + + /** + * print logcat to file (write) + */ + private fun writeStorage(priority: Int, tag: String, msg: String) { + try { + if (!saveToStorage) return + val logFile = buildStorageFile(Calendar.getInstance().time) ?: return + if (!logFile.exists()) logFile.createNewFile() + + FileWriter(logFile, true).use { + it.append("[") + it.append("tag=${tag}, ") + it.append("level=${getLevelString(priority)}, ") + it.append("time=") + it.append(dateTimeFormat.format(Calendar.getInstance().time)) + it.append("]: ") + it.append(msg) + it.append("\n") + } + } catch (e: Exception) { + Log.e(tag, "GLogCat Error: ${e.message}") + } + } + + /** + * read the logcat in the file + * if the log file exists + * + * @param date need date + */ + private fun _readStorage(date: Date): String { + val logFile = buildStorageFile(date) ?: return "read fail, application is null." + if (!logFile.exists()) return "read fail, file `${logFile.name}` non-existent." + + val reader = FileReader(logFile) + return reader.readText() + } + + /** + * clear local logcat files + */ + private fun _clearStorage() { + getStoragePath()?.forceDelete() + } + + /** + * logcat local dir + */ + private fun getStoragePath(): File? { + return localPath ?: application?.getExternalFilesDir("logs") + } + + /** + * + * build log file: appName_version_data.log + * for example: QQ_7.9.9_2022-12-12.log + */ + private fun buildStorageFile(date: Date): File? { + val application = application ?: return null + val appName = application.resources.getString(application.applicationInfo.labelRes) + val versionName = application.packageManager.getPackageInfo(application.packageName, 0).versionName ?: "" + return File(getStoragePath(), "${appName}_${versionName}_".plus(dateFormat.format(date)).plus(".log")) + } + + /** + * get logcat Level. + * for example: d == Debug, i == Info + */ + private fun getLevelString(priority: Int): String { + return when (priority) { + Log.VERBOSE -> "Verbose" + Log.DEBUG -> "Debug" + Log.INFO -> "Info" + Log.WARN -> "Warn" + Log.ERROR -> "Error" + Log.ASSERT -> "Assert" + 8 -> "CRASH" + else -> "Unknown" + } + } + + /** + * static + */ + companion object { + private val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.CHINA) + private val dateTimeFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SS", Locale.CHINA) + private val instance = KLogCat() + + /** + * init + */ + @JvmStatic + @JvmOverloads + fun init(application: Application, path: File? = null) { + instance.application = application + instance.localPath = path + } + + /** + * logcat tag + */ + @JvmStatic + fun setTag(tag: String) { + instance.tag = tag + } + + /** + * max border size + */ + @JvmStatic + fun setMaxBorderSize(size: Int) { + instance.maxBorderSize = size + } + + /** + * open logcat title + * will print 'tag' and 'level` + */ + @JvmStatic + fun showTitle() { + instance.showTitle = true + } + + /** + * open logcat middle border + * it may be called `divider line` + */ + @JvmStatic + fun showDivider() { + instance.showDivider = true + } + + /** + * open logcat sava to storage + * + * will be saved in an application private directory + * + * @param + */ + @JvmStatic + fun openStorage() { + instance.saveToStorage = true + } + + @JvmStatic + fun clearStorage() { + instance._clearStorage() + } + + @JvmStatic + fun readStorage(date: Date): String { + return instance._readStorage(date) + } + + /** + * silent mode, no logcat output + */ + @JvmStatic + fun silence() { + instance.silence = true + } + + /** + * VERBOSE = 2 + */ + @JvmStatic + fun v(vararg msg: String) { + instance.println(Log.VERBOSE, instance.tag, *msg) + } + + /** + * DEBUG = 3 + */ + @JvmStatic + fun d(vararg msg: String) { + instance.println(Log.DEBUG, instance.tag, *msg) + } + + /** + * INFO = 4 + */ + @JvmStatic + fun i(vararg msg: String) { + instance.println(Log.INFO, instance.tag, *msg) + } + + /** + * WARN = 5 + */ + @JvmStatic + fun w(vararg msg: String) { + instance.println(Log.WARN, instance.tag, *msg) + } + + /** + * ERROR = 6 + */ + @JvmStatic + fun e(vararg msg: String) { + instance.println(Log.ERROR, instance.tag, *msg) + } + } +} \ No newline at end of file diff --git a/core/src/main/java/com/freegang/xpler/utils/net/KHttpUtils.kt b/core/src/main/java/com/freegang/xpler/utils/net/KHttpUtils.kt new file mode 100644 index 0000000..a8139b5 --- /dev/null +++ b/core/src/main/java/com/freegang/xpler/utils/net/KHttpUtils.kt @@ -0,0 +1,132 @@ +package com.freegang.xpler.utils.net + +import com.freegang.xpler.utils.log.KLogCat +import java.io.IOException +import java.io.InputStreamReader +import java.io.OutputStream +import java.net.HttpURLConnection +import java.net.URL +import java.nio.charset.StandardCharsets + +object KHttpUtils { + private const val DEFAULT_UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36" + + /** + * GET请求 + * + * @param sourceUrl 目标URL地址 + * @param params 参数 + * @return 文本内容 + */ + @JvmStatic + fun get(sourceUrl: String, params: String = ""): String { + val sourceUrl = if (params.isBlank()) { + sourceUrl + } else { + if (sourceUrl.contains("?")) "$sourceUrl&$params" else "$sourceUrl?$params" + } + var body = "" + try { + val url = URL(sourceUrl) + val connect: HttpURLConnection = url.openConnection() as HttpURLConnection + connect.requestMethod = "GET" + connect.setRequestProperty("User-Agent", DEFAULT_UA) + connect.setRequestProperty("Accept", "*/*") + val inputStream = if (connect.responseCode == HttpURLConnection.HTTP_OK + || connect.responseCode == HttpURLConnection.HTTP_CREATED + ) { + InputStreamReader(connect.inputStream, StandardCharsets.UTF_8) + } else { + InputStreamReader(connect.errorStream, StandardCharsets.UTF_8) + } + body = inputStream.readText() + inputStream.close() + connect.disconnect() + } catch (e: Exception) { + e.printStackTrace() + KLogCat.e("发生异常:\n${e.stackTraceToString()}") + } + return body + } + + /** + * 获取重定向地址 + * + * @param sourceUrl 目标URL地址 + * @return 重定向后的地址 + */ + @JvmStatic + fun getRedirectsUrl(sourceUrl: String): String { + var redirectUrl = "" + try { + val url = URL(sourceUrl) + val connect: HttpURLConnection = url.openConnection() as HttpURLConnection + connect.connect() + connect.requestMethod = "GET" + connect.instanceFollowRedirects = false + redirectUrl = if (connect.responseCode == 302) { + connect.getHeaderField("Location") + } else { + connect.url.toString() + } + connect.disconnect() + } catch (e: Exception) { + e.printStackTrace() + KLogCat.e("发生异常:\n${e.stackTraceToString()}") + } + return redirectUrl + } + + /** + * 下载文件 + * @param sourceUrl 目标URL地址 + * @param output 输出流 + * @param listener 下载监听器 + */ + @JvmStatic + fun download(sourceUrl: String, output: OutputStream, listener: DownloadListener) { + var connect: HttpURLConnection? = null + try { + connect = URL(sourceUrl).openConnection() as HttpURLConnection + val input = connect.inputStream + val total = connect.contentLength + input.use { + var realCount = 0 + val buffer = ByteArray(4096) + while (true) { + val count = input.read(buffer) + if (count < 0) break + output.write(buffer, 0, count) + realCount += count + listener.downloading(realCount, total) + } + } + } catch (e: IOException) { + e.printStackTrace() + KLogCat.e("发生异常:\n${e.stackTraceToString()}") + } finally { + try { + output.flush() + output.close() + connect?.disconnect() + } catch (e: IOException) { + e.printStackTrace() + KLogCat.e("发生异常:\n${e.stackTraceToString()}") + } + } + } + + // kotlin + fun download(sourceUrl: String, output: OutputStream, listener: (real: Int, total: Int) -> Unit) { + download(sourceUrl, output, object : DownloadListener { + override fun downloading(real: Int, total: Int) { + listener.invoke(real, total) + } + }) + } + + @FunctionalInterface + interface DownloadListener { + fun downloading(real: Int, total: Int) + } +} \ No newline at end of file diff --git a/core/src/main/java/com/freegang/xpler/utils/other/KExtensionMore.kt b/core/src/main/java/com/freegang/xpler/utils/other/KExtensionMore.kt new file mode 100644 index 0000000..db411cd --- /dev/null +++ b/core/src/main/java/com/freegang/xpler/utils/other/KExtensionMore.kt @@ -0,0 +1,32 @@ +package com.freegang.xpler.utils.other + + +/// 简化循环 +fun Int.forTo(to: Int, both: Boolean = false, block: (index: Int) -> Unit) { + //10 -> 0 + if (to < this) { + val finalTo = if (both) to else to + 1 + for (i in this downTo finalTo) block.invoke(i) + } else { // 0 -> 10 + val finalTo = if (both) to else to - 1 + for (i in this..finalTo) block.invoke(i) + } +} + +/// 简化计算 +fun Int.forCalc(from: Int, initValue: T, both: Boolean = false, block: (previous: T) -> T): T { + var result: T = initValue + + if (both) { + for (i in from..this) { + result = block.invoke(result) + } + return result + } + + for (i in from until this) { + result = block.invoke(result) + } + return result +} + diff --git a/core/src/main/java/com/freegang/xpler/utils/other/KResourceUtils.kt b/core/src/main/java/com/freegang/xpler/utils/other/KResourceUtils.kt new file mode 100644 index 0000000..8c7473d --- /dev/null +++ b/core/src/main/java/com/freegang/xpler/utils/other/KResourceUtils.kt @@ -0,0 +1,31 @@ +package com.freegang.xpler.utils.other + +import android.content.Context +import android.graphics.drawable.Drawable +import android.view.LayoutInflater +import android.view.View +import androidx.annotation.DimenRes +import androidx.annotation.DrawableRes +import androidx.annotation.LayoutRes +import androidx.annotation.StringRes +import androidx.core.content.res.ResourcesCompat +import com.freegang.xpler.xp.KtXposedHelpers +import com.freegang.xpler.xp.getModuleRes + +object KResourceUtils { + fun getDrawable(@DrawableRes id: Int): Drawable? { + return ResourcesCompat.getDrawable(KtXposedHelpers.getModuleRes(), id, null) + } + + fun inflateView(context: Context, @LayoutRes id: Int): T { + return LayoutInflater.from(context).inflate(KtXposedHelpers.getModuleRes().getLayout(id), null, false) as T + } + + fun getDimension(@DimenRes id: Int): Float { + return KtXposedHelpers.getModuleRes().getDimension(id) + } + + fun getString(@StringRes id: Int): String { + return KtXposedHelpers.getModuleRes().getString(id) + } +} \ No newline at end of file diff --git a/core/src/main/java/com/freegang/xpler/utils/view/GViewUtils.java b/core/src/main/java/com/freegang/xpler/utils/view/GViewUtils.java new file mode 100644 index 0000000..d772a96 --- /dev/null +++ b/core/src/main/java/com/freegang/xpler/utils/view/GViewUtils.java @@ -0,0 +1,656 @@ +package com.freegang.xpler.utils.view; + +import static android.view.View.GONE; +import static android.view.View.INVISIBLE; +import static android.view.View.VISIBLE; + +import android.content.Context; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.IdRes; +import androidx.annotation.IntDef; +import androidx.annotation.NonNull; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + +/// View 工具类 +@Deprecated(since = "KViewUtils完善中, 该类即将作废!") +public class GViewUtils { + + @IntDef({VISIBLE, INVISIBLE, GONE}) + @Retention(RetentionPolicy.SOURCE) + public @interface Visibility { + /// + } + + private GViewUtils() { + /// + } + + public static void setVisibleAll(ViewGroup viewGroup) { + setVisibilityAll(viewGroup, VISIBLE); + } + + public static void setGoneAll(ViewGroup viewGroup) { + setVisibilityAll(viewGroup, GONE); + } + + public static void setInvisibleAll(ViewGroup viewGroup) { + setVisibilityAll(viewGroup, INVISIBLE); + } + + /** + * 对某个 ViewGroup 下的所有子视图进行 Visibility 设置 + * + * @param viewGroup + * @param visibility + */ + public static void setVisibilityAll(ViewGroup viewGroup, @Visibility int visibility) { + int childCount = viewGroup.getChildCount(); + if (childCount == 0) return; + // 先递归遍历设置所有子视图的 Visibility + for (int i = 0; i < childCount; i++) { + View childAt = viewGroup.getChildAt(i); + if (childAt instanceof ViewGroup) { + setVisibilityAll((ViewGroup) childAt, visibility); + } + childAt.setVisibility(visibility); + } + // 再设置当前视图的 Visibility + viewGroup.setVisibility(visibility); + } + + + /** + * 判断某个视图是否可见 + * + * @param view + * @return + */ + public static boolean isVisible(View view) { + return view.getVisibility() == VISIBLE; + } + + /** + * 判断某个视图是否隐藏 + * + * @param view + * @return + */ + public static boolean isGon(View view) { + return view.getVisibility() == GONE; + } + + /** + * 判断某个视图是否不可见, 但是仍然占位 + * + * @param view + * @return + */ + public static boolean isInvisible(View view) { + return view.getVisibility() == INVISIBLE; + } + + + /** + * 某个父视图下的所有视图是否可见 + * + * @param viewGroup + * @return + */ + public static boolean isVisibleAll(ViewGroup viewGroup) { + List resultList = new ArrayList<>(); + _traverseVisibilityAll(viewGroup, resultList); + return !resultList.contains(GONE); //不包含 GONE + } + + /** + * 某个父视图下的所有视图是否隐藏 + * + * @param viewGroup + * @return + */ + public static boolean isGonAll(ViewGroup viewGroup) { + List resultList = new ArrayList<>(); + _traverseVisibilityAll(viewGroup, resultList); + return !resultList.contains(VISIBLE); //不包含 VISIBLE + } + + /** + * 某个父视图下的所有视图是否不可见, 但仍然占位 + * + * @param viewGroup + * @return + */ + public static boolean isInvisibleAll(ViewGroup viewGroup) { + List resultList = new ArrayList<>(); + _traverseVisibilityAll(viewGroup, resultList); + return !(resultList.contains(GONE) || resultList.contains(VISIBLE)); //不包含 GONE 或 VISIBLE + } + + /** + * 递归遍历某个 ViewGroup 的 Visibility + * + * @param viewGroup + * @param resultList + */ + private static void _traverseVisibilityAll(ViewGroup viewGroup, List resultList) { + if (viewGroup.getChildCount() == 0) { + resultList.add(viewGroup.getVisibility()); + return; + } + + // 先递归获取所有子 View、ViewGroup 的 Visibility + for (int i = 0; i < viewGroup.getChildCount(); i++) { + View childAt = viewGroup.getChildAt(i); + if (childAt instanceof ViewGroup) { + _traverseVisibilityAll((ViewGroup) childAt, resultList); + } else { + resultList.add(childAt.getVisibility()); + } + } + + // 再获取当前 ViewGroup 的 Visibility + resultList.add(viewGroup.getVisibility()); + } + + + /** + * 某个视图是否可用 + * + * @param view + * @return + */ + public static boolean isEnabled(View view) { + if (view == null) return false; + return view.isEnabled(); + } + + /** + * 某个父视图下的所有视图是否可用 + * + * @param viewGroup + * @return + */ + public static boolean isEnabledAll(ViewGroup viewGroup) { + if (viewGroup.getChildCount() == 0) { + return viewGroup.isEnabled(); + } + + // 先递归获取所有子 View、ViewGroup + List resultList = new ArrayList<>(); + for (int i = 0; i < viewGroup.getChildCount(); i++) { + View childAt = viewGroup.getChildAt(i); + if (childAt instanceof ViewGroup) { + //对子视图做判断, 同理 return !resultList.contains(false); + resultList.add(isEnabledAll((ViewGroup) childAt)); + } + resultList.add(childAt.isEnabled()); + } + // 再获取当前 ViewGroup + resultList.add(viewGroup.isEnabled()); + + return !resultList.contains(false); //不包含false + } + + + /** + * 获取某个视图的IdName + * + * @param view 需要获取IdName的视图 + * @return 该视图的IdName, 若没有, 返回 `-1` + */ + public static String getIdName(View view) { + int id = view.getId(); + if (id == -1) return "-1"; + return view.getContext().getResources().getResourceEntryName(id); + } + + /** + * 获取某个id对应的idName + * + * @param context 视图上下文 + * @param resId id值, 应该是一个id资源值, 可以是 @drawable、@string、@layout 等一系列Resource + * @return 对应的idName + */ + public static String getIdName(Context context, @IdRes int resId) { + if (resId == -1) return "-1"; + return context.getResources().getResourceEntryName(resId); + } + + /** + * 遍历ViewGroup 获取所有子View, + * 该方法会遍历xml节点树, + * 将指定对象获取到线性数组中(前序遍历将会彻底打乱层级, 但不会打乱顺序) + * + * @param viewGroup 父视图 + * @return 所有子视图, 包括子视图中的 ViewGroup + */ + public static List deepViewGroup(ViewGroup viewGroup) { + List list = new ArrayList<>(); + list.add(viewGroup); + + int childCount = viewGroup.getChildCount(); + for (int i = 0; i < childCount; i++) { + View childAt = viewGroup.getChildAt(i); + if (childAt instanceof ViewGroup) { + list.addAll(deepViewGroup((ViewGroup) childAt)); + } else { + //如果符合目标, 添加 + list.add(childAt); + } + } + return list; + } + + /** + * 遍历ViewGroup, 查找指定类型的子View, + * 该方法会遍历xml节点树, + * 将指定对象获取到线性数组中(前序遍历将会彻底打乱层级, 但不会打乱顺序) + * + * @param viewGroup 父视图 + * @param targetType 目标视图, 可以是View + * @param T extend View + * @return 找到的所有 T 型 View + */ + public static List findViews(ViewGroup viewGroup, Class targetType) { + List listViews = new ArrayList<>(); + //如果符合目标, 添加 + if (targetType.isInstance(viewGroup)) { + listViews.add(targetType.cast(viewGroup)); + } + int childCount = viewGroup.getChildCount(); + for (int i = 0; i < childCount; i++) { + View childAt = viewGroup.getChildAt(i); + if (childAt instanceof ViewGroup) { + listViews.addAll(findViews((ViewGroup) childAt, targetType)); + } else { + //如果符合目标, 添加 + if (targetType.isInstance(childAt)) { + listViews.add(targetType.cast(childAt)); + } + } + } + + return listViews; + } + + /** + * 遍历ViewGroup, 查找指定类型的子View, + * 该方法会遍历xml节点树, + * 并且当 getContentDescription().toString().contains(containsDesc) 文本时, + * 将指定对象获取到线性数组中(前序遍历将会彻底打乱层级, 但不会打乱顺序) + * + * @param viewGroup 父视图 + * @param targetType 目标视图, 可以是View + * @param containsDesc 被包含的描述文本 + * @param T extend View + * @return 找到的所有 T 型 View + */ + public static List findViewsByDesc(ViewGroup viewGroup, Class targetType, String containsDesc) { + List listViews = new ArrayList<>(); + if (containsDesc == null) return listViews; + + //如果符合目标, 添加 + CharSequence parentDesc = viewGroup.getContentDescription(); + if (targetType.isInstance(viewGroup) && parentDesc != null && parentDesc.toString().contains(containsDesc)) { + listViews.add(targetType.cast(viewGroup)); + } + + int childCount = viewGroup.getChildCount(); + for (int i = 0; i < childCount; i++) { + View childAt = viewGroup.getChildAt(i); + if (childAt instanceof ViewGroup) { + listViews.addAll(findViewsByDesc((ViewGroup) childAt, targetType, containsDesc)); + } else { + //如果符合目标, 添加 + CharSequence childDesc = childAt.getContentDescription(); + if (targetType.isInstance(childAt) && childDesc != null && childDesc.toString().contains(containsDesc)) { + listViews.add(targetType.cast(childAt)); + } + } + } + return listViews; + } + + /** + * 遍历ViewGroup, 查找指定类型的子View, + * 该方法会遍历xml节点树, + * 并且当 IDName 匹配时(值得注意的是: IDName是可以相同的[当include时或视图动态添加时, IDName相同的情况多了去了], 不可相同的是IDHex值) + * 将指定对象获取到线性数组中(前序遍历将会彻底打乱层级, 但不会打乱顺序) + * (更简明来说: ListView->ItemView, RecyclerView->ItemView等等.. IDName也是相同的) + * + * @param viewGroup 父视图 + * @param targetType 目标视图, 可以是View + * @param idName 目标IDName + * @param T extend View + * @return 找到的所有 T 型 View + */ + public static List findViewsByIdName(ViewGroup viewGroup, Class targetType, String idName) { + List listViews = new ArrayList<>(); + if (idName == null || idName.trim().isEmpty() || idName.equals("-1")) return listViews; + + //如果符合目标, 添加 + String parentIdName = getIdName(viewGroup); + if (targetType.isInstance(viewGroup) && parentIdName.equals(idName)) { + listViews.add(targetType.cast(viewGroup)); + } + + int childCount = viewGroup.getChildCount(); + for (int i = 0; i < childCount; i++) { + View childAt = viewGroup.getChildAt(i); + if (childAt instanceof ViewGroup) { + listViews.addAll(findViewsByIdName((ViewGroup) childAt, targetType, idName)); + } else { + //如果符合目标, 添加 + String childIdName = getIdName(childAt); + if (targetType.isInstance(childAt) && childIdName.equals(idName)) { + listViews.add(targetType.cast(childAt)); + } + } + } + return listViews; + } + + /** + * 遍历ViewGroup, 精确查找指定类型的的某些子View, + *

+ * 例子: + * List textViews = GViewUtils.findViewExact(root, TextView.class, tv -> { + * String text = tv.getText().toString(); + * CharSequence desc = tv.getContentDescription(); + * return text.contains("张三") && desc == null; + * }); + * + * @param viewGroup 父视图 + * @param targetType 目标视图, 可以是View + * @param exact 自定义逻辑, 该参数是一个函数式接口, + * 回调所有找到的 @{targetType}, 需要你自行进行相关逻辑判断, + * 根据自定义逻辑`true` or `false` 返回匹配到的所有视图 + * @param T extend View + * @return 精确找到的所有视图 + */ + public static List findViewsExact(ViewGroup viewGroup, Class targetType, FindViewExactFunction exact) { + List listViews = new ArrayList<>(); + + // 如果符合目标, 添加 + if (targetType.isInstance(viewGroup) && exact.logic(targetType.cast(viewGroup))) { + listViews.add(targetType.cast(viewGroup)); + } + + int childCount = viewGroup.getChildCount(); + for (int i = 0; i < childCount; i++) { + View childAt = viewGroup.getChildAt(i); + if (childAt instanceof ViewGroup) { + listViews.addAll(findViewsExact((ViewGroup) childAt, targetType, exact)); + } else { + // 如果符合目标, 添加 + if (targetType.isInstance(childAt) && exact.logic(targetType.cast(childAt))) { + listViews.add(targetType.cast(childAt)); + } + } + } + return listViews; + } + + + /** + * 将某个ViewGroup 转换为 视图多叉树 + * + * @param viewGroup 目标根视图 + * @return 多叉树root节点 + */ + public static GViewNode getViewTree(ViewGroup viewGroup) { + //当前视图作为树根 + GViewNode rootNode = new GViewNode(); + rootNode.parent = null; //rootNode.parent = (ViewGroup) viewGroup.getParent(); + rootNode.view = viewGroup; + rootNode.depth = 1; + rootNode.children = new ArrayList<>(); + //构建子树 + return _buildViewNodeChild(viewGroup, rootNode, rootNode.depth + 1); + } + + private static GViewNode _buildViewNodeChild(ViewGroup viewGroup, GViewNode rootNode, int depth) { + //获取root视图下的所有子视图 + int childCount = viewGroup.getChildCount(); + if (childCount == 0) return rootNode; + for (int i = 0; i < childCount; i++) { + View childAt = viewGroup.getChildAt(i); + GViewNode childNode = new GViewNode(); + childNode.parent = viewGroup; + childNode.view = childAt; + childNode.depth = depth; + childNode.children = new ArrayList<>(); + //如果该子视图是ViewGroup, 将它作为下一个根遍历 + if (childAt instanceof ViewGroup) { + //接入主树 + rootNode.children.add(_buildViewNodeChild((ViewGroup) childAt, childNode, childNode.depth + 1)); + } else { + //接入主树 + rootNode.children.add(childNode); + } + } + return rootNode; + } + + /** + * 测试视图多叉树 + * + * @param viewNode 需要测试的树节点, 允许是子节点, 也可以是根节点. + * 该方法将某个节点下的所有子节点作层级打印 + */ + public static void testViewTree(GViewNode viewNode) { + System.out.println("┌────────────────────────────────────────────────────────"); + System.out.println("├" + viewNode.toSimpleString()); + _printlnViewTree(viewNode, "---"); + System.out.println("└────────────────────────────────────────────────────────"); + } + + private static void _printlnViewTree(GViewNode viewNode, String indent) { + if (viewNode.children.isEmpty()) return; + for (GViewNode child : viewNode.children) { + System.out.println("├" + indent + child.toSimpleString()); + if (!child.children.isEmpty()) { + _printlnViewTree(child, indent + "---"); + } + } + } + + /** + * 摧毁视图多叉树 + * + * @param viewNode 多叉树节点, 允许是子节点, 也可以是根节点. + * 该方法将某个节点下的所有子节点释放销毁 + */ + public static void destroyViewTree(GViewNode viewNode) { + if (viewNode == null) return; + viewNode.destroy(); + } + + + /// 反射获取事件监听器 View$ListenerInfo 内部类 + private static Object getListenerInfo(T view) { + try { + Field mListenerInfoField = findFieldRecursiveImpl(view.getClass(), "mListenerInfo"); + mListenerInfoField.setAccessible(true); + return mListenerInfoField.get(view); + } catch (NoSuchFieldException | IllegalAccessException e) { + e.printStackTrace(); + } + return null; + } + + /** + * 获取点击事件, 如果有, 否则返回NULL + * + * @param view 需要获取点击事件的视图 + * @return 返回该点击事件的具体实现, 需要响应点击, 请手动调用 onClick 方法. + */ + public static View.OnClickListener getOnClickListener(T view) { + Object listenerInfo = getListenerInfo(view); + if (listenerInfo == null) return null; + try { + Field mOnClickListenerField = listenerInfo.getClass().getDeclaredField("mOnClickListener"); + mOnClickListenerField.setAccessible(true); + Object mOnClickListener = mOnClickListenerField.get(listenerInfo); + if (mOnClickListener instanceof View.OnClickListener) { + return (View.OnClickListener) mOnClickListener; + } + return null; + } catch (NoSuchFieldException | IllegalAccessException e) { + e.printStackTrace(); + } + return null; + } + + /** + * 获取点击事件, 如果有, 否则返回NULL + * + * @param view 需要获取点击事件的视图 + * @return 返回该点击事件的具体实现, 需要响应长按, 请手动调用 onLongClick 方法. + */ + public static View.OnLongClickListener getOnLongClickListener(T view) { + Object listenerInfo = getListenerInfo(view); + if (listenerInfo == null) return null; + try { + Field mOnLongClickListenerField = listenerInfo.getClass().getDeclaredField("mOnLongClickListener"); + mOnLongClickListenerField.setAccessible(true); + Object mOnLongClickListener = mOnLongClickListenerField.get(listenerInfo); + if (mOnLongClickListener instanceof View.OnLongClickListener) { + return (View.OnLongClickListener) mOnLongClickListener; + } + return null; + } catch (NoSuchFieldException | IllegalAccessException e) { + e.printStackTrace(); + } + return null; + } + + /** + * 获取触摸事件, 如果有, 否则返回NULL + * + * @param view 需要获取触摸事件的视图 + * @return 返回该点击事件的具体实现, 需要响应触摸, 请手动调用 onTouch 方法. + */ + public static View.OnTouchListener getOnTouchListener(T view) { + Object listenerInfo = getListenerInfo(view); + if (listenerInfo == null) return null; + try { + Field mOnTouchListenerField = listenerInfo.getClass().getDeclaredField("mOnTouchListener"); + mOnTouchListenerField.setAccessible(true); + Object mOnTouchListener = mOnTouchListenerField.get(listenerInfo); + if (mOnTouchListener instanceof View.OnTouchListener) { + return (View.OnTouchListener) mOnTouchListener; + } + return null; + } catch (NoSuchFieldException | IllegalAccessException e) { + e.printStackTrace(); + } + return null; + } + + + /** + * 参照: XposedHelpers#findFieldRecursiveImpl + * see at: GReflectUtils.java + * + * @param clazz 目标class + * @param fieldName 字段名 + * @return 找到的字段 + * @throws NoSuchFieldException 未找到 + */ + private static Field findFieldRecursiveImpl(Class clazz, String fieldName) throws NoSuchFieldException { + try { + return clazz.getDeclaredField(fieldName); + } catch (NoSuchFieldException e) { + while (true) { + clazz = clazz.getSuperclass(); + if (clazz == null || clazz.equals(Object.class)) + break; + + try { + return clazz.getDeclaredField(fieldName); + } catch (NoSuchFieldException ignored) { + } + } + throw e; + } + } + + //精确查找视图扩展, 函数式接口 + public interface FindViewExactFunction { + boolean logic(T o); + } + + //视图节点多叉树 + public static class GViewNode { + //父视图 + public ViewGroup parent; + //当前视图 + public View view; + //当前树的深度 + public int depth; + //子节点 + public List children; + + //销毁节点树 + public void destroy() { + destroyChildren(this); + } + + private void destroyChildren(GViewNode viewNode) { + viewNode.parent = null; + viewNode.view = null; + viewNode.depth = 0; + if (!viewNode.children.isEmpty()) { + for (GViewNode child : viewNode.children) { + destroyChildren(child); + } + } + viewNode.children.clear(); + } + + public String toSimpleString() { + if (view == null) { + return "ViewNode{parent=" + parent + ", view=null, depth=" + depth + ", hashCode=" + this.hashCode() + "}"; + } + + try { + return "ViewNode{view=" + view.getClass().getName() + + ", idHex=" + (view.getId() == -1 ? "-1" : "0x" + Integer.toHexString(view.getId())) + + ", idName=" + (view.getId() == -1 ? "-1" : "@id/" + view.getContext().getResources().getResourceEntryName(view.getId())) + + ", depth=" + depth + + ", descr=" + view.getContentDescription() + + ", childrenSize=" + children.size() + + ", hashCode=" + this.hashCode() + + "}"; + } catch (Exception e) { + e.printStackTrace(); + return "ViewNode{view=" + view.getClass().getName() + + ", idHex=" + (view.getId() == -1 ? "-1" : "0x" + Integer.toHexString(view.getId())) + + ", idName=获取失败" + + ", depth=" + depth + + ", descr=" + view.getContentDescription() + + ", childrenSize=" + children.size() + + "}"; + } + } + + @NonNull + @Override + public String toString() { + return "ViewNode{" + + "parent=" + parent + + ", view=" + view + + ", depth=" + depth + + ", children=" + children + + '}'; + } + } +} \ No newline at end of file diff --git a/core/src/main/java/com/freegang/xpler/utils/view/KViewUtils.kt b/core/src/main/java/com/freegang/xpler/utils/view/KViewUtils.kt new file mode 100644 index 0000000..b87aad8 --- /dev/null +++ b/core/src/main/java/com/freegang/xpler/utils/view/KViewUtils.kt @@ -0,0 +1,232 @@ +package com.freegang.xpler.utils.view + +import android.content.Context +import android.view.View +import android.view.ViewGroup +import androidx.annotation.IdRes +import java.util.* +import kotlin.collections.ArrayDeque + +object KViewUtils { + + fun showAll(viewGroup: ViewGroup) { + setVisibilityAll(viewGroup, View.VISIBLE) + } + + fun hideAll(viewGroup: ViewGroup) { + setVisibilityAll(viewGroup, View.GONE) + } + + fun invisibleAll(viewGroup: ViewGroup) { + setVisibilityAll(viewGroup, View.INVISIBLE) + } + + private fun setVisibilityAll(viewGroup: ViewGroup, visibility: Int) { + if (viewGroup.visibility == visibility) return // 如果已经设置过了,就直接返回 + val childCount = viewGroup.childCount + if (childCount == 0) return + + // 先递归遍历设置所有子视图的 Visibility + for (i in 0 until childCount) { + val childAt = viewGroup.getChildAt(i) + if (childAt.visibility != visibility) { // 仅当可见性状态不同时才进行递归调用 + if (childAt is ViewGroup) { + setVisibilityAll(childAt, visibility) + } else { + childAt.visibility = visibility // 仅限于子视图范围内设置可见性 + } + } + } + viewGroup.visibility = visibility // 再设置当前视图的 Visibility + } + + + // + fun isVisible(view: View): Boolean { + return view.visibility == View.VISIBLE + } + + fun isGon(view: View): Boolean { + return view.visibility == View.GONE + } + + fun isInvisible(view: View): Boolean { + return view.visibility == View.INVISIBLE + } + + fun isVisibleAll(viewGroup: ViewGroup): Boolean { + val resultList = traverseVisibilityAll(viewGroup) + return !resultList.contains(View.GONE) && !resultList.contains(View.INVISIBLE) + } + + fun isGoneAll(viewGroup: ViewGroup): Boolean { + val resultList = traverseVisibilityAll(viewGroup) + return resultList.contains(View.GONE) && !resultList.contains(View.VISIBLE) + } + + fun isInvisibleAll(viewGroup: ViewGroup): Boolean { + val resultList = traverseVisibilityAll(viewGroup) + return !resultList.contains(View.GONE) && resultList.contains(View.INVISIBLE) + } + + private fun traverseVisibilityAll(viewGroup: ViewGroup): List { + val resultList = mutableListOf() + if (viewGroup.visibility == View.GONE) { + resultList.add(View.GONE) + return resultList + } + + val stack = ArrayDeque() + stack.addFirst(viewGroup) + + while (stack.isNotEmpty()) { + val current = stack.removeFirst() + for (i in 0 until current.childCount) { + val child = current.getChildAt(i) + if (child is ViewGroup) { + if (child.visibility == View.GONE) { + resultList.add(View.GONE) + } else { + stack.addFirst(child) + } + } + resultList.add(child.visibility) + } + } + return resultList + } + + + // + fun isEnabledAll(viewGroup: ViewGroup): Boolean { + val resultList = traverseEnabledAll(viewGroup) + return resultList.all { it } + } + + private fun traverseEnabledAll(viewGroup: ViewGroup): MutableList { + val resultList = mutableListOf() + if (!viewGroup.isEnabled) { + resultList.add(false) + return resultList + } + for (i in 0 until viewGroup.childCount) { + val child = viewGroup.getChildAt(i) + if (child is ViewGroup) { + resultList.addAll(traverseEnabledAll(child)) + } else { + resultList.add(child.isEnabled) + } + } + return resultList + } + + + // + fun getIdHex(view: View): String { + try { + val id = view.id + if (id == View.NO_ID) { + return "${View.NO_ID}" + } + val resources = view.resources + val resourceName = resources.getResourceName(id) + val type = resourceName.substringBefore('/') + val entryName = resourceName.substringAfter('/') + val entryId = resources.getIdentifier(entryName, type, resources.getResourcePackageName(id)) + return Integer.toHexString(entryId) + } catch (e: Exception) { + return "${View.NO_ID}" + } + } + + fun getIdName(view: View): String { + val id = view.id + return if (id == View.NO_ID) "${View.NO_ID}" else view.context.resources.getResourceEntryName(id) + } + + fun getIdName(context: Context, @IdRes resId: Int): String { + return if (resId == View.NO_ID) "${View.NO_ID}" else context.resources.getResourceEntryName(resId) + } + + fun deepViewGroup(viewGroup: ViewGroup): List { + val list = mutableListOf() + list.add(viewGroup) + + for (i in 0 until viewGroup.childCount) { + val childAt = viewGroup.getChildAt(i) + if (childAt is ViewGroup) { + list.addAll(deepViewGroup(childAt)) + } else { + // 如果符合目标,添加 + list.add(childAt) + } + } + return list + } + + + // + fun findViews(viewGroup: ViewGroup, targetType: Class): List { + val views = mutableListOf() + //如果符合目标, 添加 + if (targetType.isInstance(viewGroup)) { + targetType.cast(viewGroup)?.let { views.add(it) } + } + for (i in 0 until viewGroup.childCount) { + val child = viewGroup.getChildAt(i) + if (targetType.isInstance(child)) { + targetType.cast(child)?.let { views.add(it) } + } else if (child is ViewGroup) { + views.addAll(findViews(child, targetType)) + } + } + return views + } + + fun findViews(viewGroup: ViewGroup, targetType: Class, exact: (v: T) -> Boolean): List { + val views = mutableListOf() + //如果符合目标, 添加 + if (targetType.isInstance(viewGroup) && exact.invoke(targetType.cast(viewGroup) as T)) { + targetType.cast(viewGroup) + } + for (i in 0 until viewGroup.childCount) { + val child = viewGroup.getChildAt(i) + if (targetType.isInstance(child) && exact.invoke(targetType.cast(viewGroup) as T)) { + targetType.cast(child) + } else if (child is ViewGroup) { + views.addAll(findViews(child, targetType, exact)) + } + } + return views + } + + fun findViewsByDesc(viewGroup: ViewGroup, targetType: Class, containsDesc: Regex): List { + val listViews = mutableListOf() + + val stack = Stack() + stack.push(viewGroup) + + while (!stack.empty()) { + val currentView = stack.pop() + val childCount = currentView.childCount + for (i in 0 until childCount) { + val childView = currentView.getChildAt(i) + if (childView is ViewGroup) { + stack.push(childView) + } else if (targetType.isInstance(childView)) { + val childDesc = childView.contentDescription ?: "" + if (childDesc.contains(containsDesc)) { + listViews.add(targetType.cast(childView) as T) + } + } + } + if (targetType.isInstance(currentView)) { + val parentDesc = currentView.contentDescription ?: "" + if (parentDesc.contains(containsDesc)) { + listViews.add(targetType.cast(currentView) as T) + } + } + } + return listViews + } +} \ No newline at end of file diff --git a/core/src/main/java/com/freegang/xpler/xp/KtXposedHelpers.kt b/core/src/main/java/com/freegang/xpler/xp/KtXposedHelpers.kt new file mode 100644 index 0000000..fc56118 --- /dev/null +++ b/core/src/main/java/com/freegang/xpler/xp/KtXposedHelpers.kt @@ -0,0 +1,139 @@ +package com.freegang.xpler.xp + +import com.freegang.xpler.xp.bridge.ConstructorHook +import com.freegang.xpler.xp.bridge.ConstructorHookImpl +import com.freegang.xpler.xp.bridge.MethodHook +import com.freegang.xpler.xp.bridge.MethodHookImpl +import de.robv.android.xposed.XposedHelpers +import java.lang.reflect.Method + +/// Xposed hook 的基础封装 +class KtXposedHelpers { + private var clazz: Class<*>? = null + + companion object { + private val instances = KtXposedHelpers() + + /** + * Hook某个类 + * @param clazz 类 + */ + fun hookClass(clazz: Class<*>): KtXposedHelpers { + instances.clazz = clazz + return instances + } + + /** + * Hook某个类 + * @param className 类名 + * @param classLoader 类加载器 + */ + fun hookClass(className: String, classLoader: ClassLoader): KtXposedHelpers { + val findClass = XposedHelpers.findClass(className, classLoader) + instances.clazz = findClass + return instances + } + } + + /** + * Hook某个方法 + * @param argsTypes 参数类型列表 + * @param block hook实例 + */ + fun constructor(vararg argsTypes: Any, block: ConstructorHook.() -> Unit): KtXposedHelpers { + val constructorHookImpl = ConstructorHookImpl(clazz!!, *argsTypes) + block.invoke(constructorHookImpl) + constructorHookImpl.start() + return this + } + + /** + * Hook某个方法 + * @param methodName 方法 + * @param block hook实例 + */ + fun method(methodName: Method, block: MethodHook.() -> Unit): KtXposedHelpers { + val methodHookImpl = MethodHookImpl(methodName) + block.invoke(methodHookImpl) + methodHookImpl.start() + return this + } + + /** + * Hook某个方法 + * @param methodName 方法名 + * @param argsTypes 参数类型列表 + * @param block hook实例 + */ + fun method(methodName: String, vararg argsTypes: Any, block: MethodHook.() -> Unit): KtXposedHelpers { + val methodHookImpl = MethodHookImpl(clazz!!, methodName, *argsTypes) + block.invoke(methodHookImpl) + methodHookImpl.start() + return this + } + + /** + * Hook某个类中的所有构造方法 + * @param block hook实例 + */ + fun constructorsAll(block: MethodHook.() -> Unit): KtXposedHelpers { + val constructors = clazz!!.declaredConstructors + for (c in constructors) { + c.isAccessible = true + val constructorHookImpl = MethodHookImpl(c) + block.invoke(constructorHookImpl) + constructorHookImpl.start() + } + return this + } + + /** + * Hook某个类中的所有方法(构造方法除外) + * @param block hook实例 + */ + fun methodAll(block: MethodHook.() -> Unit): KtXposedHelpers { + val methods = clazz!!.declaredMethods + for (method in methods) { + method.isAccessible = true + val methodHookImpl = MethodHookImpl(method) + block.invoke(methodHookImpl) + methodHookImpl.start() + } + return this + } + + /** + * Hook某个类中所有同名方法, 不考虑参数类型、数量 + * @param methodName 方法名 + * @param block hook实例 + */ + fun methodAllByName(methodName: String, block: MethodHook.() -> Unit): KtXposedHelpers { + val methods = clazz!!.declaredMethods + for (method in methods) { + if (method.name == methodName) { + method.isAccessible = true + val methodHookImpl = MethodHookImpl(method) + block.invoke(methodHookImpl) + methodHookImpl.start() + } + } + return this + } + + /** + * Hook某个类中所有方法返回类型为[returnType]的方法, 不考虑方法名, 参数类型 + * @param returnType 返回类型 + */ + fun methodAllByReturnType(returnType: Class<*>, block: MethodHook.() -> Unit): KtXposedHelpers { + val methods = clazz!!.declaredMethods + for (method in methods) { + if (method.returnType == returnType) { + method.isAccessible = true + val methodHookImpl = MethodHookImpl(method) + block.invoke(methodHookImpl) + methodHookImpl.start() + } + } + return this + } +} \ No newline at end of file diff --git a/core/src/main/java/com/freegang/xpler/xp/KtXposedMore.kt b/core/src/main/java/com/freegang/xpler/xp/KtXposedMore.kt new file mode 100644 index 0000000..863e775 --- /dev/null +++ b/core/src/main/java/com/freegang/xpler/xp/KtXposedMore.kt @@ -0,0 +1,152 @@ +package com.freegang.xpler.xp + +import android.content.res.XModuleResources +import com.freegang.xpler.xp.bridge.ConstructorHook +import com.freegang.xpler.xp.bridge.MethodHook +import com.freegang.xpler.xp.bridge.MethodHookImpl +import de.robv.android.xposed.XposedHelpers +import de.robv.android.xposed.callbacks.XC_LoadPackage +import java.lang.reflect.Field +import java.lang.reflect.Method + +//Method +fun Method.hook(block: MethodHook.() -> Unit): Unit { + val methodHookImpl = MethodHookImpl(this) + block.invoke(methodHookImpl) + methodHookImpl.start() +} + +fun Method.call(obj: Any, vararg args: Any): Any? { + return XposedHelpers.callMethod(obj, this.name, *args) +} + +//Object +fun Any.findMethod(methodName: String, vararg args: Any): Method? { + return try { + XposedHelpers.findMethodExact(this::class.java, methodName, *args) + } catch (e: Exception) { + null + } +} + +fun Any.findMethod(methodName: String, argsTypes: Array>): Method? { + return try { + XposedHelpers.findMethodExact(this::class.java, methodName, *argsTypes) + } catch (e: Exception) { + null + } +} + +fun Any.findMethodsByReturnType(returnType: Class<*>): List { + val result = mutableListOf() + val methods = this::class.java.declaredMethods + for (method in methods) { + if (method.returnType == returnType) { + method.isAccessible = true + result.add(method) + } + } + return result +} + +fun Any.callMethod(methodName: String, vararg args: Any): T? { + return XposedHelpers.callMethod(this, methodName, *args) as T? +} + +fun Any.callMethod(methodName: String, argsTypes: Array>, vararg args: Any): T? { + return XposedHelpers.callMethod(this, methodName, *argsTypes, *args) as T? +} + +fun Any.callStaticMethod(methodName: String, vararg args: Any): T? { + return XposedHelpers.callStaticMethod(this::class.java, methodName, *args) as T? +} + +fun Any.callStaticMethod(methodName: String, argsTypes: Array>, vararg args: Any): T? { + return XposedHelpers.callStaticMethod(this::class.java, methodName, *argsTypes, *args) as T? +} + +fun Any.getObjectField(fieldName: String): T? { + return XposedHelpers.getObjectField(this, fieldName) as T? +} + +fun Any.setObjectField(fieldName: String, value: Any) { + XposedHelpers.setObjectField(this, fieldName, value) +} + +fun Any.getStaticObjectField(fieldName: String): T? { + return XposedHelpers.getStaticObjectField(this::class.java, fieldName) as T? +} + +fun Any.setStaticObjectField(fieldName: String, value: Any) { + XposedHelpers.setStaticObjectField(this::class.java, fieldName, value) +} + +fun Any.findFieldByType(type: Class<*>): List { + val result = mutableListOf() + val fields = this::class.java.declaredFields + for (field in fields) { + if (field.type == type) { + field.isAccessible = true + result.add(field) + } + } + return result +} + +//Class +fun Class<*>.hookConstructor( + vararg argsTypes: Any, + block: ConstructorHook.() -> Unit, +): KtXposedHelpers { + return KtXposedHelpers + .hookClass(this) + .constructor(*argsTypes) { block.invoke(this) } +} + +fun Class<*>.hookMethod( + methodName: String, + vararg argsTypes: Any, + block: MethodHook.() -> Unit, +): KtXposedHelpers { + return KtXposedHelpers + .hookClass(this) + .method(methodName, *argsTypes) { block.invoke(this) } +} + + +//ClassLoader +fun ClassLoader.hookClass(clazz: Class<*>): KtXposedHelpers { + return KtXposedHelpers.hookClass(clazz.name, this) +} + +fun ClassLoader.hookClass(className: String): KtXposedHelpers { + return KtXposedHelpers.hookClass(className, this) +} + +fun ClassLoader.findClass(className: String): Class<*> { + return XposedHelpers.findClass(className, this) +} + +//Xposed +fun XC_LoadPackage.LoadPackageParam.hookClass(clazz: Class<*>): KtXposedHelpers { + return KtXposedHelpers.hookClass(clazz.name, this.classLoader) +} + +fun XC_LoadPackage.LoadPackageParam.hookClass(className: String): KtXposedHelpers { + return KtXposedHelpers.hookClass(className, this.classLoader) +} + +fun XC_LoadPackage.LoadPackageParam.findClass(className: String): Class<*> { + return XposedHelpers.findClass(className, this.classLoader) +} + +//其他扩展 +private var mModulePath: String? = null +private var mModuleRes: XModuleResources? = null +fun KtXposedHelpers.Companion.initModule(modulePath: String, moduleRes: XModuleResources) { + mModulePath = modulePath + mModuleRes = moduleRes +} + +fun KtXposedHelpers.Companion.getModulePath() = mModulePath!! +fun KtXposedHelpers.Companion.getModuleRes() = mModuleRes!! diff --git a/core/src/main/java/com/freegang/xpler/xp/bridge/ConstructorHook.kt b/core/src/main/java/com/freegang/xpler/xp/bridge/ConstructorHook.kt new file mode 100644 index 0000000..a341243 --- /dev/null +++ b/core/src/main/java/com/freegang/xpler/xp/bridge/ConstructorHook.kt @@ -0,0 +1,9 @@ +package com.freegang.xpler.xp.bridge + +import de.robv.android.xposed.XposedHelpers + +/// 实现类 +class ConstructorHookImpl(clazz: Class<*>, vararg argsTypes: Any) : + MethodHookImpl(XposedHelpers.findConstructorExact(clazz, *argsTypes)), + ConstructorHook { +} \ No newline at end of file diff --git a/core/src/main/java/com/freegang/xpler/xp/bridge/KtHook.kt b/core/src/main/java/com/freegang/xpler/xp/bridge/KtHook.kt new file mode 100644 index 0000000..e3b8ccb --- /dev/null +++ b/core/src/main/java/com/freegang/xpler/xp/bridge/KtHook.kt @@ -0,0 +1,20 @@ +package com.freegang.xpler.xp.bridge + +import de.robv.android.xposed.XC_MethodHook + +/// 扩展接口 +interface KtHook { + +} + +//对普通方法的Hook +interface MethodHook : KtHook { + fun onBefore(block: (param: XC_MethodHook.MethodHookParam) -> Unit) + fun onAfter(block: (param: XC_MethodHook.MethodHookParam) -> Unit) + fun onReplace(block: (param: XC_MethodHook.MethodHookParam) -> Any) +} + +//对构造方法的Hook +interface ConstructorHook : MethodHook { + +} \ No newline at end of file diff --git a/core/src/main/java/com/freegang/xpler/xp/bridge/MethodHook.kt b/core/src/main/java/com/freegang/xpler/xp/bridge/MethodHook.kt new file mode 100644 index 0000000..9fc3a78 --- /dev/null +++ b/core/src/main/java/com/freegang/xpler/xp/bridge/MethodHook.kt @@ -0,0 +1,64 @@ +package com.freegang.xpler.xp.bridge + +import de.robv.android.xposed.XC_MethodHook +import de.robv.android.xposed.XC_MethodReplacement +import de.robv.android.xposed.XposedBridge +import de.robv.android.xposed.XposedHelpers +import java.lang.reflect.Member + +/// 实现类 +open class MethodHookImpl(private var method: Member) : MethodHook { + private var beforeBlock: ((param: XC_MethodHook.MethodHookParam) -> Unit)? = null + private var afterBlock: ((param: XC_MethodHook.MethodHookParam) -> Unit)? = null + private var replaceBlock: ((param: XC_MethodHook.MethodHookParam) -> Any)? = null + + constructor(clazz: Class<*>, methodName: String, vararg argsTypes: Any) : + this(XposedHelpers.findMethodExact(clazz, methodName, *argsTypes)) + + /** + * 在进入某个方法第一行进行注入 + * @param block 注入的代码块 + */ + override fun onBefore(block: (param: XC_MethodHook.MethodHookParam) -> Unit) { + this.beforeBlock = block + } + + /** + * 在结束某个方法最后一行(return之前)进行注入 + * @param block 注入的代码块 + */ + override fun onAfter(block: (param: XC_MethodHook.MethodHookParam) -> Unit) { + this.afterBlock = block + } + + /** + * 对某个方法进行替换 + * @param block 注入的代码块 + */ + override fun onReplace(block: (param: XC_MethodHook.MethodHookParam) -> Any) { + this.replaceBlock = block + } + + /** + * 开启Hook + */ + fun start() { + if (replaceBlock != null) { + XposedBridge.hookMethod(method, object : XC_MethodReplacement() { + override fun replaceHookedMethod(param: MethodHookParam): Any { + return this@MethodHookImpl.replaceBlock!!.invoke(param) + } + }) + } else { + XposedBridge.hookMethod(method, object : XC_MethodHook() { + override fun beforeHookedMethod(param: MethodHookParam) { + this@MethodHookImpl.beforeBlock?.invoke(param) + } + + override fun afterHookedMethod(param: MethodHookParam) { + this@MethodHookImpl.afterBlock?.invoke(param) + } + }) + } + } +} \ No newline at end of file diff --git a/core/src/main/res/drawable/dialog_background.xml b/core/src/main/res/drawable/dialog_background.xml new file mode 100644 index 0000000..0a167cb --- /dev/null +++ b/core/src/main/res/drawable/dialog_background.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/core/src/main/res/drawable/dialog_cancel_button_background.xml b/core/src/main/res/drawable/dialog_cancel_button_background.xml new file mode 100644 index 0000000..ec34b9c --- /dev/null +++ b/core/src/main/res/drawable/dialog_cancel_button_background.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/core/src/main/res/drawable/dialog_confirm_button_background.xml b/core/src/main/res/drawable/dialog_confirm_button_background.xml new file mode 100644 index 0000000..0f65356 --- /dev/null +++ b/core/src/main/res/drawable/dialog_confirm_button_background.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/core/src/main/res/drawable/dialog_single_button_background.xml b/core/src/main/res/drawable/dialog_single_button_background.xml new file mode 100644 index 0000000..16dce88 --- /dev/null +++ b/core/src/main/res/drawable/dialog_single_button_background.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/core/src/main/res/drawable/hook_add_button.xml b/core/src/main/res/drawable/hook_add_button.xml new file mode 100644 index 0000000..7bc1b7d --- /dev/null +++ b/core/src/main/res/drawable/hook_add_button.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/core/src/main/res/drawable/hook_save_button.xml b/core/src/main/res/drawable/hook_save_button.xml new file mode 100644 index 0000000..4fc33e7 --- /dev/null +++ b/core/src/main/res/drawable/hook_save_button.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/core/src/main/res/drawable/item_selector_background.xml b/core/src/main/res/drawable/item_selector_background.xml new file mode 100644 index 0000000..0dc4323 --- /dev/null +++ b/core/src/main/res/drawable/item_selector_background.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/core/src/main/res/layout/dialog_choice_item.xml b/core/src/main/res/layout/dialog_choice_item.xml new file mode 100644 index 0000000..857b144 --- /dev/null +++ b/core/src/main/res/layout/dialog_choice_item.xml @@ -0,0 +1,16 @@ + + + + + + \ No newline at end of file diff --git a/core/src/main/res/layout/dialog_choice_layout.xml b/core/src/main/res/layout/dialog_choice_layout.xml new file mode 100644 index 0000000..ac889e9 --- /dev/null +++ b/core/src/main/res/layout/dialog_choice_layout.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/core/src/main/res/layout/dialog_message_layout.xml b/core/src/main/res/layout/dialog_message_layout.xml new file mode 100644 index 0000000..4332940 --- /dev/null +++ b/core/src/main/res/layout/dialog_message_layout.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/core/src/main/res/layout/hook_add_save_emoji_layout.xml b/core/src/main/res/layout/hook_add_save_emoji_layout.xml new file mode 100644 index 0000000..487c389 --- /dev/null +++ b/core/src/main/res/layout/hook_add_save_emoji_layout.xml @@ -0,0 +1,29 @@ + + + + + + + + \ No newline at end of file diff --git a/core/src/main/res/layout/hook_appbar_layout.xml b/core/src/main/res/layout/hook_appbar_layout.xml new file mode 100644 index 0000000..175d7fa --- /dev/null +++ b/core/src/main/res/layout/hook_appbar_layout.xml @@ -0,0 +1,27 @@ + + + + + + + + + \ No newline at end of file diff --git a/core/src/main/res/values/dimens.xml b/core/src/main/res/values/dimens.xml new file mode 100644 index 0000000..7793c30 --- /dev/null +++ b/core/src/main/res/values/dimens.xml @@ -0,0 +1,4 @@ + + + 12dp + \ No newline at end of file diff --git a/core/src/test/java/com/freegang/xpler/ExampleUnitTest.kt b/core/src/test/java/com/freegang/xpler/ExampleUnitTest.kt new file mode 100644 index 0000000..45956b3 --- /dev/null +++ b/core/src/test/java/com/freegang/xpler/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.freegang.xpler + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..3c5031e --- /dev/null +++ b/gradle.properties @@ -0,0 +1,23 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Kotlin code style for this project: "official" or "obsolete": +kotlin.code.style=official +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..e708b1c023ec8b20f512888fe07c5bd3ff77bb8f GIT binary patch literal 59203 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

K7UpUK$>l@ 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 E2O(~oYXATM literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..c18d09a --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Sun Jan 08 22:13:25 CST 2023 +distributionBase=GRADLE_USER_HOME +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip +distributionPath=wrapper/dists +zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..4f906e0 --- /dev/null +++ b/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..568464a --- /dev/null +++ b/settings.gradle @@ -0,0 +1,18 @@ +pluginManagement { + repositories { + gradlePluginPortal() + google() + mavenCentral() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} +rootProject.name = "FreedomPlus" +include ':app' +include ':core' +include ':aweme'