├── app ├── .gitignore ├── src │ ├── main │ │ ├── assets │ │ │ └── xposed_init │ │ ├── ic_launcher-playstore.png │ │ ├── res │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_launcher_round.png │ │ │ │ └── ic_launcher_foreground.png │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_launcher_round.png │ │ │ │ └── ic_launcher_foreground.png │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_launcher_round.png │ │ │ │ └── ic_launcher_foreground.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_launcher_round.png │ │ │ │ └── ic_launcher_foreground.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_launcher_round.png │ │ │ │ └── ic_launcher_foreground.png │ │ │ ├── values │ │ │ │ ├── ic_launcher_background.xml │ │ │ │ ├── dimens.xml │ │ │ │ ├── colors.xml │ │ │ │ └── styles.xml │ │ │ ├── anim │ │ │ │ ├── custom_decelerate_interpolator.xml │ │ │ │ ├── fragment_exit.xml │ │ │ │ └── fragment_enter.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ ├── ic_launcher.xml │ │ │ │ └── ic_launcher_round.xml │ │ │ ├── drawable │ │ │ │ ├── ic_play_arrow_black.xml │ │ │ │ ├── ic_add.xml │ │ │ │ ├── ic_file_download.xml │ │ │ │ ├── ic_arrow_back.xml │ │ │ │ ├── ic_close.xml │ │ │ │ ├── ic_play_for_work.xml │ │ │ │ ├── ic_exit_to_app.xml │ │ │ │ ├── ic_more_vert.xml │ │ │ │ ├── ic_restore.xml │ │ │ │ ├── ic_search.xml │ │ │ │ ├── ic_block.xml │ │ │ │ ├── ic_delete.xml │ │ │ │ ├── ic_wallpaper.xml │ │ │ │ ├── ic_get_app.xml │ │ │ │ ├── ic_translate.xml │ │ │ │ ├── ic_warning.xml │ │ │ │ ├── ic_info.xml │ │ │ │ ├── ic_check_circle.xml │ │ │ │ ├── ic_opacity.xml │ │ │ │ ├── ic_save.xml │ │ │ │ ├── ic_center_focus_strong.xml │ │ │ │ ├── ic_move_to_inbox.xml │ │ │ │ ├── ic_help.xml │ │ │ │ ├── ic_wb_sunny.xml │ │ │ │ ├── ic_screen_rotation.xml │ │ │ │ ├── ic_cloud_off.xml │ │ │ │ ├── ic_g_translate.xml │ │ │ │ ├── ic_sentiment_dissatisfied.xml │ │ │ │ ├── ic_sentiment_satisfied.xml │ │ │ │ ├── ic_face.xml │ │ │ │ ├── ic_sogou.xml │ │ │ │ ├── ic_bug_report.xml │ │ │ │ ├── ic_color_lens.xml │ │ │ │ ├── ic_language.xml │ │ │ │ ├── ic_settings_applications.xml │ │ │ │ ├── ic_spa.xml │ │ │ │ ├── ic_baidu.xml │ │ │ │ └── ic_settings.xml │ │ │ ├── menu │ │ │ │ ├── menu_variable_fragment.xml │ │ │ │ ├── menu_activity_main.xml │ │ │ │ ├── menu_fragment_category_home_template.xml │ │ │ │ ├── menu_fragment_category_translation.xml │ │ │ │ ├── menu_activity_translation_editor.xml │ │ │ │ ├── menu_activity_selectapp.xml │ │ │ │ └── menu_fragment_category_home.xml │ │ │ ├── animator │ │ │ │ ├── appbar_layout_elevation.xml │ │ │ │ └── life_on_touch.xml │ │ │ ├── xml │ │ │ │ ├── backup_descriptor.xml │ │ │ │ ├── setting_about_preference.xml │ │ │ │ ├── setting_template_preference.xml │ │ │ │ ├── setting_home_preference.xml │ │ │ │ ├── setting_general_preference.xml │ │ │ │ ├── setting_translation_preference.xml │ │ │ │ ├── navigation_bar_preference.xml │ │ │ │ ├── status_bar_preference.xml │ │ │ │ ├── screen_preference.xml │ │ │ │ ├── variable_preference.xml │ │ │ │ └── translation_preference.xml │ │ │ ├── layout │ │ │ │ ├── activity_translation_editor.xml │ │ │ │ ├── toolbar.xml │ │ │ │ ├── activity_category.xml │ │ │ │ ├── activity_setting.xml │ │ │ │ ├── bottom_sheet_api_config.xml │ │ │ │ ├── item_translation_info.xml │ │ │ │ ├── activity_selectapp.xml │ │ │ │ ├── item_selectedapp.xml │ │ │ │ ├── fragment_main_home.xml │ │ │ │ ├── item_selectapp.xml │ │ │ │ ├── item_home_module_state.xml │ │ │ │ ├── item_home_hooking_apps.xml │ │ │ │ └── activity_main.xml │ │ │ ├── values-night │ │ │ │ ├── colors.xml │ │ │ │ └── styles.xml │ │ │ ├── navigation │ │ │ │ ├── nav_graph_main.xml │ │ │ │ └── nav_graph_setting.xml │ │ │ └── values-zh-rCN │ │ │ │ └── arrays.xml │ │ └── java │ │ │ ├── io │ │ │ └── ikws4 │ │ │ │ └── weiju │ │ │ │ ├── data │ │ │ │ ├── User.kt │ │ │ │ ├── AppInfo.kt │ │ │ │ ├── VariableModel.kt │ │ │ │ ├── TranslationInfoRepository.kt │ │ │ │ ├── AppInfoDao.kt │ │ │ │ ├── TranslationDao.kt │ │ │ │ ├── UserRepository.kt │ │ │ │ ├── TranslationInfo.kt │ │ │ │ ├── UserDao.kt │ │ │ │ ├── AppInfoRepository.kt │ │ │ │ └── AppDatabase.kt │ │ │ │ ├── ui │ │ │ │ ├── fragments │ │ │ │ │ ├── SettingAboutFragment.kt │ │ │ │ │ ├── CategoryScreenFragment.kt │ │ │ │ │ ├── SettingGeneralFragment.kt │ │ │ │ │ ├── SettingTemplateFragment.kt │ │ │ │ │ ├── CategoryNavBarFragment.kt │ │ │ │ │ ├── CategoryStatusBarFragment.kt │ │ │ │ │ ├── SettingHomeFragment.kt │ │ │ │ │ └── CategoryTranslationFragment.kt │ │ │ │ ├── viewmodels │ │ │ │ │ ├── UserViewModelFactory.kt │ │ │ │ │ ├── AppInfoViewModelFactory.kt │ │ │ │ │ ├── TranslationInfoViewModelFactory.kt │ │ │ │ │ ├── UserViewModel.kt │ │ │ │ │ ├── AppInfoViewModel.kt │ │ │ │ │ └── TranslationInfoViewModel.kt │ │ │ │ ├── preferences │ │ │ │ │ ├── EditTextPreference.kt │ │ │ │ │ └── SeekBarPreference.kt │ │ │ │ └── activitys │ │ │ │ │ ├── BasicActivity.kt │ │ │ │ │ ├── CategoryActivity.kt │ │ │ │ │ └── SettingActivity.kt │ │ │ │ ├── utilities │ │ │ │ ├── Constants.kt │ │ │ │ ├── DiffCallBack.kt │ │ │ │ ├── XSharedPreferencesUtil.kt │ │ │ │ ├── InjectorUtils.kt │ │ │ │ ├── LogcatManager.kt │ │ │ │ ├── XSPUtils.kt │ │ │ │ └── SPManager.kt │ │ │ │ ├── adapters │ │ │ │ └── BindingAdapters.kt │ │ │ │ ├── broadcasts │ │ │ │ ├── BootReceiver.kt │ │ │ │ └── ApkReceiver.kt │ │ │ │ ├── worker │ │ │ │ ├── RemoveAppInfoWorker.kt │ │ │ │ ├── AddAppInfoWorker.kt │ │ │ │ ├── CheckUpdateWorker.kt │ │ │ │ ├── SeedAppDatabaseWorker.kt │ │ │ │ └── UpdateAppDatabaseWorker.kt │ │ │ │ ├── WeiJuApplication.kt │ │ │ │ ├── xposed │ │ │ │ ├── MainHook.kt │ │ │ │ ├── StatusBarHook.kt │ │ │ │ └── NavBarHook.kt │ │ │ │ ├── provider │ │ │ │ └── SharedPreferencesProvider.kt │ │ │ │ └── servers │ │ │ │ └── ApkServer.kt │ │ │ └── Collections.kt │ ├── test │ │ └── java │ │ │ └── io │ │ │ └── ikws4 │ │ │ └── xposed │ │ │ └── weiju │ │ │ └── ExampleUnitTest.kt │ └── androidTest │ │ └── java │ │ └── io │ │ └── ikws4 │ │ └── xposed │ │ └── weiju │ │ └── ExampleInstrumentedTest.kt ├── google-services.json └── proguard-rules.pro ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .idea ├── codeStyles │ └── codeStyleConfig.xml ├── vcs.xml ├── misc.xml ├── runConfigurations.xml ├── gradle.xml ├── dictionaries │ └── zhipingne.xml └── jarRepositories.xml ├── settings.gradle ├── .gitignore ├── README.md ├── gradle.properties └── gradlew.bat /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/src/main/assets/xposed_init: -------------------------------------------------------------------------------- 1 | io.ikws4.weiju.xposed.MainHook -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ikws4/WeiJu/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ikws4/WeiJu/HEAD/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ikws4/WeiJu/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ikws4/WeiJu/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ikws4/WeiJu/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ikws4/WeiJu/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ikws4/WeiJu/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ikws4/WeiJu/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ikws4/WeiJu/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ikws4/WeiJu/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ikws4/WeiJu/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ikws4/WeiJu/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ikws4/WeiJu/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ikws4/WeiJu/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ikws4/WeiJu/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ikws4/WeiJu/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ikws4/WeiJu/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/values/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFFFFF 4 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | //setBinding(new Binding([gradle: this])) 4 | //evaluate(new File( 5 | // settingsDir.parentFile, 6 | // 'weiju_flutter/.android/include_flutter.groovy' 7 | //)) -------------------------------------------------------------------------------- /app/src/main/res/anim/custom_decelerate_interpolator.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4dp 4 | 8dp 5 | 16dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/data/User.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.data 2 | 3 | import androidx.room.Entity 4 | import androidx.room.PrimaryKey 5 | 6 | @Entity(tableName = "user") 7 | data class User( 8 | @PrimaryKey 9 | val freeSogouApiAmount: Int 10 | ) -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | /images 15 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Jul 20 17:15:37 CST 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip 7 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_play_arrow_black.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_add.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_variable_fragment.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_file_download.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/animator/appbar_layout_elevation.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_arrow_back.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/data/AppInfo.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.data 2 | 3 | import androidx.room.Entity 4 | import androidx.room.PrimaryKey 5 | 6 | @Entity(tableName = "app_infos") 7 | data class AppInfo( 8 | @PrimaryKey val pkgName: String, 9 | val name: String, 10 | val iconPath: String, 11 | val isSystemApp: Boolean, 12 | val isSelect: Boolean = false, 13 | val isDelete: Boolean = false 14 | ) -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/data/VariableModel.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.data 2 | 3 | import androidx.annotation.Keep 4 | 5 | @Keep 6 | data class VariableModel( 7 | val model: String, 8 | val brand: String, 9 | val device: String, 10 | val productName: String, 11 | val androidRelease: String, 12 | val longitude: String, 13 | val latitude: String, 14 | val imei: String, 15 | val imsi: String 16 | ) -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_fragment_category_home_template.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/xml/backup_descriptor.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_fragment_category_translation.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_close.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/ui/fragments/SettingAboutFragment.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.ui.fragments 2 | 3 | import android.os.Bundle 4 | import androidx.preference.PreferenceFragmentCompat 5 | import io.ikws4.weiju.R 6 | 7 | class SettingAboutFragment : PreferenceFragmentCompat() { 8 | 9 | override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { 10 | setPreferencesFromResource(R.xml.setting_about_preference, rootKey) 11 | } 12 | } -------------------------------------------------------------------------------- /app/src/test/java/io/ikws4/xposed/weiju/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.xposed.weiju 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_play_for_work.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/data/TranslationInfoRepository.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.data 2 | 3 | import kotlinx.coroutines.Dispatchers 4 | import kotlinx.coroutines.withContext 5 | 6 | class TranslationInfoRepository(private val translationDao: TranslationDao) { 7 | 8 | fun getByPkgName(pkgName: String) = translationDao.getByPkgName(pkgName) 9 | 10 | suspend fun update(info: TranslationInfo) = withContext(Dispatchers.IO) { 11 | translationDao.update(info) 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/res/xml/setting_about_preference.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_activity_translation_editor.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_exit_to_app.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_more_vert.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 12 | -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/ui/viewmodels/UserViewModelFactory.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.ui.viewmodels 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.ViewModelProvider 5 | import io.ikws4.weiju.data.UserRepository 6 | 7 | @Suppress("UNCHECKED_CAST") 8 | class UserViewModelFactory(private val userRepository: UserRepository):ViewModelProvider.NewInstanceFactory() { 9 | 10 | override fun create(modelClass: Class): T { 11 | return UserViewModel(userRepository) as T 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/res/anim/fragment_exit.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | 11 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/ui/viewmodels/AppInfoViewModelFactory.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.ui.viewmodels 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.ViewModelProvider 5 | import io.ikws4.weiju.data.AppInfoRepository 6 | 7 | class AppInfoViewModelFactory(private val appInfoRepository: AppInfoRepository) : 8 | ViewModelProvider.NewInstanceFactory() { 9 | 10 | @Suppress("UNCHECKED_CAST") 11 | override fun create(modelClass: Class): T { 12 | return AppInfoViewModel(appInfoRepository) as T 13 | } 14 | } -------------------------------------------------------------------------------- /app/src/main/res/anim/fragment_enter.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | 11 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_restore.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_search.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/utilities/Constants.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.utilities 2 | 3 | import io.ikws4.weiju.BuildConfig 4 | 5 | const val DATABASE_NAME = "weiju-db" 6 | const val WEIJU_PKG_NAME = BuildConfig.APPLICATION_ID 7 | const val FREE_SOGOU_API_REWARDED_AD_ID = "ca-app-pub-7928027245732586/3326443327" 8 | const val TEST_DEVICE_ID = "43805120C657BBBA84A17AE9C2BBBCA7" 9 | // sharedPreferences name constants 10 | const val HOOK_LIST_SP = "hook_list" // 用于在Xposed中的的判断 11 | const val TEMPLATE_SP = "$WEIJU_PKG_NAME.template" 12 | const val WEIJU_SP = "${WEIJU_PKG_NAME}_preferences" 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_block.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/xml/setting_template_preference.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_delete.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/ui/preferences/EditTextPreference.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.ui.preferences 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import androidx.preference.EditTextPreference 6 | 7 | class EditTextPreference(context: Context, attributeSet: AttributeSet) : EditTextPreference(context, attributeSet) { 8 | 9 | 10 | override fun setText(text: String?) { 11 | super.setText(text) 12 | notifyChanged() 13 | } 14 | 15 | override fun getSummary(): CharSequence { 16 | return String.format(super.getSummary().toString(), text) 17 | } 18 | } -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/ui/preferences/SeekBarPreference.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.ui.preferences 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import androidx.preference.SeekBarPreference 6 | 7 | class SeekBarPreference(context: Context, attributeSet: AttributeSet) : SeekBarPreference(context, attributeSet) { 8 | 9 | override fun persistInt(value: Int): Boolean { 10 | notifyChanged() 11 | return super.persistInt(value) 12 | } 13 | 14 | override fun getSummary(): CharSequence { 15 | return String.format(super.getSummary().toString(), value) 16 | } 17 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_wallpaper.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_get_app.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/ui/viewmodels/TranslationInfoViewModelFactory.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.ui.viewmodels 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.ViewModelProvider 5 | import io.ikws4.weiju.data.TranslationInfoRepository 6 | 7 | @Suppress("UNCHECKED_CAST") 8 | class TranslationInfoViewModelFactory(private val translationInfoRepository: TranslationInfoRepository, private val pkgName: String) : 9 | ViewModelProvider.NewInstanceFactory() { 10 | 11 | override fun create(modelClass: Class): T { 12 | return TranslationInfoViewModel(translationInfoRepository,pkgName) as T 13 | } 14 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_translate.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_warning.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_info.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_activity_selectapp.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 12 | 13 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/androidTest/java/io/ikws4/xposed/weiju/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.xposed.weiju 2 | 3 | import androidx.test.InstrumentationRegistry 4 | import androidx.test.runner.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getTargetContext() 22 | assertEquals("io.ikws4.xposed.weiju", appContext.packageName) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/ui/viewmodels/UserViewModel.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.ui.viewmodels 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.viewModelScope 5 | import io.ikws4.weiju.data.UserRepository 6 | import kotlinx.coroutines.ExperimentalCoroutinesApi 7 | import kotlinx.coroutines.cancel 8 | import kotlinx.coroutines.launch 9 | 10 | class UserViewModel(private val userRepository: UserRepository) : ViewModel() { 11 | val user = userRepository.getUser() 12 | 13 | @ExperimentalCoroutinesApi 14 | override fun onCleared() { 15 | super.onCleared() 16 | viewModelScope.cancel() 17 | } 18 | 19 | fun increaseFreeSogouApiAmount(amount: Int) = viewModelScope.launch { 20 | userRepository.increaseFreeSogouApiAmount(amount) 21 | } 22 | } -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/data/AppInfoDao.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.data 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.room.* 5 | 6 | @Dao 7 | interface AppInfoDao { 8 | @Query("SELECT * FROM app_infos WHERE isDelete = 0 ORDER BY name") 9 | fun getAll(): LiveData> 10 | 11 | @Query("SELECT * FROM app_infos WHERE pkgName = :pkgName") 12 | fun getByPkgName(pkgName: String):AppInfo? 13 | 14 | @Insert(onConflict = OnConflictStrategy.REPLACE) 15 | fun insert(appInfo: AppInfo) 16 | 17 | @Insert(onConflict = OnConflictStrategy.REPLACE) 18 | fun insert(appInfos: List) 19 | 20 | @Update 21 | fun update(appInfo: AppInfo) 22 | 23 | @Query("UPDATE app_infos SET isDelete = 1 WHERE pkgName = :pkgName") 24 | fun remove(pkgName: String) 25 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_check_circle.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_opacity.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_save.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_center_focus_strong.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/data/TranslationDao.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.data 2 | 3 | import android.database.Cursor 4 | import androidx.lifecycle.LiveData 5 | import androidx.room.Dao 6 | import androidx.room.Insert 7 | import androidx.room.Query 8 | import androidx.room.Update 9 | 10 | @Dao 11 | interface TranslationDao { 12 | @Query("SELECT * FROM translation_info WHERE pkgName = :pkgName") 13 | fun getByPkgName(pkgName: String): LiveData> // 查找特定包名的翻译数据,用于翻译编辑器 14 | 15 | @Query("SELECT result FROM translation_info WHERE `query` = :query AND pkgName = :pkgName AND `from` = :from AND `to` = :to") 16 | fun get(query: String, pkgName: String, from: String, to: String): Cursor 17 | 18 | @Insert 19 | fun insert(info: TranslationInfo): Long 20 | 21 | @Update 22 | fun update(info: TranslationInfo) // 更新翻译结果(修正) 23 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_move_to_inbox.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/adapters/BindingAdapters.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.adapters 2 | 3 | import android.view.View 4 | import android.widget.ImageView 5 | import androidx.annotation.ColorRes 6 | import androidx.core.content.ContextCompat 7 | import androidx.databinding.BindingAdapter 8 | 9 | @BindingAdapter("isGone") 10 | fun bindIsGone(view: View, isGone: Boolean) { 11 | view.visibility = if (isGone) { 12 | View.GONE 13 | } else { 14 | View.VISIBLE 15 | } 16 | } 17 | 18 | @BindingAdapter("backgroundTint") 19 | fun bindBackGroundTint(imageView: ImageView, @ColorRes tint: Int) { 20 | val context = imageView.context 21 | val color = ContextCompat.getColor(context, tint) 22 | imageView.setColorFilter(color) 23 | } 24 | 25 | @BindingAdapter("src") 26 | fun bindSrc(view: ImageView, src: Int) { 27 | view.setImageResource(src) 28 | } -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/data/UserRepository.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.data 2 | 3 | import kotlinx.coroutines.Dispatchers 4 | import kotlinx.coroutines.withContext 5 | 6 | class UserRepository private constructor(private val userDao: UserDao) { 7 | 8 | suspend fun increaseFreeSogouApiAmount(amount: Int) = withContext(Dispatchers.IO) { 9 | userDao.increaseFreeSogouApiAmount(amount) 10 | } 11 | 12 | fun getUser() = userDao.getUserFormLiveData() 13 | 14 | companion object { 15 | // For Singleton instantiation 16 | @Volatile 17 | private var instance: UserRepository? = null 18 | 19 | fun getInstance(userDao: UserDao): UserRepository { 20 | return instance ?: synchronized(this) { 21 | instance ?: UserRepository(userDao).also { instance = it } 22 | } 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/data/TranslationInfo.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.data 2 | 3 | import android.content.ContentValues 4 | import androidx.room.Entity 5 | import androidx.room.PrimaryKey 6 | 7 | @Entity(tableName = "translation_info") 8 | data class TranslationInfo( 9 | @PrimaryKey(autoGenerate = true) 10 | val id: Long = 0, 11 | val pkgName: String, 12 | val query: String, 13 | val from: String, 14 | val to: String, 15 | val result: String 16 | ) { 17 | companion object { 18 | fun fromContentValues(values: ContentValues) = TranslationInfo( 19 | pkgName = values.getAsString("pkgName"), 20 | query = values.getAsString("query"), 21 | from = values.getAsString("from"), 22 | to = values.getAsString("to"), 23 | result = values.getAsString("result") 24 | ) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/ui/viewmodels/AppInfoViewModel.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.ui.viewmodels 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.viewModelScope 5 | import io.ikws4.weiju.data.AppInfo 6 | import io.ikws4.weiju.data.AppInfoRepository 7 | import kotlinx.coroutines.ExperimentalCoroutinesApi 8 | import kotlinx.coroutines.cancel 9 | import kotlinx.coroutines.launch 10 | 11 | class AppInfoViewModel(private val appInfoRepository: AppInfoRepository) : ViewModel() { 12 | val appInfos = appInfoRepository.getAll() 13 | 14 | @ExperimentalCoroutinesApi 15 | override fun onCleared() { 16 | super.onCleared() 17 | viewModelScope.cancel() 18 | } 19 | 20 | /** 21 | * 标记为是否被选择 isSelect 22 | */ 23 | fun update(appInfo: AppInfo) = viewModelScope.launch { 24 | appInfoRepository.update(appInfo) 25 | } 26 | } -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_translation_editor.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 13 | 14 | 17 | 18 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/ui/fragments/CategoryScreenFragment.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.ui.fragments 2 | 3 | import android.os.Bundle 4 | import androidx.navigation.fragment.navArgs 5 | import androidx.preference.ListPreference 6 | import androidx.preference.Preference 7 | import androidx.preference.PreferenceFragmentCompat 8 | import com.afollestad.materialdialogs.MaterialDialog 9 | import com.afollestad.materialdialogs.input.getInputField 10 | import com.afollestad.materialdialogs.input.input 11 | import io.ikws4.weiju.R 12 | 13 | class CategoryScreenFragment : PreferenceFragmentCompat() { 14 | private val args: CategoryScreenFragmentArgs by navArgs() 15 | 16 | override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { 17 | preferenceManager.sharedPreferencesName = args.pkgName 18 | setPreferencesFromResource(R.xml.screen_preference, rootKey) 19 | } 20 | } -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/utilities/DiffCallBack.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.utilities 2 | 3 | import androidx.recyclerview.widget.DiffUtil 4 | import io.ikws4.weiju.data.AppInfo 5 | import io.ikws4.weiju.data.TranslationInfo 6 | 7 | class AppInfoDiffCallBack : DiffUtil.ItemCallback() { 8 | override fun areItemsTheSame(oldItem: AppInfo, newItem: AppInfo): Boolean = 9 | oldItem.pkgName == newItem.pkgName 10 | 11 | override fun areContentsTheSame(oldItem: AppInfo, newItem: AppInfo): Boolean = 12 | oldItem == oldItem 13 | } 14 | 15 | class TranslationInfoDiffCallBack : DiffUtil.ItemCallback() { 16 | override fun areItemsTheSame(oldItem: TranslationInfo, newItem: TranslationInfo): Boolean = 17 | oldItem.query == newItem.query 18 | 19 | override fun areContentsTheSame(oldItem: TranslationInfo, newItem: TranslationInfo): Boolean = 20 | oldItem == newItem 21 | } -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/ui/viewmodels/TranslationInfoViewModel.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.ui.viewmodels 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.viewModelScope 5 | import io.ikws4.weiju.data.TranslationInfo 6 | import io.ikws4.weiju.data.TranslationInfoRepository 7 | import kotlinx.coroutines.ExperimentalCoroutinesApi 8 | import kotlinx.coroutines.cancel 9 | import kotlinx.coroutines.launch 10 | 11 | class TranslationInfoViewModel(private val translationInfoRepository: TranslationInfoRepository, pkgName: String) : ViewModel() { 12 | val translationInfos = translationInfoRepository.getByPkgName(pkgName) 13 | 14 | @ExperimentalCoroutinesApi 15 | override fun onCleared() { 16 | super.onCleared() 17 | viewModelScope.cancel() 18 | } 19 | 20 | fun update(info: TranslationInfo) = viewModelScope.launch { 21 | translationInfoRepository.update(info) 22 | } 23 | } -------------------------------------------------------------------------------- /app/src/main/res/xml/setting_home_preference.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #ffffff 5 | #ffffff 6 | #448aff 7 | #448aff 8 | #fafafa 9 | #eeeeee 10 | 11 | #ffffff 12 | 13 | #000000 14 | 15 | 16 | #000000 17 | #757575 18 | #e16b8c 19 | #90b44b 20 | #58b2dc 21 | #ca7a2c 22 | #ffC408 23 | #4286f3 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/animator/life_on_touch.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 13 | 14 | 15 | 16 | 17 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_help.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/values-night/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #ffffff 5 | #1A1A1A 6 | #448aff 7 | #448aff 8 | #fafafa 9 | #eeeeee 10 | 11 | #ffffff 12 | 13 | #ffffff 14 | 15 | 16 | 17 | #ffffff 18 | #757575 19 | #e16b8c 20 | #90b44b 21 | #58b2dc 22 | #ca7a2c 23 | #ffC408 24 | #4286f3 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/layout/toolbar.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 16 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_wb_sunny.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/dictionaries/zhipingne.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | activitys 5 | appid 6 | appinfos 7 | baidu 8 | claxx 9 | curtime 10 | databinding 11 | ikws 12 | imei 13 | imsi 14 | infos 15 | lpparam 16 | mido 17 | mqqwpa 18 | recyclerview 19 | redmi 20 | selectapp 21 | selectedapp 22 | snackbar 23 | sogou 24 | sougou 25 | vert 26 | virtualapp 27 | vungle 28 | weiju 29 | xiaomi 30 | xposed 31 | xposeddescription 32 | xposedm 33 | xposedminversion 34 | xposedmodule 35 | youdao 36 | 37 | 38 | -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/data/UserDao.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.data 2 | 3 | import android.database.Cursor 4 | import androidx.lifecycle.LiveData 5 | import androidx.room.Dao 6 | import androidx.room.Insert 7 | import androidx.room.Query 8 | 9 | @Dao 10 | interface UserDao { 11 | @Query("SELECT * FROM user") 12 | fun getUserFormLiveData(): LiveData 13 | 14 | @Query("SELECT * FROM user") 15 | fun getUser(): User 16 | 17 | @Query("SELECT freeSogouApiAmount FROM user") 18 | fun getFreeSogouApiAmount(): Cursor 19 | 20 | @Query("UPDATE user SET freeSogouApiAmount =:amount") 21 | fun setFreeSogouApiAmount(amount: Int) 22 | 23 | @Query("UPDATE user SET freeSogouApiAmount = freeSogouApiAmount + :amount") 24 | fun increaseFreeSogouApiAmount(amount: Int) 25 | 26 | @Query("UPDATE user SET freeSogouApiAmount = freeSogouApiAmount - :amount") 27 | fun decreaseFreeSogouApiAmount(amount: Int) 28 | 29 | @Insert 30 | fun insert(user: User) 31 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_screen_rotation.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 14 | 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WeiJu 2 | ![](https://img.shields.io/github/license/ikws4/WeiJu) 3 | ![](https://img.shields.io/github/v/release/ikws4/WeiJu) 4 | 5 | ### 功能介绍 6 | 1. 状态栏于导航栏自定义(背景颜色、图标颜色、显隐) 7 | 2. 屏幕方向(横屏竖屏) 8 | 3. 强制截图与录屏 9 | 4. 自定义语言 10 | 5. 对话框取消 11 | 6. 自定义每个应用的DPI 12 | 7. 全局翻译(类似Chrome的翻译功能) 13 | 8. 机型修改 14 | ....还有更多,欢迎使用。 15 | 16 | ### 翻译功能 17 | [微聚的翻译功能使用教程](https://ikws4.github.io/post/Wq44jmv1i/) 18 |

