├── 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 |
4 |
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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
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 |
--------------------------------------------------------------------------------
/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 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
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 | 
3 | 
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 |
--------------------------------------------------------------------------------
/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 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
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 | }
--------------------------------------------------------------------------------