19 | 20 | 21 |

22 | 23 | ### 应用截图 24 |

25 | 26 | 27 |

28 | -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/broadcasts/BootReceiver.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.broadcasts 2 | 3 | import android.content.BroadcastReceiver 4 | import android.content.Context 5 | import android.content.Intent 6 | import android.os.Build 7 | import io.ikws4.weiju.servers.ApkServer 8 | import io.ikws4.weiju.utilities.SPManager 9 | 10 | class BootReceiver : BroadcastReceiver() { 11 | 12 | override fun onReceive(context: Context, intent: Intent) { 13 | // 启动前台服务 14 | // 监听应用的安装与卸载,用于更新数据库的数据 15 | if (intent.action == Intent.ACTION_BOOT_COMPLETED) { 16 | val weiJuSP = SPManager.getInstance(context).WeiJuSP() 17 | if (weiJuSP.isBootCompleted) { 18 | val server = Intent(context, ApkServer::class.java) 19 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 20 | context.startForegroundService(server) 21 | } else { 22 | context.startService(server) 23 | } 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/ui/fragments/SettingGeneralFragment.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.ui.fragments 2 | 3 | import android.os.Bundle 4 | import androidx.appcompat.app.AppCompatDelegate 5 | import androidx.preference.ListPreference 6 | import androidx.preference.Preference 7 | import androidx.preference.PreferenceFragmentCompat 8 | import io.ikws4.weiju.R 9 | import io.ikws4.weiju.utilities.LogcatManager 10 | 11 | class SettingGeneralFragment : PreferenceFragmentCompat() { 12 | 13 | override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { 14 | setPreferencesFromResource(R.xml.setting_general_preference, rootKey) 15 | } 16 | 17 | override fun onPreferenceTreeClick(preference: Preference): Boolean { 18 | return when (preference.key) { 19 | "report_bug" -> { 20 | val message = LogcatManager.getSavedLog(context!!) 21 | LogcatManager.show(context!!, message, true) 22 | true 23 | } 24 | else -> super.onPreferenceTreeClick(preference) 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/ui/fragments/SettingTemplateFragment.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.ui.fragments 2 | 3 | import android.os.Bundle 4 | import androidx.navigation.fragment.findNavController 5 | import androidx.preference.Preference 6 | import androidx.preference.PreferenceFragmentCompat 7 | import io.ikws4.weiju.R 8 | import io.ikws4.weiju.utilities.TEMPLATE_SP 9 | 10 | class SettingTemplateFragment : PreferenceFragmentCompat() { 11 | 12 | override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { 13 | setPreferencesFromResource(R.xml.setting_template_preference, rootKey) 14 | } 15 | 16 | override fun onPreferenceTreeClick(preference: Preference): Boolean { 17 | return when (preference.key) { 18 | "config_template" -> { 19 | val title = preference.title.toString() 20 | findNavController().navigate(SettingTemplateFragmentDirections.toCategoryActivity(title, TEMPLATE_SP)) 21 | true 22 | } 23 | else -> super.onPreferenceTreeClick(preference) 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_cloud_off.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/data/AppInfoRepository.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.data 2 | 3 | import kotlinx.coroutines.Dispatchers 4 | import kotlinx.coroutines.withContext 5 | 6 | /** 7 | * 用于对数据库的读写 8 | * @property appInfoDao AppInfoDao 9 | * @constructor 10 | */ 11 | class AppInfoRepository private constructor(private val appInfoDao: AppInfoDao) { 12 | 13 | suspend fun insert(appInfo: AppInfo) = withContext(Dispatchers.IO) { 14 | appInfoDao.insert(appInfo) 15 | } 16 | 17 | suspend fun update(appInfo: AppInfo) = withContext(Dispatchers.IO) { 18 | appInfoDao.update(appInfo) 19 | } 20 | 21 | fun getAll() = appInfoDao.getAll() 22 | 23 | companion object { 24 | // For Singleton instantiation 25 | @Volatile 26 | private var instance: AppInfoRepository? = null 27 | 28 | fun getInstance(appInfoDao: AppInfoDao): AppInfoRepository { 29 | return instance ?: synchronized(this) { 30 | instance ?: AppInfoRepository(appInfoDao).also { 31 | instance = it 32 | } 33 | } 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_g_translate.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/google-services.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_info": { 3 | "project_number": "233827984030", 4 | "firebase_url": "https://weiju-247903.firebaseio.com", 5 | "project_id": "weiju-247903", 6 | "storage_bucket": "weiju-247903.appspot.com" 7 | }, 8 | "client": [ 9 | { 10 | "client_info": { 11 | "mobilesdk_app_id": "1:233827984030:android:b08aa905bdbb3b35", 12 | "android_client_info": { 13 | "package_name": "io.ikws4.weiju" 14 | } 15 | }, 16 | "oauth_client": [ 17 | { 18 | "client_id": "233827984030-oe2r7oonbrrmfsk7tmho7uutafnhla1a.apps.googleusercontent.com", 19 | "client_type": 3 20 | } 21 | ], 22 | "api_key": [ 23 | { 24 | "current_key": "AIzaSyCQEKTvXKC4_SCKhUhsSiIZxyGJ7h4KHIw" 25 | } 26 | ], 27 | "services": { 28 | "appinvite_service": { 29 | "other_platform_oauth_client": [ 30 | { 31 | "client_id": "233827984030-oe2r7oonbrrmfsk7tmho7uutafnhla1a.apps.googleusercontent.com", 32 | "client_type": 3 33 | } 34 | ] 35 | } 36 | } 37 | } 38 | ], 39 | "configuration_version": "1" 40 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_category.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 14 | 15 | 18 | 19 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/ui/fragments/CategoryNavBarFragment.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.ui.fragments 2 | 3 | import android.os.Bundle 4 | import androidx.navigation.fragment.navArgs 5 | import androidx.preference.ListPreference 6 | import androidx.preference.PreferenceFragmentCompat 7 | import io.ikws4.weiju.R 8 | import io.ikws4.weiju.ui.preferences.EditTextPreference 9 | 10 | class CategoryNavBarFragment : PreferenceFragmentCompat() { 11 | private val args: CategoryNavBarFragmentArgs by navArgs() 12 | 13 | override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { 14 | preferenceManager.sharedPreferencesName = args.pkgName 15 | setPreferencesFromResource(R.xml.navigation_bar_preference, rootKey) 16 | 17 | with(preferenceManager) { 18 | val immersive = findPreference("immersive_nav_bar")!! 19 | val customColor = findPreference("custom_nav_bar_color")!!.apply { 20 | isEnabled = immersive.value == "Custom" 21 | } 22 | immersive.setOnPreferenceChangeListener { _, newValue -> 23 | customColor.isEnabled = newValue.toString() == "Custom" 24 | true 25 | } 26 | 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/ui/fragments/CategoryStatusBarFragment.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.ui.fragments 2 | 3 | import android.os.Bundle 4 | import androidx.navigation.fragment.navArgs 5 | import androidx.preference.EditTextPreference 6 | import androidx.preference.ListPreference 7 | import androidx.preference.PreferenceFragmentCompat 8 | import io.ikws4.weiju.R 9 | 10 | class CategoryStatusBarFragment : PreferenceFragmentCompat() { 11 | private val args: CategoryStatusBarFragmentArgs by navArgs() 12 | 13 | override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { 14 | preferenceManager.sharedPreferencesName = args.pkgName 15 | setPreferencesFromResource(R.xml.status_bar_preference, rootKey) 16 | 17 | with(preferenceManager) { 18 | val immersive = findPreference("immersive_status_bar")!! 19 | val customColor = findPreference("custom_status_bar_color")!!.apply { 20 | isEnabled = immersive.value == "Custom" 21 | } 22 | immersive.setOnPreferenceChangeListener { _, newValue -> 23 | customColor.isEnabled = newValue.toString() == "Custom" 24 | true 25 | } 26 | } 27 | } 28 | 29 | 30 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting_home_preference is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app's APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true 20 | # Kotlin code style for this project: "official" or "obsolete": 21 | kotlin.code.style=official 22 | org.gradle.caching=true 23 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_setting.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 14 | 15 | 18 | 19 | 31 | 32 | -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/worker/RemoveAppInfoWorker.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.worker 2 | 3 | import android.content.Context 4 | import androidx.core.content.edit 5 | import androidx.work.CoroutineWorker 6 | import androidx.work.WorkerParameters 7 | import io.ikws4.weiju.data.AppDatabase 8 | import io.ikws4.weiju.utilities.LogcatManager 9 | import kotlinx.coroutines.coroutineScope 10 | 11 | class RemoveAppInfoWorker( 12 | context: Context, 13 | workerParameters: WorkerParameters 14 | ) : 15 | CoroutineWorker(context, workerParameters) { 16 | 17 | override suspend fun doWork(): Result = coroutineScope { 18 | try { 19 | val pkgName = inputData.getString("pkgName")!! 20 | val appInfoDao = AppDatabase.getInstance(applicationContext).appInfoDao() 21 | appInfoDao.remove(pkgName) 22 | val sharedPreferences = applicationContext.getSharedPreferences(pkgName, Context.MODE_PRIVATE) 23 | sharedPreferences.edit { 24 | clear() 25 | } 26 | 27 | Result.success() 28 | } catch (ex: Exception) { 29 | LogcatManager.saveToFile(applicationContext, ex) 30 | Result.failure() 31 | } 32 | } 33 | 34 | companion object { 35 | const val TAG = "RemoveAppInfoWorker" 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/ui/activitys/BasicActivity.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.ui.activitys 2 | 3 | import android.annotation.SuppressLint 4 | import android.content.pm.PackageManager 5 | import android.view.MenuItem 6 | import androidx.appcompat.app.AppCompatActivity 7 | import androidx.core.app.ActivityCompat 8 | import androidx.core.content.ContextCompat 9 | 10 | @SuppressLint("Registered") 11 | open class BasicActivity : AppCompatActivity() { 12 | 13 | override fun onOptionsItemSelected(item: MenuItem): Boolean { 14 | return when (item.itemId) { 15 | android.R.id.home -> { 16 | onBackPressed() 17 | true 18 | } 19 | else -> super.onOptionsItemSelected(item) 20 | } 21 | } 22 | 23 | /** 24 | * 申请权限 25 | * @param permissions Array 26 | */ 27 | fun getPermission(permissions:Array){ 28 | permissions.forEach {permission -> 29 | if (ContextCompat.checkSelfPermission(this,permission) != PackageManager.PERMISSION_GRANTED){ 30 | if (ActivityCompat.shouldShowRequestPermissionRationale(this,permission)){ 31 | 32 | }else{ 33 | ActivityCompat.requestPermissions(this, arrayOf(permission),1) 34 | } 35 | } 36 | 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/ui/fragments/SettingHomeFragment.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.ui.fragments 2 | 3 | import android.os.Bundle 4 | import androidx.navigation.fragment.findNavController 5 | import androidx.preference.Preference 6 | import androidx.preference.PreferenceFragmentCompat 7 | import io.ikws4.weiju.R 8 | 9 | class SettingHomeFragment : PreferenceFragmentCompat() { 10 | 11 | override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { 12 | setPreferencesFromResource(R.xml.setting_home_preference, rootKey) 13 | } 14 | 15 | override fun onPreferenceTreeClick(preference: Preference): Boolean { 16 | when (preference.key) { 17 | "general_screen" -> { 18 | findNavController().navigate(SettingHomeFragmentDirections.toSettingGeneralFragment()) 19 | } 20 | "template_screen" -> { 21 | findNavController().navigate(SettingHomeFragmentDirections.toSettingTemplateFragment()) 22 | } 23 | "translation_screen" -> { 24 | findNavController().navigate(SettingHomeFragmentDirections.toSettingTranslationFragment()) 25 | } 26 | "about_screen" -> { 27 | findNavController().navigate(SettingHomeFragmentDirections.toSettingAboutFragment()) 28 | } 29 | } 30 | return true 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_sentiment_dissatisfied.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 14 | 17 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_sentiment_satisfied.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 14 | 17 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_fragment_category_home.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 11 | 12 | 18 | 19 | 20 | 24 | 25 | 29 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /app/src/main/res/xml/setting_general_preference.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 13 | 14 | 19 | 20 | 21 | 22 | 23 | 24 | 28 | 29 | 30 | 31 | 32 | 33 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_face.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 14 | 17 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_sogou.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/layout/bottom_sheet_api_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 14 | 15 | 19 | 20 | 21 | 22 | 28 | 29 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/utilities/XSharedPreferencesUtil.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.utilities 2 | 3 | import android.content.Context 4 | import android.os.Environment 5 | import de.robv.android.xposed.XSharedPreferences 6 | import io.ikws4.weiju.BuildConfig 7 | import java.io.File 8 | 9 | /** 10 | * 由于 [XSharedPreferences] 是直接通过 [Environment.getDataDirectory] 11 | * 来获取 Data path 的,所以无法在 virtual xposed 这样的沙盒应用中运行 12 | * 所以使用 [Context] 来获取 Data path 13 | */ 14 | internal object XSharedPreferencesUtil { 15 | private const val PACKAGE_NAME = BuildConfig.APPLICATION_ID 16 | 17 | private fun getUserDataPathFromContext(context: Context): String { 18 | return context.filesDir.parentFile!!.parent!! 19 | } 20 | 21 | 22 | fun get( 23 | context: Context, packageName: String, prefFileName: String 24 | ): XSharedPreferences { 25 | val file = 26 | File(getUserDataPathFromContext(context), "$packageName/shared_prefs/$prefFileName.xml") 27 | 28 | return XSharedPreferences(file) 29 | } 30 | 31 | fun get( 32 | context: Context, prefFileName: String 33 | ): XSharedPreferences { 34 | return get(context, PACKAGE_NAME, prefFileName) 35 | } 36 | 37 | fun getHookList(context: Context): XSharedPreferences { 38 | return get(context, PACKAGE_NAME, HOOK_LIST_SP) 39 | } 40 | 41 | fun getAppConfig(context: Context): XSharedPreferences { 42 | return get(context, PACKAGE_NAME, PACKAGE_NAME + "_preferences") 43 | } 44 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting_home_preference in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | # JSR 305 annotations are for embedding nullability information. 23 | -dontwarn javax.annotation.** 24 | 25 | # A resource is loaded with a relative path so the package of this class must be preserved. 26 | -keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase 27 | 28 | # Animal Sniffer compileOnly dependency to ensure APIs are compatible with older versions of Java. 29 | -dontwarn org.codehaus.mojo.animal_sniffer.* 30 | 31 | # OkHttp platform used only on JVM and when Conscrypt dependency is available. 32 | -dontwarn okhttp3.internal.platform.ConscryptPlatform 33 | 34 | -dontwarn com.commonsware.cwac.saferoom.* 35 | 36 | -keep class net.sqlcipher.** { *; } 37 | 38 | -keepattributes Signature -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/broadcasts/ApkReceiver.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.broadcasts 2 | 3 | import android.content.BroadcastReceiver 4 | import android.content.Context 5 | import android.content.Intent 6 | import androidx.work.Data 7 | import androidx.work.OneTimeWorkRequestBuilder 8 | import androidx.work.WorkManager 9 | import io.ikws4.weiju.worker.AddAppInfoWorker 10 | import io.ikws4.weiju.worker.RemoveAppInfoWorker 11 | 12 | 13 | class ApkReceiver : BroadcastReceiver() { 14 | override fun onReceive(context: Context, intent: Intent) { 15 | val pkgName = intent.data!!.schemeSpecificPart 16 | when (intent.action) { 17 | Intent.ACTION_PACKAGE_ADDED -> { 18 | val data = Data.Builder() 19 | .putString("pkgName", pkgName) 20 | .build() 21 | val result = OneTimeWorkRequestBuilder() 22 | .setInputData(data) 23 | .build() 24 | WorkManager.getInstance(context).enqueue(result) 25 | } 26 | 27 | Intent.ACTION_PACKAGE_FULLY_REMOVED, Intent.ACTION_PACKAGE_REMOVED -> { 28 | val data = Data.Builder() 29 | .putString("pkgName", pkgName) 30 | .build() 31 | val result = OneTimeWorkRequestBuilder() 32 | .setInputData(data) 33 | .build() 34 | WorkManager.getInstance(context).enqueue(result) 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_translation_info.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 14 | 15 | 24 | 25 | 30 | 31 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /app/src/main/res/xml/setting_translation_preference.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 13 | 14 | 22 | 23 | 24 | 25 | 26 | 27 | 31 | 35 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_bug_report.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/navigation/nav_graph_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 16 | 19 | 22 | 23 | 24 | 27 | 30 | 33 | 34 | 35 | 38 | 39 | 42 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/worker/AddAppInfoWorker.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.worker 2 | 3 | import android.content.Context 4 | import android.content.pm.ApplicationInfo 5 | import androidx.work.CoroutineWorker 6 | import androidx.work.WorkerParameters 7 | import io.ikws4.weiju.data.AppDatabase 8 | import io.ikws4.weiju.data.AppInfo 9 | import io.ikws4.weiju.utilities.LogcatManager 10 | import kotlinx.coroutines.coroutineScope 11 | import saveToFile 12 | 13 | class AddAppInfoWorker( 14 | context: Context, 15 | workerParameters: WorkerParameters 16 | ) : 17 | CoroutineWorker(context, workerParameters) { 18 | 19 | override suspend fun doWork(): Result = coroutineScope { 20 | try { 21 | val appInfoDao = AppDatabase.getInstance(applicationContext).appInfoDao() 22 | val pm = applicationContext.packageManager 23 | val pkgName = inputData.getString("pkgName")!! 24 | val applicationInfo = pm.getApplicationInfo(pkgName, 0) 25 | val path = "${applicationContext.filesDir.path}/application_icons/" 26 | val fileName = "${applicationInfo.packageName}.png" 27 | val fullPath = path + fileName 28 | 29 | 30 | appInfoDao.insert( 31 | AppInfo( 32 | pkgName = applicationInfo.packageName, 33 | name = applicationInfo.loadLabel(pm).toString(), 34 | iconPath = fullPath, 35 | isSystemApp = (applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM) != 0 36 | ) 37 | ) 38 | applicationInfo.loadIcon(pm).saveToFile(fileName, path) 39 | Result.success() 40 | } catch (ex: Exception) { 41 | LogcatManager.saveToFile(applicationContext, ex) 42 | Result.failure() 43 | } 44 | } 45 | 46 | companion object { 47 | const val TAG = "AddAppInfoWorker" 48 | } 49 | 50 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_selectapp.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 11 | 12 | 15 | 16 | 17 | 18 | 22 | 23 | 26 | 27 | 31 | 32 | 39 | 40 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/utilities/InjectorUtils.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.utilities 2 | 3 | import android.content.Context 4 | import io.ikws4.weiju.data.AppDatabase 5 | import io.ikws4.weiju.data.AppInfoRepository 6 | import io.ikws4.weiju.data.TranslationInfoRepository 7 | import io.ikws4.weiju.data.UserRepository 8 | import io.ikws4.weiju.ui.viewmodels.AppInfoViewModelFactory 9 | import io.ikws4.weiju.ui.viewmodels.TranslationInfoViewModelFactory 10 | import io.ikws4.weiju.ui.viewmodels.UserViewModelFactory 11 | 12 | object InjectorUtils { 13 | 14 | private fun getAppInfoRepository(context: Context): AppInfoRepository { 15 | val appInfoDao = AppDatabase.getInstance(context.applicationContext).appInfoDao() 16 | return AppInfoRepository.getInstance(appInfoDao) 17 | } 18 | 19 | private fun getTranslationRepository(context: Context): TranslationInfoRepository { 20 | val translationDao = AppDatabase.getInstance(context.applicationContext).translationInfoDao() 21 | return TranslationInfoRepository(translationDao) 22 | } 23 | 24 | private fun getUserRepository(context: Context): UserRepository { 25 | val userDao = AppDatabase.getInstance(context.applicationContext).userDao() 26 | return UserRepository.getInstance(userDao) 27 | } 28 | 29 | fun provideAppInfoViewModelFactory(context: Context): AppInfoViewModelFactory { 30 | val repository = getAppInfoRepository(context) 31 | return AppInfoViewModelFactory(repository) 32 | } 33 | 34 | fun provideTranslationInfoViewModelFactory(context: Context, pkgName: String): TranslationInfoViewModelFactory { 35 | val repository = getTranslationRepository(context) 36 | return TranslationInfoViewModelFactory(repository, pkgName) 37 | } 38 | 39 | fun provideUserViewModelFactory(context: Context): UserViewModelFactory { 40 | val repository = getUserRepository(context) 41 | return UserViewModelFactory(repository) 42 | } 43 | } -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/ui/activitys/CategoryActivity.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.ui.activitys 2 | 3 | import android.os.Bundle 4 | import android.view.MenuItem 5 | import androidx.databinding.DataBindingUtil 6 | import androidx.navigation.NavController 7 | import androidx.navigation.findNavController 8 | import androidx.navigation.navArgs 9 | import com.google.android.material.appbar.MaterialToolbar 10 | import io.ikws4.weiju.R 11 | import io.ikws4.weiju.databinding.ActivityCategoryBinding 12 | 13 | class CategoryActivity : BasicActivity() { 14 | private val args by navArgs() 15 | 16 | override fun onCreate(savedInstanceState: Bundle?) { 17 | super.onCreate(savedInstanceState) 18 | val binding = DataBindingUtil.setContentView(this, R.layout.activity_category) 19 | val navController = findNavController(R.id.nav_host_fragment) 20 | val toolbar = binding.actionBar.toolbar 21 | setupToolbarWithNav(navController, toolbar) 22 | } 23 | 24 | /** 25 | * 设置Toolbar标题为appName 26 | * 把destination的label属性作为toolbar的子标题 27 | * @param navController NavController 28 | * @param toolbar MaterialToolbar 29 | */ 30 | private fun setupToolbarWithNav(navController: NavController, toolbar: MaterialToolbar) { 31 | toolbar.title = args.title 32 | setSupportActionBar(toolbar) 33 | val bundle = Bundle().apply { 34 | putString("pkgName", args.pkgName) 35 | } 36 | with(navController) { 37 | // 动态设置导航图属性 38 | setGraph(R.navigation.nav_graph_category, bundle) 39 | addOnDestinationChangedListener { _, destination, _ -> 40 | toolbar.subtitle = destination.label 41 | } 42 | } 43 | } 44 | 45 | override fun onOptionsItemSelected(item: MenuItem): Boolean { 46 | return when (item.itemId) { 47 | else -> super.onOptionsItemSelected(item) 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_color_lens.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 14 | 17 | 20 | 23 | 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_selectedapp.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 14 | 15 | 29 | 30 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /app/src/main/res/xml/navigation_bar_preference.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 9 | 10 | 17 | 18 | 28 | 29 | 36 | 37 | 47 | 48 | -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/worker/CheckUpdateWorker.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.worker 2 | 3 | import android.content.Context 4 | import androidx.work.CoroutineWorker 5 | import androidx.work.WorkerParameters 6 | import io.ikws4.weiju.BuildConfig 7 | import io.ikws4.weiju.utilities.LogcatManager 8 | import io.ikws4.weiju.utilities.SPManager 9 | import kotlinx.coroutines.coroutineScope 10 | import okhttp3.OkHttpClient 11 | import okhttp3.Request 12 | import org.json.JSONObject 13 | 14 | /** 15 | * 在后台检查软件更新 16 | * @constructor 17 | */ 18 | class CheckUpdateWorker(context: Context, workerParameters: WorkerParameters) : CoroutineWorker(context, workerParameters) { 19 | 20 | override suspend fun doWork(): Result = coroutineScope { 21 | try { 22 | val client = OkHttpClient() 23 | val request = Request.Builder() 24 | .url("https://api.github.com/repos/ikws4/WeiJu/releases/latest") 25 | .build() 26 | val response = client.newCall(request).execute() 27 | val result = JSONObject(response.body!!.string()) 28 | 29 | val versionName = result.getString("tag_name") 30 | val spManager = SPManager.getInstance(applicationContext) 31 | if (BuildConfig.VERSION_NAME < versionName) { 32 | val assets = result.getJSONArray("assets") 33 | val downloadUrl = assets.getJSONObject(0).getString("browser_download_url") 34 | with(spManager.WeiJuSP()) { 35 | newVersionDownloadUrl = downloadUrl 36 | this.versionName = versionName 37 | } 38 | spManager.WeiJuSP().newVersionDownloadUrl = downloadUrl 39 | } else { 40 | spManager.WeiJuSP().versionName = BuildConfig.VERSION_NAME 41 | } 42 | 43 | Result.success() 44 | } catch (ex: Exception) { 45 | LogcatManager.saveToFile(applicationContext, ex) 46 | Result.failure() 47 | } 48 | } 49 | 50 | companion object { 51 | const val TAG = "CheckUpdateWorker" 52 | } 53 | } -------------------------------------------------------------------------------- /app/src/main/res/xml/status_bar_preference.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 9 | 10 | 17 | 18 | 28 | 29 | 37 | 38 | 48 | 49 | -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/worker/SeedAppDatabaseWorker.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.worker 2 | 3 | import android.content.Context 4 | import android.content.pm.ApplicationInfo 5 | import androidx.work.CoroutineWorker 6 | import androidx.work.WorkerParameters 7 | import io.ikws4.weiju.data.AppDatabase 8 | import io.ikws4.weiju.data.AppInfo 9 | import io.ikws4.weiju.data.User 10 | import io.ikws4.weiju.utilities.LogcatManager 11 | import kotlinx.coroutines.coroutineScope 12 | import saveToFile 13 | 14 | /** 15 | * 创建Database时,执行该任务 16 | * @constructor 17 | */ 18 | class SeedAppDatabaseWorker(context: Context, workerParameters: WorkerParameters) : 19 | CoroutineWorker(context, workerParameters) { 20 | 21 | override suspend fun doWork(): Result = coroutineScope { 22 | try { 23 | val data = arrayListOf() 24 | val pm = applicationContext.packageManager 25 | val appDatabase = AppDatabase.getInstance(applicationContext) 26 | // 初始化用户数据 27 | appDatabase.userDao().insert(User((0))) 28 | 29 | pm.getInstalledPackages(0).forEach { 30 | val path = "${applicationContext.filesDir.path}/application_icons/" 31 | val fileName = "${it.packageName}.png" 32 | val fullPath = path + fileName 33 | 34 | data.add( 35 | AppInfo( 36 | pkgName = it.packageName, 37 | name = it.applicationInfo.loadLabel(pm).toString(), 38 | iconPath = fullPath, 39 | isSystemApp = (it.applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM) != 0 40 | ) 41 | ) 42 | // 把图标保存到 内置的files/application_icon文件夹下 43 | it.applicationInfo.loadIcon(pm).saveToFile(fileName, path) 44 | } 45 | appDatabase.appInfoDao().insert(data) 46 | Result.success() 47 | } catch (ex: Exception) { 48 | LogcatManager.saveToFile(applicationContext, ex) 49 | Result.retry() 50 | } 51 | } 52 | 53 | companion object { 54 | const val TAG = "SeedAppDatabaseWorker" 55 | } 56 | } -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/WeiJuApplication.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju 2 | 3 | import android.app.Application 4 | import android.content.Context 5 | import android.content.Intent 6 | import androidx.appcompat.app.AppCompatDelegate 7 | import androidx.core.content.ContextCompat 8 | import androidx.work.Constraints 9 | import androidx.work.NetworkType 10 | import androidx.work.OneTimeWorkRequestBuilder 11 | import androidx.work.WorkManager 12 | import com.google.android.gms.ads.MobileAds 13 | import io.ikws4.weiju.servers.ApkServer 14 | import io.ikws4.weiju.utilities.WEIJU_SP 15 | import io.ikws4.weiju.worker.CheckUpdateWorker 16 | import io.ikws4.weiju.worker.UpdateAppDatabaseWorker 17 | 18 | class WeiJuApplication : Application() { 19 | 20 | override fun onCreate() { 21 | super.onCreate() 22 | val intent = Intent(this, ApkServer::class.java) 23 | ContextCompat.startForegroundService(this, intent) 24 | MobileAds.initialize(this) 25 | startWorkers() 26 | themeChangeListener() 27 | } 28 | 29 | private fun startWorkers() { 30 | val workManager = WorkManager.getInstance(applicationContext) 31 | 32 | val checkUpdateWorkerConstraints = Constraints.Builder() 33 | .setRequiredNetworkType(NetworkType.CONNECTED) 34 | .build() 35 | 36 | val checkUpdateWorker = OneTimeWorkRequestBuilder() 37 | .setConstraints(checkUpdateWorkerConstraints) 38 | .build() 39 | 40 | val updateAppDatabaseWorker = OneTimeWorkRequestBuilder() 41 | .build() 42 | 43 | workManager.enqueue(updateAppDatabaseWorker) 44 | workManager.enqueue(checkUpdateWorker) 45 | } 46 | 47 | private fun themeChangeListener() { 48 | val sp = getSharedPreferences(WEIJU_SP, Context.MODE_PRIVATE) 49 | AppCompatDelegate.setDefaultNightMode(sp.getString("theme", "-1")!!.toInt()) 50 | sp.registerOnSharedPreferenceChangeListener { sharedPreferences, key -> 51 | if (key == "theme") { 52 | AppCompatDelegate.setDefaultNightMode(sharedPreferences.getString("theme", "-1")!!.toInt()) 53 | } 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /app/src/main/res/xml/screen_preference.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 9 | 10 | 20 | 21 | 28 | 29 | 39 | 40 | 45 | 46 | 51 | 52 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_language.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_settings_applications.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/worker/UpdateAppDatabaseWorker.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.worker 2 | 3 | import android.content.Context 4 | import android.content.pm.ApplicationInfo 5 | import android.content.pm.PackageInfo 6 | import androidx.work.CoroutineWorker 7 | import androidx.work.WorkerParameters 8 | import io.ikws4.weiju.data.AppDatabase 9 | import io.ikws4.weiju.data.AppInfo 10 | import io.ikws4.weiju.utilities.LogcatManager 11 | import kotlinx.coroutines.coroutineScope 12 | import saveToFile 13 | 14 | class UpdateAppDatabaseWorker(ctx: Context, workerParameters: WorkerParameters) : CoroutineWorker(ctx, workerParameters) { 15 | 16 | override suspend fun doWork(): Result = coroutineScope { 17 | try { 18 | val appInfoDao = AppDatabase.getInstance(applicationContext).appInfoDao() 19 | val pm = applicationContext.packageManager 20 | val data = arrayListOf() 21 | 22 | pm.getInstalledPackages(0).forEach { packageInfo: PackageInfo -> 23 | val appInfo = appInfoDao.getByPkgName(packageInfo.packageName) 24 | 25 | if (appInfo == null) { 26 | val path = "${applicationContext.filesDir.path}/application_icons/" 27 | val fileName = "${packageInfo.packageName}.png" 28 | val fullPath = path + fileName 29 | 30 | data.add( 31 | AppInfo( 32 | pkgName = packageInfo.packageName, 33 | name = packageInfo.applicationInfo.loadLabel(pm).toString(), 34 | iconPath = fullPath, 35 | isSystemApp = (packageInfo.applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM) != 0 36 | ) 37 | ) 38 | 39 | packageInfo.applicationInfo.loadIcon(pm).saveToFile(fileName, path) 40 | 41 | } else if (appInfoDao.getByPkgName(packageInfo.packageName) != null) { 42 | appInfoDao.update( 43 | appInfo.copy( 44 | isDelete = false 45 | ) 46 | ) 47 | } 48 | } 49 | appInfoDao.insert(data) 50 | Result.success() 51 | } catch (e: Exception) { 52 | LogcatManager.saveToFile(applicationContext, e) 53 | Result.retry() 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_main_home.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 11 | 12 | 15 | 16 | 19 | 20 | 21 | 22 | 26 | 27 | 31 | 32 | 37 | 38 | 43 | 44 | 48 | 49 | 50 | 51 | 52 | 53 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_spa.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 14 | 17 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_selectapp.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 28 | 29 | 44 | 45 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /app/src/main/java/Collections.kt: -------------------------------------------------------------------------------- 1 | import android.content.Context 2 | import android.content.SharedPreferences 3 | import android.graphics.Bitmap 4 | import android.graphics.drawable.Drawable 5 | import androidx.core.content.edit 6 | import androidx.core.graphics.drawable.toBitmap 7 | import java.io.File 8 | import java.io.FileOutputStream 9 | import java.security.MessageDigest 10 | 11 | /** 12 | * 将Drawable保存为Png文件 13 | * @receiver Drawable 14 | * @param fileName String 15 | * @param path String 16 | */ 17 | fun Drawable.saveToFile(fileName: String, path: String) { 18 | val iconDir = File(path) 19 | if (!iconDir.exists()) iconDir.mkdirs() 20 | val icon = File(iconDir, fileName) 21 | val bitmap = toBitmap() 22 | val outStream = FileOutputStream(icon) 23 | bitmap.compress(Bitmap.CompressFormat.PNG, 100, outStream) 24 | outStream.flush() 25 | outStream.close() 26 | } 27 | 28 | /** 29 | * 替换SharedPReferences中的数据 30 | * @receiver SharedPreferences 31 | * @param values MutableMap 32 | */ 33 | fun SharedPreferences.replaceAll(values: MutableMap) { 34 | this.edit { 35 | values.forEach { 36 | when (val value = it.value) { 37 | is Boolean -> this.putBoolean(it.key, value) 38 | is String -> this.putString(it.key, value) 39 | is Float -> this.putFloat(it.key, value) 40 | is Long -> this.putLong(it.key, value) 41 | is Int -> this.putInt(it.key, value) 42 | } 43 | } 44 | } 45 | } 46 | 47 | /** 48 | * 获取应用shared_prefs文件夹 49 | * 例如:/data/data/com.example.app/shared_prefs 50 | * @receiver Context 51 | * @return File 52 | */ 53 | fun Context.sharedPrefsDir(): File { 54 | return File(dataRootDir(), "shared_prefs") 55 | } 56 | 57 | /** 58 | * 获取应用私有数据目录 59 | * 例如:/data/data/com.example.app/ 60 | * @receiver Context 61 | * @return File 62 | */ 63 | fun Context.dataRootDir(): File = File(filesDir.parentFile.path) 64 | 65 | /** 66 | * 将任意字符转换为MD5 67 | * @receiver String 68 | * @return String 69 | */ 70 | fun String.toMD5(): String { 71 | val bytes = MessageDigest.getInstance("MD5").digest(toByteArray()) 72 | return bytes.toHex() 73 | } 74 | 75 | /** 76 | * 转化为SHA256 77 | * @receiver String 78 | * @return String 79 | */ 80 | fun String.toSHA256(): String { 81 | val bytes = MessageDigest.getInstance("SHA-256").digest(toByteArray()) 82 | return bytes.fold("", { str, it -> str + "%02x".format(it) }) 83 | } 84 | 85 | /** 86 | * 将二进制数组转16进制 87 | * @receiver ByteArray 88 | * @return String 89 | */ 90 | fun ByteArray.toHex(): String { 91 | return joinToString("") { "%02x".format(it) } 92 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/item_home_module_state.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 11 | 12 | 15 | 16 | 17 | 18 | 22 | 23 | 28 | 29 | 33 | 34 | 39 | 40 | 52 | 53 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/ui/activitys/SettingActivity.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.ui.activitys 2 | 3 | import android.os.Bundle 4 | import android.view.MenuItem 5 | import androidx.databinding.DataBindingUtil.setContentView 6 | import androidx.navigation.NavController 7 | import androidx.navigation.findNavController 8 | import androidx.navigation.navArgs 9 | import com.google.android.material.appbar.MaterialToolbar 10 | import io.ikws4.weiju.R 11 | import io.ikws4.weiju.databinding.ActivitySettingBinding 12 | 13 | class SettingActivity : BasicActivity() { 14 | private val args: SettingActivityArgs by navArgs() 15 | 16 | override fun onCreate(savedInstanceState: Bundle?) { 17 | super.onCreate(savedInstanceState) 18 | val binding = setContentView(this, R.layout.activity_setting) 19 | val navController = findNavController(R.id.nav_host_fragment) 20 | val toolbar = binding.actionBar.toolbar 21 | setupToolbarWithNav(navController, toolbar) 22 | navigation(navController) 23 | } 24 | 25 | /** 26 | * 用来导航到目标Fragment,方便切换 27 | * @param navController NavController 28 | */ 29 | private fun navigation(navController: NavController) { 30 | val graph = navController.graph.apply { 31 | startDestination = when (args.pageName) { 32 | HOME -> R.id.settingHomeFragment 33 | GENERAL -> R.id.settingGeneralFragment 34 | TEMPLATE -> R.id.settingTemplateFragment 35 | TRANSLATION -> R.id.settingTranslationFragment 36 | else -> R.id.settingHomeFragment 37 | } 38 | } 39 | navController.graph = graph 40 | } 41 | 42 | /** 43 | * 设置Toolbar 44 | * 把destination的label属性作为toolbar的子标题 45 | * @param navController NavController 46 | * @param toolbar MaterialToolbar 47 | */ 48 | private fun setupToolbarWithNav(navController: NavController, toolbar: MaterialToolbar) { 49 | setSupportActionBar(toolbar) 50 | with(navController) { 51 | addOnDestinationChangedListener { _, destination, _ -> 52 | toolbar.subtitle = destination.label 53 | } 54 | } 55 | } 56 | 57 | override fun onOptionsItemSelected(item: MenuItem): Boolean { 58 | return when (item.itemId) { 59 | else -> super.onOptionsItemSelected(item) 60 | } 61 | } 62 | 63 | companion object { 64 | const val HOME = "/" 65 | const val GENERAL = "/general" 66 | const val TEMPLATE = "/template" 67 | const val TRANSLATION = "/translation" 68 | const val ABOUT = "/about" 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baidu.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/xml/variable_preference.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 15 | 16 | 21 | 22 | 27 | 28 | 33 | 34 | 39 | 40 | 46 | 47 | 53 | 54 | 60 | 61 | 67 | 68 | -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/utilities/LogcatManager.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.utilities 2 | 3 | import android.content.ClipData 4 | import android.content.ClipboardManager 5 | import android.content.Context 6 | import android.util.Log 7 | import com.google.android.material.dialog.MaterialAlertDialogBuilder 8 | import io.ikws4.weiju.R 9 | import java.io.File 10 | 11 | 12 | object LogcatManager { 13 | 14 | fun show(context: Context, t: Throwable) { 15 | val message = Log.getStackTraceString(t) 16 | MaterialAlertDialogBuilder(context) 17 | .setTitle(R.string.error_message) 18 | .setMessage(message) 19 | .setNegativeButton(android.R.string.cancel, null) 20 | .setPositiveButton(android.R.string.copy) { _, _ -> 21 | val cbm = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager 22 | val clipData = ClipData.newPlainText("LogcatManager", message) 23 | cbm.primaryClip = clipData 24 | }.show() 25 | saveToFile(context, t) 26 | } 27 | 28 | fun show(context: Context, s: String, isShowClearButton: Boolean = false) { 29 | val builder = MaterialAlertDialogBuilder(context) 30 | .setTitle(R.string.error_message) 31 | .setMessage(s) 32 | .setNegativeButton(android.R.string.cancel, null) 33 | .setPositiveButton(android.R.string.copy) { _, _ -> 34 | val cbm = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager 35 | val clipData = ClipData.newPlainText("LogcatManager", s) 36 | cbm.primaryClip = clipData 37 | } 38 | if (isShowClearButton) { 39 | builder.setNeutralButton(R.string.clear) { _, _ -> 40 | deleteLogFile(context) 41 | } 42 | } 43 | builder.show() 44 | } 45 | 46 | private fun deleteLogFile(context: Context) { 47 | val file = File(context.filesDir, "logcat.txt") 48 | if (file.exists()) { 49 | file.delete() 50 | } 51 | } 52 | 53 | fun saveToFile(context: Context, t: Throwable, showLog: Boolean = false) { 54 | if (showLog) { 55 | t.printStackTrace() 56 | } 57 | val file = File(context.filesDir, "logcat.txt") 58 | with(file) { 59 | if (!exists()) { 60 | createNewFile() 61 | } 62 | appendText(Log.getStackTraceString(t)) 63 | } 64 | } 65 | 66 | fun getSavedLog(context: Context): String { 67 | val file = File(context.filesDir, "logcat.txt") 68 | with(file) { 69 | if (!exists()) { 70 | createNewFile() 71 | } 72 | return file.readText() 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/data/AppDatabase.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.data 2 | 3 | import android.content.Context 4 | import android.widget.EditText 5 | import androidx.annotation.Keep 6 | import androidx.room.Database 7 | import androidx.room.Room 8 | import androidx.room.RoomDatabase 9 | import androidx.room.migration.Migration 10 | import androidx.sqlite.db.SupportSQLiteDatabase 11 | import androidx.work.OneTimeWorkRequestBuilder 12 | import androidx.work.WorkManager 13 | import com.commonsware.cwac.saferoom.SafeHelperFactory 14 | import io.ikws4.weiju.utilities.DATABASE_NAME 15 | import io.ikws4.weiju.utilities.WEIJU_PKG_NAME 16 | import io.ikws4.weiju.worker.SeedAppDatabaseWorker 17 | 18 | @Database(entities = [AppInfo::class, TranslationInfo::class, User::class], version = 2, exportSchema = false) 19 | abstract class AppDatabase : RoomDatabase() { 20 | 21 | abstract fun appInfoDao(): AppInfoDao 22 | 23 | abstract fun translationInfoDao(): TranslationDao 24 | 25 | abstract fun userDao(): UserDao 26 | 27 | companion object { 28 | @Volatile 29 | private var instance: AppDatabase? = null 30 | 31 | fun getInstance(context: Context): AppDatabase { 32 | return instance ?: synchronized(this) { 33 | instance ?: buildDatabase(context).also { instance = it } 34 | } 35 | } 36 | 37 | private val MIGRATION_1_2 = object : Migration(1, 2) { 38 | override fun migrate(database: SupportSQLiteDatabase) { 39 | database.execSQL("CREATE TABLE translation_info (id INTEGER NOT NULL, pkgName TEXT NOT NULL, `query` TEXT NOT NULL, `from` TEXT NOT NULL, `to` TEXT NOT NULL, result TEXT NOT NULL, PRIMARY KEY(id))") 40 | database.execSQL("CREATE TABLE user (freeSogouApiAmount INTEGER NOT NULL)") 41 | } 42 | } 43 | 44 | @Keep 45 | private fun buildDatabase(context: Context): AppDatabase { 46 | val editText = EditText(context) 47 | editText.setText(WEIJU_PKG_NAME) 48 | val safeHelperFactory = SafeHelperFactory.fromUser(editText.text) 49 | 50 | return Room.databaseBuilder(context, AppDatabase::class.java, DATABASE_NAME) 51 | .openHelperFactory(safeHelperFactory) 52 | .addMigrations(MIGRATION_1_2) 53 | .addCallback(object : RoomDatabase.Callback() { 54 | override fun onCreate(db: SupportSQLiteDatabase) { 55 | super.onCreate(db) 56 | val result = OneTimeWorkRequestBuilder() 57 | .addTag(SeedAppDatabaseWorker.TAG) 58 | .build() 59 | WorkManager.getInstance(context).enqueue(result) 60 | } 61 | }).build() 62 | } 63 | } 64 | } 65 | 66 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 21 | 24 | 25 | 28 | 29 | 36 | 37 | 38 | 41 | 42 | 48 | 49 | 52 | 53 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 21 | 24 | 25 | 28 | 29 | 36 | 37 | 38 | 41 | 42 | 48 | 49 | 52 | 53 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_settings.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_home_hooking_apps.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 11 | 12 | 13 | 14 | 18 | 19 | 24 | 25 | 28 | 29 | 32 | 33 | 41 | 42 | 52 | 53 | 58 | 59 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/xposed/MainHook.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.xposed 2 | 3 | import android.app.Application 4 | import android.app.Instrumentation 5 | import android.content.Context 6 | import android.util.Log 7 | import androidx.annotation.Keep 8 | import de.robv.android.xposed.IXposedHookLoadPackage 9 | import de.robv.android.xposed.XposedBridge 10 | import de.robv.android.xposed.callbacks.XC_LoadPackage 11 | import io.ikws4.library.xposedktx.hookMethod 12 | import io.ikws4.weiju.BuildConfig 13 | import io.ikws4.weiju.utilities.HOOK_LIST_SP 14 | import io.ikws4.weiju.utilities.WEIJU_PKG_NAME 15 | import io.ikws4.weiju.utilities.WEIJU_SP 16 | import io.ikws4.weiju.utilities.XSPUtils 17 | import kotlinx.coroutines.ExperimentalCoroutinesApi 18 | 19 | @Keep 20 | @ExperimentalCoroutinesApi 21 | class MainHook : IXposedHookLoadPackage { 22 | private var isRunning = false 23 | 24 | override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) { 25 | val pkgName = lpparam.packageName 26 | 27 | Instrumentation::class.java.hookMethod("callApplicationOnCreate", Application::class.java) { param -> 28 | val application = param.args[0] as Application 29 | // 刷新模块状态 30 | setModuleState(application, lpparam) 31 | // Hook在Hook_List_SP名单中的应用 32 | val hookListSP = XSPUtils(application, HOOK_LIST_SP) 33 | 34 | if (hookListSP.getBoolean(pkgName)) { 35 | val sp = XSPUtils(application, pkgName) 36 | StatusBarHook(sp) 37 | NavBarHook(sp) 38 | ScreenHook(sp) 39 | TranslationHook(application, pkgName) 40 | VariableHook(sp,lpparam.classLoader) 41 | } 42 | isRunning = true 43 | } 44 | } 45 | 46 | /** 47 | * 刷新模块状态(UPDATE ACTIVE WARNING) 48 | * @param context Context 49 | * @param lpparam LoadPackageParam 50 | */ 51 | private fun setModuleState(context: Context, lpparam: XC_LoadPackage.LoadPackageParam) { 52 | if (lpparam.packageName == WEIJU_PKG_NAME) { 53 | val className = "$WEIJU_PKG_NAME.ui.fragments.MainHomeFragment" 54 | className.hookMethod(lpparam.classLoader, "refreshModuleState", String::class.java, 55 | beforeHookedMethod = { 56 | val weiJuSP = XSPUtils(context, WEIJU_SP) 57 | if (BuildConfig.VERSION_NAME < weiJuSP.getString("version_name")) 58 | it.args[0] = "UPDATE" 59 | else 60 | it.args[0] = "ACTIVE" 61 | }) 62 | } 63 | } 64 | 65 | 66 | companion object { 67 | fun log(text: String) { 68 | if (BuildConfig.DEBUG) { 69 | XposedBridge.log("WeiJu-> $text") 70 | } 71 | } 72 | 73 | fun log(t: Throwable) { 74 | if (BuildConfig.DEBUG) { 75 | XposedBridge.log("WeiJu-> ${Log.getStackTraceString(t)}") 76 | } 77 | } 78 | } 79 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 14 | 15 | 21 | 22 | 26 | 27 | 31 | 32 | 39 | 40 | 44 | 45 | 46 | 54 | 55 | 56 | 57 | 58 | 59 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /app/src/main/res/navigation/nav_graph_setting.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 10 | 17 | 24 | 31 | 38 | 39 | 40 | 44 | 47 | 48 | 49 | 52 | 55 | 58 | 59 | 60 | 64 | 65 | 69 | 70 | 74 | 75 | -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/ui/fragments/CategoryTranslationFragment.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.ui.fragments 2 | 3 | import android.os.Bundle 4 | import android.view.Menu 5 | import android.view.MenuInflater 6 | import android.view.MenuItem 7 | import android.view.View 8 | import androidx.navigation.fragment.findNavController 9 | import androidx.navigation.fragment.navArgs 10 | import androidx.preference.Preference 11 | import androidx.preference.PreferenceFragmentCompat 12 | import com.google.android.material.dialog.MaterialAlertDialogBuilder 13 | import io.ikws4.weiju.R 14 | import io.ikws4.weiju.ui.activitys.SettingActivity 15 | import io.ikws4.weiju.utilities.SPManager 16 | 17 | class CategoryTranslationFragment : PreferenceFragmentCompat() { 18 | private val args: CategoryTranslationFragmentArgs by navArgs() 19 | private val spManager by lazy { SPManager.getInstance(context!!) } 20 | 21 | override fun onCreate(savedInstanceState: Bundle?) { 22 | super.onCreate(savedInstanceState) 23 | setHasOptionsMenu(true) 24 | } 25 | 26 | override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { 27 | preferenceManager.sharedPreferencesName = args.pkgName 28 | setPreferencesFromResource(R.xml.translation_preference, rootKey) 29 | } 30 | 31 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 32 | super.onViewCreated(view, savedInstanceState) 33 | 34 | // 提醒设置appid和key的对话框 35 | if (spManager.WeiJuSP().isCategoryTranslationFragmentRemindShow) 36 | MaterialAlertDialogBuilder(context!!) 37 | .setTitle(R.string.warning) 38 | .setMessage(R.string.set_translation_appid_key_remind) 39 | .setCancelable(false) 40 | .setPositiveButton(R.string.go) { _, _ -> 41 | findNavController().navigate(CategoryTranslationFragmentDirections.toSettingActivity(SettingActivity.TRANSLATION)) 42 | } 43 | .setNegativeButton(android.R.string.cancel, null) 44 | .setNeutralButton(getString(R.string.not_remind_again)) { _, _ -> 45 | // 禁止再次显示 46 | spManager.WeiJuSP().isCategoryTranslationFragmentRemindShow = false 47 | }.show() 48 | } 49 | 50 | override fun onPreferenceTreeClick(preference: Preference): Boolean { 51 | return when (preference.key) { 52 | "translation_data_files" -> { 53 | findNavController().navigate( 54 | CategoryTranslationFragmentDirections 55 | .toTranslationEditorActivity(args.pkgName) 56 | ) 57 | true 58 | } 59 | else -> super.onPreferenceTreeClick(preference) 60 | } 61 | } 62 | 63 | override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { 64 | super.onCreateOptionsMenu(menu, inflater) 65 | inflater.inflate(R.menu.menu_fragment_category_translation, menu) 66 | } 67 | 68 | override fun onOptionsItemSelected(item: MenuItem): Boolean { 69 | return when (item.itemId) { 70 | R.id.action_setting -> { 71 | findNavController().navigate(CategoryTranslationFragmentDirections.toSettingActivity(SettingActivity.TRANSLATION)) 72 | true 73 | } 74 | else -> super.onOptionsItemSelected(item) 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/xposed/StatusBarHook.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.xposed 2 | 3 | import android.R 4 | import android.app.Activity 5 | import android.graphics.Color 6 | import android.os.Build 7 | import android.os.Bundle 8 | import android.util.TypedValue 9 | import android.view.View 10 | import android.view.WindowManager.LayoutParams 11 | import android.widget.Toast 12 | import de.robv.android.xposed.XSharedPreferences 13 | import io.ikws4.library.xposedktx.hookMethod 14 | import io.ikws4.weiju.utilities.XSPUtils 15 | 16 | class StatusBarHook(sp: XSPUtils) { 17 | private val isEnable = sp.getBoolean("is_enable_status_bar") 18 | private val isHide = sp.getBoolean("is_hide_status_bar") 19 | private val immersive = sp.getString("immersive_status_bar") 20 | private val customColor = sp.getString("custom_status_bar_color") 21 | private val iconColor = sp.getString("status_bar_icon_color") 22 | 23 | init { 24 | if (isEnable) { 25 | Activity::class.java.hookMethod("onCreate", Bundle::class.java, afterHookedMethod = { 26 | with(window) { 27 | if (immersive != "Default") { 28 | // 沉浸 29 | val typedValue = TypedValue() 30 | when (immersive) { 31 | "ColorPrimary" -> { 32 | theme.resolveAttribute(R.attr.colorPrimary, typedValue, true) 33 | } 34 | "ColorPrimaryDark" -> { 35 | theme.resolveAttribute(R.attr.colorPrimaryDark, typedValue, true) 36 | } 37 | "ColorAccent" -> { 38 | theme.resolveAttribute(R.attr.colorAccent, typedValue, true) 39 | } 40 | "Custom" -> { 41 | try { 42 | val color = Color.parseColor(customColor) 43 | statusBarColor = color 44 | } catch (ex: Exception) { 45 | ex.printStackTrace() 46 | Toast.makeText(this@hookMethod, "WeiJu: ${ex.message}", Toast.LENGTH_SHORT).show() 47 | } 48 | } 49 | } 50 | statusBarColor = typedValue.data 51 | } 52 | 53 | 54 | // 显隐状态栏 55 | if (isHide) { 56 | addFlags(LayoutParams.FLAG_FULLSCREEN) 57 | } else { 58 | clearFlags(LayoutParams.FLAG_FULLSCREEN) 59 | } 60 | 61 | // 图标颜色 62 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 63 | val flag = decorView.systemUiVisibility 64 | 65 | when (iconColor) { 66 | "Grey" -> { 67 | decorView.systemUiVisibility = flag or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR 68 | } 69 | "White" -> { 70 | decorView.systemUiVisibility = flag xor View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR 71 | } 72 | } 73 | } 74 | } 75 | }) 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/provider/SharedPreferencesProvider.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.provider 2 | 3 | import android.content.ContentProvider 4 | import android.content.ContentValues 5 | import android.content.Context 6 | import android.database.Cursor 7 | import android.net.Uri 8 | import android.os.Bundle 9 | import androidx.core.content.edit 10 | 11 | class SharedPreferencesProvider : ContentProvider() { 12 | 13 | /*******************************这些方法不用管******************************/ 14 | 15 | override fun insert(uri: Uri, values: ContentValues?): Uri? = null 16 | 17 | override fun query( 18 | uri: Uri, 19 | projection: Array?, 20 | selection: String?, 21 | selectionArgs: Array?, 22 | sortOrder: String? 23 | ): Cursor? = null 24 | 25 | override fun onCreate(): Boolean = false 26 | 27 | override fun update( 28 | uri: Uri, 29 | values: ContentValues?, 30 | selection: String?, 31 | selectionArgs: Array? 32 | ): Int = 0 33 | 34 | override fun delete(uri: Uri, selection: String?, selectionArgs: Array?): Int = 0 35 | 36 | override fun getType(uri: Uri): String? = null 37 | 38 | /*******************************这些方法不用管******************************/ 39 | 40 | companion object { 41 | const val EXTRA_KEY = "key" 42 | const val EXTRA_VALUE = "value" 43 | 44 | const val METHOD_PUT_STRING = "put_string" 45 | const val METHOD_GET_STRING = "get_string" 46 | 47 | const val METHOD_PUT_INT = "put_int" 48 | const val METHOD_GET_INT = "get_int" 49 | 50 | const val METHOD_PUT_BOOLEAN = "put_boolean" 51 | const val METHOD_GET_BOOLEAN = "get_boolean" 52 | 53 | // 如果还需要别的类型,在继续加 54 | } 55 | 56 | /** 57 | * 我们只需要这个call方法 58 | * @param method String 需要调用的方法 59 | * @param arg String? SharedPreferences的文件名称 60 | * @param extras Bundle? 通过Bundle传输数据 61 | * @return Bundle? 62 | */ 63 | override fun call(method: String, arg: String?, extras: Bundle?): Bundle? { 64 | 65 | val bundle = Bundle() 66 | val sharedPreferences = context!!.getSharedPreferences(arg, Context.MODE_PRIVATE) 67 | 68 | val key = extras!!.getString(EXTRA_KEY) 69 | 70 | when (method) { 71 | METHOD_PUT_STRING -> { 72 | sharedPreferences.edit { 73 | val value = extras.getString(EXTRA_VALUE) 74 | putString(key, value) 75 | } 76 | } 77 | METHOD_GET_STRING -> { 78 | bundle.putString(EXTRA_VALUE, sharedPreferences.getString(key, "")) 79 | } 80 | METHOD_PUT_INT -> { 81 | sharedPreferences.edit { 82 | val value = extras.getInt(EXTRA_VALUE) 83 | putInt(key, value) 84 | } 85 | } 86 | METHOD_GET_INT -> { 87 | bundle.putInt(EXTRA_VALUE, sharedPreferences.getInt(key, 0)) 88 | } 89 | METHOD_PUT_BOOLEAN -> { 90 | sharedPreferences.edit { 91 | val value = extras.getBoolean(EXTRA_VALUE) 92 | putBoolean(key, value) 93 | } 94 | } 95 | METHOD_GET_BOOLEAN -> { 96 | bundle.putBoolean(EXTRA_VALUE, sharedPreferences.getBoolean(key, false)) 97 | } 98 | } 99 | 100 | return if (bundle.isEmpty) { 101 | super.call(method, arg, extras) 102 | } else { 103 | bundle 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/servers/ApkServer.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.servers 2 | 3 | import android.app.* 4 | import android.content.Context 5 | import android.content.Intent 6 | import android.content.IntentFilter 7 | import android.graphics.BitmapFactory 8 | import android.os.Build 9 | import android.os.IBinder 10 | import androidx.annotation.RequiresApi 11 | import androidx.core.app.NotificationCompat 12 | import io.ikws4.weiju.R 13 | import io.ikws4.weiju.broadcasts.ApkReceiver 14 | import io.ikws4.weiju.ui.activitys.MainActivity 15 | 16 | 17 | class ApkServer : Service() { 18 | companion object { 19 | const val APK_SERVER_NOTIFICATION_ID = 1 20 | const val CHANNEL_ID = "apk foreground notification" 21 | const val CHANNEL_NAME = "apk listener" 22 | } 23 | 24 | private lateinit var apkReceiver: ApkReceiver 25 | 26 | override fun onCreate() { 27 | apkReceiver = ApkReceiver() 28 | val intentFilter = IntentFilter().apply { 29 | addAction(Intent.ACTION_PACKAGE_ADDED) 30 | addAction(Intent.ACTION_PACKAGE_REMOVED) 31 | addDataScheme("package") 32 | } 33 | registerReceiver(apkReceiver, intentFilter) 34 | super.onCreate() 35 | } 36 | 37 | override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { 38 | 39 | // TODO: 待优化 40 | 41 | val mainIntent = Intent(this, MainActivity::class.java) 42 | val pendingIntent = PendingIntent.getActivity(this, 1, mainIntent, 0) 43 | 44 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 45 | createNotificationChannel() 46 | val builder = NotificationCompat.Builder(this, CHANNEL_ID) 47 | .setSmallIcon(R.drawable.ic_face) 48 | .setLargeIcon(BitmapFactory.decodeResource(resources, R.drawable.ic_face)) 49 | .setContentText(getString(R.string.foreground_notification)) 50 | .setContentIntent(pendingIntent) 51 | .setAutoCancel(true) 52 | 53 | val notification = builder.build() 54 | startForeground(APK_SERVER_NOTIFICATION_ID, notification) 55 | } else { 56 | val builder = NotificationCompat.Builder(this) 57 | .setSmallIcon(R.drawable.ic_face) 58 | .setLargeIcon(BitmapFactory.decodeResource(resources, R.drawable.ic_face)) 59 | .setContentText(getString(R.string.foreground_notification)) 60 | .setPriority(NotificationCompat.PRIORITY_DEFAULT) 61 | .setContentIntent(pendingIntent) 62 | .setAutoCancel(true) 63 | 64 | val notification = builder.build() 65 | startForeground(APK_SERVER_NOTIFICATION_ID, notification) 66 | } 67 | return super.onStartCommand(intent, flags, startId) 68 | } 69 | 70 | override fun onBind(p0: Intent?): IBinder? { 71 | throw UnsupportedOperationException("Not yet implemented") 72 | } 73 | 74 | override fun onDestroy() { 75 | stopForeground(true) 76 | unregisterReceiver(apkReceiver) 77 | super.onDestroy() 78 | } 79 | 80 | @RequiresApi(Build.VERSION_CODES.O) 81 | private fun createNotificationChannel() { 82 | val channel = 83 | NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_NONE).apply { 84 | lockscreenVisibility = Notification.VISIBILITY_PUBLIC 85 | enableLights(true) 86 | lightColor = getColor(R.color.colorAccent) 87 | 88 | } 89 | val server = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager 90 | server.createNotificationChannel(channel) 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/utilities/XSPUtils.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.utilities 2 | 3 | import android.content.Context 4 | import android.net.Uri 5 | import android.os.Bundle 6 | import io.ikws4.weiju.provider.SharedPreferencesProvider 7 | 8 | /** 9 | * 对SharePreferencesProvider的一个封装 10 | * 11 | * 用法: 12 | * val spHelper = XSPUtils(context,"Your sharePreference file name") 13 | * val value = spHelper.getString("there is a key") 14 | * 15 | * @property context Context 16 | * @property prefName String SharePreferences件名称 17 | * @property uri (android.net.Uri..android.net.Uri?) 18 | * @constructor 19 | */ 20 | class XSPUtils(private val context: Context, private val prefName: String) { 21 | 22 | private val uri = Uri.parse("content://io.ikws4.weiju.provider.SharedPreferencesProvider") 23 | 24 | /** 25 | * 更新SP文件中的某个String 26 | * @param key String 27 | * @param value String 28 | */ 29 | fun putString(key: String, value: String) { 30 | val data = Bundle() 31 | data.putString(SharedPreferencesProvider.EXTRA_KEY, key) 32 | data.putString(SharedPreferencesProvider.EXTRA_VALUE, value) 33 | 34 | context.contentResolver.call( 35 | uri, 36 | SharedPreferencesProvider.METHOD_PUT_STRING, 37 | prefName, 38 | data 39 | ) 40 | } 41 | 42 | /** 43 | * 传入你的key,然后获取相应的value 44 | * @param key String 45 | * @return String 46 | */ 47 | fun getString(key: String): String { 48 | val data = Bundle() 49 | data.putString(SharedPreferencesProvider.EXTRA_KEY, key) 50 | 51 | val bundle = context.contentResolver.call( 52 | uri, 53 | SharedPreferencesProvider.METHOD_GET_STRING, 54 | prefName, 55 | data 56 | )!! 57 | 58 | return bundle.getString(SharedPreferencesProvider.EXTRA_VALUE, "") 59 | } 60 | 61 | fun putInt(key: String, value: Int) { 62 | val data = Bundle() 63 | data.putString(SharedPreferencesProvider.EXTRA_KEY, key) 64 | data.putInt(SharedPreferencesProvider.EXTRA_VALUE, value) 65 | 66 | context.contentResolver.call( 67 | uri, 68 | SharedPreferencesProvider.METHOD_PUT_INT, 69 | prefName, 70 | data 71 | ) 72 | } 73 | 74 | fun getInt(key: String): Int { 75 | val data = Bundle() 76 | data.putString(SharedPreferencesProvider.EXTRA_KEY, key) 77 | 78 | val bundle = context.contentResolver.call( 79 | uri, 80 | SharedPreferencesProvider.METHOD_GET_INT, 81 | prefName, 82 | data 83 | )!! 84 | 85 | return bundle.getInt(SharedPreferencesProvider.EXTRA_VALUE, 0) 86 | } 87 | 88 | fun putBoolean(key: String, value: Boolean) { 89 | val data = Bundle() 90 | data.putString(SharedPreferencesProvider.EXTRA_KEY, key) 91 | data.putBoolean(SharedPreferencesProvider.EXTRA_VALUE, value) 92 | 93 | context.contentResolver.call( 94 | uri, 95 | SharedPreferencesProvider.METHOD_PUT_BOOLEAN, 96 | prefName, 97 | data 98 | ) 99 | } 100 | 101 | fun getBoolean(key: String): Boolean { 102 | val data = Bundle() 103 | data.putString(SharedPreferencesProvider.EXTRA_KEY, key) 104 | 105 | val bundle = context.contentResolver.call( 106 | uri, 107 | SharedPreferencesProvider.METHOD_GET_BOOLEAN, 108 | prefName, 109 | data 110 | )!! 111 | 112 | return bundle.getBoolean(SharedPreferencesProvider.EXTRA_VALUE, false) 113 | } 114 | 115 | fun getContext() = context 116 | } -------------------------------------------------------------------------------- /app/src/main/res/xml/translation_preference.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 9 | 10 | 18 | 19 | 25 | 26 | 30 | 31 | 38 | 39 | 46 | 47 | 48 | 49 | 53 | 54 | 61 | 62 | 69 | 70 | 71 | 72 | 76 | 77 | 84 | 85 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /app/src/main/res/values-zh-rCN/arrays.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 默认 5 | 传感器 6 | 横屏显示 7 | 竖屏显示 8 | 9 | 10 | 11 | 默认 12 | 300 13 | 320 14 | 340 15 | 360 16 | 380 17 | 400 18 | 420 19 | 440 20 | 460 21 | 480 22 | 500 23 | 自定义 24 | 25 | 26 | 27 | 默认 28 | 简体中文 29 | 繁體中文 30 | English 31 | 日本語 32 | Русский 33 | 34 | 35 | 36 | 默认 37 | 方案一 38 | 方案二 39 | 方案三 40 | 自定义 41 | 42 | 43 | 44 | 默认 45 | 浅灰 46 | 纯白 47 | 48 | 49 | 50 | 搜狗免费 51 | 搜狗 52 | 百度 53 | 有道 54 | 55 | 56 | 57 | 阿拉伯语 58 | 爱沙尼亚语 59 | 保加利亚语 60 | 波兰语 61 | 韩语 62 | 波斯尼亚语 63 | 波斯语 64 | 白苗文 65 | 丹麦语 66 | 德语 67 | 俄语 68 | 法语 69 | 芬兰语 70 | 克林贡语(piqaD) 71 | 克林贡语 72 | 克罗地亚语 73 | 克雷塔罗奥托米语 74 | 加泰隆语 75 | 捷克语 76 | 罗马尼亚语 77 | 拉脱维亚语 78 | 海地克里奥尔语 79 | 立陶宛语 80 | 荷兰语 81 | 马来语 82 | 马耳他语 83 | 葡萄牙语 84 | 日语 85 | 斯洛文尼亚语 86 | 泰语 87 | 土耳其语 88 | 塞尔维亚语(拉丁文) 89 | 塞尔维亚语(西里尔文) 90 | 斯洛伐克语 91 | 斯瓦希里语 92 | 南非荷兰语 93 | 挪威语 94 | 英语 95 | 西班牙语 96 | 乌克兰语 97 | 乌尔都语 98 | 希腊语 99 | 匈牙利语 100 | 威尔士语 101 | 尤卡坦玛雅语 102 | 希伯来语 103 | 中文 104 | 意大利语 105 | 印地语 106 | 印度尼西亚语 107 | 中文繁体 108 | 越南语 109 | 瑞典语 110 | 粤语(繁体) 111 | 斐济 112 | 菲律宾语 113 | 萨摩亚语 114 | 汤加语 115 | 塔希提语 116 | 马尔加什语 117 | 孟加拉语 118 | 119 | 120 | 121 | 122 | 浅色 123 | 深色 124 | 系统默认设置 125 | 126 | 127 | -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/xposed/NavBarHook.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.xposed 2 | 3 | import android.R 4 | import android.app.Activity 5 | import android.graphics.Color 6 | import android.os.Build 7 | import android.os.Bundle 8 | import android.util.TypedValue 9 | import android.view.View 10 | import android.widget.Toast 11 | import de.robv.android.xposed.XSharedPreferences 12 | import io.ikws4.library.xposedktx.hookMethod 13 | import io.ikws4.weiju.utilities.XSPUtils 14 | 15 | class NavBarHook(sp: XSPUtils) { 16 | private val isEnable = sp.getBoolean("is_enable_nav_bar") 17 | private val isHide = sp.getBoolean("is_hide_nav_bar") 18 | private val immersive = sp.getString("immersive_nav_bar") 19 | private val customColor = sp.getString("custom_nav_bar_color") 20 | private val iconColor = sp.getString("nav_bar_icon_color") 21 | 22 | init { 23 | if (isEnable) { 24 | Activity::class.java.hookMethod("onCreate", Bundle::class.java, 25 | afterHookedMethod = { 26 | with(window) { 27 | val flag = decorView.systemUiVisibility 28 | 29 | // 沉浸 30 | if (immersive != "Default") { 31 | val typedValue = TypedValue() 32 | when (immersive) { 33 | "ColorPrimary" -> { 34 | theme.resolveAttribute(R.attr.colorPrimary, typedValue, true) 35 | } 36 | "ColorPrimaryDark" -> { 37 | theme.resolveAttribute(R.attr.colorPrimaryDark, typedValue, true) 38 | } 39 | "ColorAccent" -> { 40 | theme.resolveAttribute(R.attr.colorAccent, typedValue, true) 41 | } 42 | "Custom" -> { 43 | if (customColor != "Default") { 44 | try { 45 | val color = Color.parseColor(customColor) 46 | navigationBarColor = color 47 | } catch (ex: Exception) { 48 | ex.printStackTrace() 49 | Toast.makeText(this@hookMethod, "WeiJU: ${ex.message}", Toast.LENGTH_SHORT).show() 50 | } 51 | } 52 | } 53 | } 54 | navigationBarColor = typedValue.data 55 | } else { 56 | navigationBarColor = Color.parseColor(customColor) 57 | } 58 | 59 | // 显隐状态栏 60 | if (isHide) { 61 | decorView.systemUiVisibility = 62 | flag or (View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) 63 | } 64 | 65 | // 图标颜色 66 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 67 | when (iconColor) { 68 | "Grey" -> { 69 | decorView.systemUiVisibility = flag or View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR 70 | } 71 | "White" -> { 72 | decorView.systemUiVisibility = flag xor View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR 73 | } 74 | } 75 | } 76 | } 77 | }) 78 | } 79 | } 80 | } -------------------------------------------------------------------------------- /app/src/main/java/io/ikws4/weiju/utilities/SPManager.kt: -------------------------------------------------------------------------------- 1 | package io.ikws4.weiju.utilities 2 | 3 | import android.annotation.SuppressLint 4 | import android.content.Context 5 | import android.content.SharedPreferences 6 | import androidx.core.content.edit 7 | import io.ikws4.weiju.BuildConfig 8 | 9 | class SPManager private constructor(context: Context) { 10 | private val weiJuSP = context.getSharedPreferences(WEIJU_SP, Context.MODE_PRIVATE) 11 | private val hookListSP = context.getSharedPreferences(HOOK_LIST_SP, Context.MODE_PRIVATE) 12 | val templateSP: SharedPreferences = context.getSharedPreferences(TEMPLATE_SP, Context.MODE_PRIVATE) 13 | 14 | inner class WeiJuSP { 15 | val isAutoApplyTemplate = weiJuSP.getBoolean("is_auto_apply_template", false) 16 | val isBootCompleted = weiJuSP.getBoolean("is_boot_completed", true) 17 | val isHideSystemApp = weiJuSP.getBoolean("is_hide_system_app", true) 18 | var apiSogouAppid: String 19 | get() = weiJuSP.getString("api_sogou_appid", "")!! 20 | set(value) = weiJuSP.edit { putString("api_sogou_appid", value) } 21 | 22 | var apiSogouKey: String 23 | get() = weiJuSP.getString("api_sogou_key", "")!! 24 | set(value) = weiJuSP.edit { putString("api_sogou_key", value) } 25 | 26 | var apiBaiduAppid: String 27 | get() = weiJuSP.getString("api_baidu_appid", "")!! 28 | set(value) = weiJuSP.edit { putString("api_baidu_appid", value) } 29 | 30 | var apiBaiduKey: String 31 | get() = weiJuSP.getString("api_baidu_key", "")!! 32 | set(value) = weiJuSP.edit { putString("api_baidu_key", value) } 33 | 34 | var apiYoudaoAppid: String 35 | get() = weiJuSP.getString("api_youdao_appid", "")!! 36 | set(value) = weiJuSP.edit { putString("api_youdao_appid", value) } 37 | 38 | var apiYoudaoKey: String 39 | get() = weiJuSP.getString("api_youdao_key", "")!! 40 | set(value) = weiJuSP.edit { putString("api_youdao_key", value) } 41 | 42 | var freeSogouApiAmount: Int 43 | get() = weiJuSP.getInt("free_sogou_api_amount", 0) 44 | set(value) = weiJuSP.edit { putInt("free_sogou_api_amount", value) } 45 | 46 | var isCategoryTranslationFragmentRemindShow: Boolean 47 | get() = weiJuSP.getBoolean("is_category_translation_fragment_remind_show", true) 48 | set(value) = weiJuSP.edit { putBoolean("is_category_translation_fragment_remind_show", value) } 49 | 50 | var newVersionDownloadUrl: String 51 | get() = weiJuSP.getString("download_url", "")!! 52 | set(value) = weiJuSP.edit { putString("download_url", value) } 53 | 54 | var versionName: String 55 | get() = weiJuSP.getString("version_name", BuildConfig.VERSION_NAME)!! 56 | set(value) = weiJuSP.edit { putString("version_name", value) } 57 | } 58 | 59 | inner class HookListSP { 60 | /** 61 | * 标记为true,开启Hook 62 | * @param pkgName String 63 | */ 64 | fun add(pkgName: String) { 65 | hookListSP.edit { 66 | putBoolean(pkgName, true) 67 | } 68 | } 69 | 70 | /** 71 | * 标记为false,取消Hook 72 | * @param pkgName String 73 | */ 74 | fun remove(pkgName: String) { 75 | hookListSP.edit { 76 | putBoolean(pkgName, false) 77 | } 78 | } 79 | } 80 | 81 | companion object { 82 | // For Singleton instantiation 83 | @SuppressLint("StaticFieldLeak") 84 | @Volatile 85 | private var instance: SPManager? = null 86 | 87 | fun getInstance(ctx: Context): SPManager { 88 | return instance ?: synchronized(this) { 89 | instance ?: SPManager(ctx).also { instance = it } 90 | } 91 | } 92 | } 93 | } --------------------------------------------------------------------------------