├── .gitignore ├── .idea ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── gradle.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── README_en.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── me │ │ └── sweetll │ │ └── evilhide │ │ └── ApplicationTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── kotlin │ │ └── me │ │ │ └── sweetll │ │ │ └── evilhide │ │ │ ├── AppApplication.kt │ │ │ ├── MainActivity.kt │ │ │ ├── ProxyActivity.kt │ │ │ ├── SettingsActivity.kt │ │ │ ├── adapter │ │ │ ├── AppAdapter.kt │ │ │ └── BindingAdapter.kt │ │ │ ├── config │ │ │ ├── Settings.kt │ │ │ └── SharedPreferenceConfig.kt │ │ │ ├── extension │ │ │ ├── SharedPreferenceExtensions.kt │ │ │ └── StringExtensions.kt │ │ │ ├── fragment │ │ │ └── SettingFragment.kt │ │ │ ├── model │ │ │ └── AppInfo.kt │ │ │ ├── receiver │ │ │ └── CallReceiver.kt │ │ │ ├── service │ │ │ └── HiddenService.kt │ │ │ └── viewmodel │ │ │ └── AppViewModel.kt │ └── res │ │ ├── drawable │ │ ├── ic_add.xml │ │ └── ic_settings.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── activity_settings.xml │ │ └── item_app.xml │ │ ├── menu │ │ └── menu_main.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ ├── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ │ └── xml │ │ └── settings.xml │ └── test │ └── java │ └── me │ └── sweetll │ └── evilhide │ └── ExampleUnitTest.kt ├── build.gradle ├── demo.gif ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 26 | 27 | 28 | 29 | 30 | 31 | 33 | 34 | 35 | 36 | 37 | 1.8 38 | 39 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 中文 | [English][2] 2 | 3 | # Evil Hide 4 | 隐藏app的app。用处自己挖掘。 5 | 6 | ![1](./demo.gif) 7 | 8 | ### 注意: 9 | 只在Nexus 4、Nexus 5X上测试通过,小米手机不能使用(MIUI会把不在近期任务列表里的应用杀掉,导致应用的BroadcastReceiver不能响应拨号事件) 10 | 11 | ### 特点: 12 | - 可以隐藏软件自身的图标,并通过在拨号盘输入暗号启动应用 13 | - 不需要root!(仅限`原生Android 5.0`以上,一般而言,Nexus家族是可以使用的,包括但不限于Nexus 4、Nexus 5、Nexus 5x、Nexus 6P等,第三方厂商自己修改过的ROM不可使用,包括但不限于三星,小米,华为等) 14 | 15 | ### 原理 16 | 在Android 5.0以下使用以下命令显示和隐藏(需要root权限): 17 | ``` 18 | adb shell pm endable 19 | adb shell pm disable 20 | ``` 21 | 在Android 5.0及以上使用以下命令显示和隐藏(不需要root,但是需要系统权限): 22 | ``` 23 | adb shell pm hide 24 | adb shell pm unhide 25 | ``` 26 | 27 | 区别: 28 | `hide`命令相当于`uninstall -k`卸载应用但是保留数据 29 | 30 | 实际上,`hide`命令最终调用了以下函数(来自`android.content.pm.PackageManager`). 不幸的是, 它被标识为`@hide`, 所以在Android SDK中,你无法通过正常手段调用它. 31 | ``` 32 | /** 33 | * Puts the package in a hidden state, which is almost like an uninstalled state, 34 | * making the package unavailable, but it doesn't remove the data or the actual 35 | * package file. Application can be unhidden by either resetting the hidden state 36 | * or by installing it, such as with {@link #installExistingPackage(String)} 37 | * @hide 38 | */ 39 | public abstract boolean setApplicationHiddenSettingAsUser(String packageName, boolean hidden, 40 | UserHandle userHandle); 41 | ``` 42 | 43 | 44 | ### 用法: 45 | 如果你想使用非root版本,请确保满足条件并切换到`noroot`分支 46 | ``` 47 | git checkout noroot 48 | ``` 49 | 50 | 本软件的默认启动密码是`#1234` 51 | 启动方式: 52 | 打开拨号盘,输入`#1234`,再拨出,就可以启动本应用了,然后在应用列表里选择应用是否隐藏即可 53 | 54 | ### 待做列表: 55 | - [x] Android 6.0的权限管理 56 | - [x] 当检测到手机是Android 4.4以上时,使用`pm hide`代替`pm disable` 57 | - [ ] 使用密码启动被隐藏的应用 58 | 59 | [1]: https://github.com/blackbbc/Evil-Hide/blob/master/README.md 60 | [2]: https://github.com/blackbbc/Evil-Hide/blob/master/README_en.md 61 | -------------------------------------------------------------------------------- /README_en.md: -------------------------------------------------------------------------------- 1 | [中文][1] | English 2 | 3 | # Evil Hide 4 | This app is used to hide other apps' icon in the launcher. 5 | Note: After hide, the hidden app cannot be launched from anywhere before it is unhide. 6 | 7 | ![1](./demo.gif) 8 | 9 | ### Notice: 10 | Only tested on Nexus 4 and Nexus 5X. I also find that this app may not work on some ROM, such as MIUI for these ROM will kill apps that not show in the recent tasks, which cause the broadcastreceiver not work. 11 | 12 | ### Feature: 13 | - You can choose to hide the icon of this app. Then you can launch it by inputting password in the dial pad. 14 | - No root needed!(Only for rom build from `AOSP` and android version is bigger than 5.0. For example Nexus series, including Nexus 4, Nexus 5, Nexus 5x, Nexus 6p and etc) 15 | 16 | ### Theory 17 | Before Android 5.0, you can use the following shell commands to hide other apps (root permission is needed!): 18 | ``` 19 | adb shell pm endable 20 | adb shell pm disable 21 | ``` 22 | After Android 5.0, you can use the following shell commands to hide other apps(root permission is `not` needed!): 23 | ``` 24 | adb shell pm hide 25 | adb shell pm unhide 26 | ``` 27 | 28 | Difference between disable and unhide: 29 | `hide` is equals to `uninstall -k` which means uninstall application but keep data 30 | 31 | In fact, the `hide` command finally call the following function from `android.content.pm.PackageManager`. Unfortunately, it is marked as `@hide`, so you cannot use it in sdk. 32 | ``` 33 | /** 34 | * Puts the package in a hidden state, which is almost like an uninstalled state, 35 | * making the package unavailable, but it doesn't remove the data or the actual 36 | * package file. Application can be unhidden by either resetting the hidden state 37 | * or by installing it, such as with {@link #installExistingPackage(String)} 38 | * @hide 39 | */ 40 | public abstract boolean setApplicationHiddenSettingAsUser(String packageName, boolean hidden, 41 | UserHandle userHandle); 42 | ``` 43 | 44 | 45 | ### Usage: 46 | If you'd like to use no root version. Please make sure you satisfy the conditions and switch to the `noroot` branch: 47 | ``` 48 | git checkout noroot 49 | ``` 50 | 51 | The default launch password is `#1234` 52 | Launch method: 53 | Open dial pad,input `#1234`,then press call button. This app will be launched 54 | After that, you can switch the app's hidden state in the list 55 | 56 | ### Todo list: 57 | - [x] Android M dynamic permission 58 | - [x] Support `hide` and `unhide` commands 59 | - [ ] Use password to launch hidden apps 60 | 61 | [1]: https://github.com/blackbbc/Evil-Hide/blob/master/README.md 62 | [2]: https://github.com/blackbbc/Evil-Hide/blob/master/README_en.md 63 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android' 3 | 4 | dependencies { 5 | compile fileTree(dir: 'libs', include: ['*.jar']) 6 | testCompile 'junit:junit:4.12' 7 | compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" 8 | compile 'com.android.support:appcompat-v7:26.0.1' 9 | compile 'com.android.support:design:26.0.1' 10 | compile 'com.android.support:recyclerview-v7:26.0.1' 11 | compile 'com.karumi:dexter:4.1.0' 12 | compile 'eu.chainfire:libsuperuser:1.0.0.+' 13 | compile 'com.github.ivbaranov:materialfavoritebutton:0.1.2' 14 | kapt "com.android.databinding:compiler:$plugin_version" 15 | } 16 | 17 | android { 18 | compileSdkVersion 26 19 | buildToolsVersion "26.0.1" 20 | 21 | defaultConfig { 22 | applicationId "me.sweetll.evilhide" 23 | minSdkVersion 14 24 | targetSdkVersion 26 25 | versionCode 1 26 | versionName "1.0.0" 27 | } 28 | buildTypes { 29 | release { 30 | minifyEnabled false 31 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 32 | } 33 | } 34 | dataBinding { 35 | enabled = true 36 | } 37 | sourceSets { 38 | main.java.srcDirs += 'src/main/kotlin' 39 | } 40 | } 41 | 42 | kapt { 43 | generateStubs = true 44 | } 45 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /opt/android-sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/me/sweetll/evilhide/ApplicationTest.kt: -------------------------------------------------------------------------------- 1 | package me.sweetll.evilhide 2 | 3 | import android.app.Application 4 | import android.test.ApplicationTestCase 5 | 6 | /** 7 | * [Testing Fundamentals](http://d.android.com/tools/testing/testing_android.html) 8 | */ 9 | class ApplicationTest : ApplicationTestCase(Application::class.java) -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 26 | 29 | 30 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /app/src/main/kotlin/me/sweetll/evilhide/AppApplication.kt: -------------------------------------------------------------------------------- 1 | package me.sweetll.evilhide 2 | 3 | import android.app.Application 4 | import android.preference.PreferenceManager 5 | 6 | class AppApplication : Application() { 7 | companion object { 8 | private lateinit var INSTANCE: AppApplication 9 | 10 | fun get(): AppApplication = INSTANCE 11 | } 12 | 13 | override fun onCreate() { 14 | super.onCreate() 15 | INSTANCE = this 16 | PreferenceManager.setDefaultValues(this, R.xml.settings, false) 17 | } 18 | } -------------------------------------------------------------------------------- /app/src/main/kotlin/me/sweetll/evilhide/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package me.sweetll.evilhide 2 | 3 | import android.Manifest 4 | import android.content.Intent 5 | import android.content.pm.ApplicationInfo 6 | import android.databinding.DataBindingUtil 7 | import android.support.v7.app.AppCompatActivity 8 | import android.os.Bundle 9 | import android.support.v7.widget.LinearLayoutManager 10 | import android.view.Menu 11 | import android.view.MenuItem 12 | import android.view.View 13 | import android.view.ViewGroup 14 | import android.widget.AdapterView 15 | import com.karumi.dexter.Dexter 16 | import com.karumi.dexter.listener.single.SnackbarOnDeniedPermissionListener 17 | 18 | import me.sweetll.evilhide.adapter.AppAdapter 19 | import me.sweetll.evilhide.config.Settings 20 | import me.sweetll.evilhide.databinding.ActivityMainBinding 21 | import me.sweetll.evilhide.extension.getFavorite 22 | import me.sweetll.evilhide.model.AppInfo 23 | 24 | class MainActivity : AppCompatActivity() { 25 | lateinit var binding: ActivityMainBinding 26 | val appAdapter = AppAdapter(mutableListOf()) 27 | 28 | override fun onCreate(savedInstanceState: Bundle?) { 29 | super.onCreate(savedInstanceState) 30 | binding = DataBindingUtil.setContentView(this, R.layout.activity_main) 31 | setSupportActionBar(binding.toolbar) 32 | supportActionBar!!.setDisplayShowTitleEnabled(false) 33 | 34 | initPermissions() 35 | 36 | setupRecyclerView() 37 | 38 | binding.spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { 39 | override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { 40 | populateAppList(position) 41 | } 42 | 43 | override fun onNothingSelected(parent: AdapterView<*>?) { 44 | 45 | } 46 | } 47 | } 48 | 49 | fun setupRecyclerView() { 50 | binding.appRecycler.layoutManager = LinearLayoutManager(this) 51 | binding.appRecycler.adapter = appAdapter 52 | } 53 | 54 | override fun onCreateOptionsMenu(menu: Menu): Boolean { 55 | menuInflater.inflate(R.menu.menu_main, menu) 56 | return true 57 | } 58 | 59 | override fun onOptionsItemSelected(item: MenuItem): Boolean { 60 | when (item.itemId) { 61 | R.id.action_settings -> { 62 | val intent = Intent() 63 | intent.setClass(this, SettingsActivity::class.java) 64 | startActivity(intent) 65 | } 66 | } 67 | return super.onOptionsItemSelected(item) 68 | } 69 | 70 | fun initPermissions() { 71 | val snackBarPermissionListener = SnackbarOnDeniedPermissionListener.Builder 72 | .with(binding.root as ViewGroup, "需要电话权限以便从拨号盘启动") 73 | .withOpenSettingsButton("设置") 74 | .build() 75 | Dexter.withActivity(this) 76 | .withPermission(Manifest.permission.PROCESS_OUTGOING_CALLS) 77 | .withListener(snackBarPermissionListener) 78 | .check() 79 | } 80 | 81 | fun populateAppList(flag: Int) { 82 | val pm = packageManager 83 | val installedApps = pm.getInstalledApplications(0) 84 | 85 | appAdapter.setNewData( 86 | when (flag) { 87 | Settings.SPINNER_STAR_APP -> installedApps.filter { it.packageName.getFavorite() } 88 | Settings.SPINNER_HIDDEN_APP -> installedApps.filter { !it.enabled } 89 | else -> installedApps 90 | } 91 | .filter { it.packageName != BuildConfig.APPLICATION_ID && it.flags and ApplicationInfo.FLAG_SYSTEM != 1} 92 | .fold(mutableListOf(), { 93 | newData, applicationInfo -> 94 | newData.add(AppInfo(applicationInfo)) 95 | newData 96 | }) 97 | ) 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /app/src/main/kotlin/me/sweetll/evilhide/ProxyActivity.kt: -------------------------------------------------------------------------------- 1 | package me.sweetll.evilhide 2 | 3 | import android.content.Intent 4 | import android.support.v7.app.AppCompatActivity 5 | import android.os.Bundle 6 | 7 | class ProxyActivity : AppCompatActivity() { 8 | 9 | override fun onCreate(savedInstanceState: Bundle?) { 10 | super.onCreate(savedInstanceState) 11 | val intent = Intent(this, MainActivity::class.java) 12 | intent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP 13 | startActivity(intent) 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/kotlin/me/sweetll/evilhide/SettingsActivity.kt: -------------------------------------------------------------------------------- 1 | package me.sweetll.evilhide 2 | 3 | import android.databinding.DataBindingUtil 4 | import android.os.Bundle 5 | import android.support.v7.app.AppCompatActivity 6 | 7 | import me.sweetll.evilhide.databinding.ActivitySettingsBinding 8 | import me.sweetll.evilhide.fragment.SettingFragment 9 | 10 | class SettingsActivity : AppCompatActivity() { 11 | 12 | public override fun onCreate(savedInstanceState: Bundle?) { 13 | super.onCreate(savedInstanceState) 14 | val binding : ActivitySettingsBinding = DataBindingUtil.setContentView(this, R.layout.activity_settings) 15 | fragmentManager.beginTransaction() 16 | .replace(android.R.id.content, SettingFragment()) 17 | .commit() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/kotlin/me/sweetll/evilhide/adapter/AppAdapter.kt: -------------------------------------------------------------------------------- 1 | package me.sweetll.evilhide.adapter 2 | 3 | import android.databinding.DataBindingUtil 4 | import android.support.v7.widget.RecyclerView 5 | import android.view.LayoutInflater 6 | import android.view.ViewGroup 7 | import me.sweetll.evilhide.R 8 | import me.sweetll.evilhide.databinding.ItemAppBinding 9 | import me.sweetll.evilhide.model.AppInfo 10 | import me.sweetll.evilhide.viewmodel.AppViewModel 11 | 12 | class AppAdapter(var data: MutableList) : RecyclerView.Adapter() { 13 | 14 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { 15 | val binding: ItemAppBinding = DataBindingUtil.inflate( 16 | LayoutInflater.from(parent.context), 17 | R.layout.item_app, 18 | parent, 19 | false) 20 | return ViewHolder(binding) 21 | } 22 | 23 | override fun onBindViewHolder(holder: ViewHolder, position: Int) { 24 | val appInfo = data[position] 25 | holder.bindAppInfo(appInfo) 26 | } 27 | 28 | override fun getItemCount(): Int = data.size 29 | 30 | fun setNewData(newData: MutableList) { 31 | data.clear() 32 | data.addAll(newData) 33 | notifyDataSetChanged() 34 | } 35 | 36 | class ViewHolder(val binding: ItemAppBinding): RecyclerView.ViewHolder(binding.root) { 37 | fun bindAppInfo(appInfo: AppInfo) { 38 | binding.starBtn.setOnFavoriteChangeListener{ 39 | button, favorite -> 40 | if (button.isPressed) { 41 | binding.viewModel?.onFavoriteChange(favorite) 42 | } 43 | } 44 | binding.switchBtn.setOnCheckedChangeListener { 45 | button, check -> 46 | if (button.isPressed) { 47 | binding.viewModel?.onCheckChange(check) 48 | } 49 | } 50 | binding.switchBtn.isChecked = appInfo.hidden 51 | binding.viewModel = AppViewModel(itemView.context, appInfo) 52 | } 53 | } 54 | 55 | } -------------------------------------------------------------------------------- /app/src/main/kotlin/me/sweetll/evilhide/adapter/BindingAdapter.kt: -------------------------------------------------------------------------------- 1 | package me.sweetll.evilhide.adapter 2 | 3 | import android.databinding.BindingAdapter 4 | import android.graphics.drawable.Drawable 5 | import android.widget.ImageView 6 | import android.widget.Switch 7 | import com.github.ivbaranov.mfb.MaterialFavoriteButton 8 | 9 | @BindingAdapter("drawable") 10 | fun setImageSrc(imageView: ImageView, drawable: Drawable) { 11 | imageView.setImageDrawable(drawable) 12 | } 13 | 14 | @BindingAdapter("favorite") 15 | fun setFavorite(materialFavoriteButton: MaterialFavoriteButton, favorite: Boolean) { 16 | materialFavoriteButton.setFavorite(favorite, false) 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/kotlin/me/sweetll/evilhide/config/Settings.kt: -------------------------------------------------------------------------------- 1 | package me.sweetll.evilhide.config 2 | 3 | object Settings { 4 | val SPINNER_STAR_APP = 0 5 | val SPINNER_HIDDEN_APP = 1 6 | val SPINNER_ALL_APP = 2 7 | 8 | val KEY_PREF_LAUNCH_PASSWORD = "me.sweetll.evilhide.launch.password" 9 | val KEY_PREF_INVISIBLE = "me.sweetll.evilhide.invisible" 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/kotlin/me/sweetll/evilhide/config/SharedPreferenceConfig.kt: -------------------------------------------------------------------------------- 1 | package me.sweetll.evilhide.config 2 | 3 | import android.content.Context 4 | import android.content.SharedPreferences 5 | import me.sweetll.evilhide.AppApplication 6 | 7 | object SharedPreferenceConfig { 8 | val FILE_NAME = "me.sweetll.evilhide_app" 9 | 10 | val PREFIX_PREF_KEY_B_FAVORITE = "favorite_" 11 | val PREFIX_PREF_KEY_B_HIDDEN = "hidden_" 12 | val PREFIX_PREF_KEY_S_PASSWORD = "password_" 13 | 14 | val sp: SharedPreferences by lazy { 15 | AppApplication.get() 16 | .getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/kotlin/me/sweetll/evilhide/extension/SharedPreferenceExtensions.kt: -------------------------------------------------------------------------------- 1 | package me.sweetll.evilhide.extension 2 | 3 | import android.content.SharedPreferences 4 | 5 | fun SharedPreferences.edit(func: SharedPreferences.Editor.() -> Unit) { 6 | val editor = edit() 7 | editor.func() 8 | editor.apply() 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/kotlin/me/sweetll/evilhide/extension/StringExtensions.kt: -------------------------------------------------------------------------------- 1 | package me.sweetll.evilhide.extension 2 | 3 | import me.sweetll.evilhide.config.SharedPreferenceConfig.PREFIX_PREF_KEY_B_FAVORITE 4 | import me.sweetll.evilhide.config.SharedPreferenceConfig.PREFIX_PREF_KEY_B_HIDDEN 5 | import me.sweetll.evilhide.config.SharedPreferenceConfig.PREFIX_PREF_KEY_S_PASSWORD 6 | import me.sweetll.evilhide.config.SharedPreferenceConfig.sp 7 | 8 | fun String.getFavorite(): Boolean = sp.getBoolean("$PREFIX_PREF_KEY_B_FAVORITE$this", false) 9 | fun String.saveFavorite(favorite: Boolean) { 10 | sp.edit { 11 | putBoolean("$PREFIX_PREF_KEY_B_FAVORITE${this@saveFavorite}", favorite) 12 | } 13 | } 14 | 15 | fun String.getHidden(): Boolean = sp.getBoolean("$PREFIX_PREF_KEY_B_HIDDEN$this", false) 16 | fun String.saveHidden(hidden: Boolean) { 17 | sp.edit { 18 | putBoolean("$PREFIX_PREF_KEY_B_HIDDEN${this@saveHidden}", hidden) 19 | } 20 | } 21 | 22 | fun String.getPassword(): String = sp.getString("$PREFIX_PREF_KEY_S_PASSWORD$this", "") 23 | fun String.savePassword(password: String) { 24 | sp.edit { 25 | putString("$PREFIX_PREF_KEY_S_PASSWORD${this@savePassword}", password) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/kotlin/me/sweetll/evilhide/fragment/SettingFragment.kt: -------------------------------------------------------------------------------- 1 | package me.sweetll.evilhide.fragment 2 | 3 | import android.content.ComponentName 4 | import android.content.SharedPreferences 5 | import android.content.pm.PackageManager 6 | import android.os.Bundle 7 | import android.os.Handler 8 | import android.preference.PreferenceFragment 9 | import android.widget.Toast 10 | import me.sweetll.evilhide.AppApplication 11 | import me.sweetll.evilhide.MainActivity 12 | import me.sweetll.evilhide.ProxyActivity 13 | 14 | import me.sweetll.evilhide.R 15 | import me.sweetll.evilhide.config.Settings 16 | 17 | class SettingFragment : PreferenceFragment(), SharedPreferences.OnSharedPreferenceChangeListener { 18 | val handler = Handler() 19 | 20 | override fun onCreate(savedInstanceState: Bundle?) { 21 | super.onCreate(savedInstanceState) 22 | addPreferencesFromResource(R.xml.settings) 23 | } 24 | 25 | override fun onResume() { 26 | super.onResume() 27 | preferenceScreen.sharedPreferences.registerOnSharedPreferenceChangeListener(this) 28 | } 29 | 30 | override fun onPause() { 31 | super.onPause() 32 | preferenceScreen.sharedPreferences.unregisterOnSharedPreferenceChangeListener(this) 33 | } 34 | 35 | override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) { 36 | if (key == Settings.KEY_PREF_INVISIBLE) { 37 | val selfInvisible = sharedPreferences.getBoolean(key, false) 38 | val p = activity.packageManager 39 | val componentName = ComponentName(activity, ProxyActivity::class.java) 40 | when (selfInvisible) { 41 | true -> { 42 | Toast.makeText(AppApplication.get(), "程序将在3s后退出,退出后请从拨号盘进入", Toast.LENGTH_LONG).show() 43 | handler.postDelayed({ 44 | p.setComponentEnabledSetting(componentName, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 0) 45 | }, 3000) 46 | } 47 | false -> p.setComponentEnabledSetting(componentName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP) 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/kotlin/me/sweetll/evilhide/model/AppInfo.kt: -------------------------------------------------------------------------------- 1 | package me.sweetll.evilhide.model 2 | 3 | 4 | import android.content.pm.ApplicationInfo 5 | import me.sweetll.evilhide.AppApplication 6 | import me.sweetll.evilhide.extension.* 7 | import me.sweetll.evilhide.service.HiddenService 8 | 9 | class AppInfo(val applicationInfo: ApplicationInfo) { 10 | val packageName: String 11 | get() = applicationInfo.packageName 12 | 13 | var favorite: Boolean 14 | get() = packageName.getFavorite() 15 | set(value) { 16 | packageName.saveFavorite(value) 17 | } 18 | var hidden: Boolean 19 | get() = !applicationInfo.enabled 20 | set(value) { 21 | val cmd = "pm ${if (value) "disable" else "enable"} $packageName" 22 | HiddenService.performAction(cmd) 23 | applicationInfo.enabled = !applicationInfo.enabled 24 | } 25 | var password: String 26 | get() = packageName.getPassword() 27 | set(value) { 28 | packageName.savePassword(value) 29 | } 30 | } -------------------------------------------------------------------------------- /app/src/main/kotlin/me/sweetll/evilhide/receiver/CallReceiver.kt: -------------------------------------------------------------------------------- 1 | package me.sweetll.evilhide.receiver 2 | 3 | import android.content.BroadcastReceiver 4 | import android.content.Context 5 | import android.content.Intent 6 | import android.preference.PreferenceManager 7 | 8 | import me.sweetll.evilhide.MainActivity 9 | import me.sweetll.evilhide.config.Settings 10 | import me.sweetll.evilhide.ProxyActivity 11 | 12 | class CallReceiver : BroadcastReceiver() { 13 | override fun onReceive(context: Context, intent: Intent) { 14 | var phoneNumber: String? = resultData 15 | val preferences = PreferenceManager.getDefaultSharedPreferences(context) 16 | val launchPassword = preferences.getString(Settings.KEY_PREF_LAUNCH_PASSWORD, "#1234") 17 | if (phoneNumber == null) { 18 | phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER) 19 | } 20 | 21 | if (phoneNumber == launchPassword) { 22 | val i = Intent() 23 | i.setClassName("me.sweetll.evilhide", "me.sweetll.evilhide.MainActivity") 24 | i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 25 | context.startActivity(i) 26 | resultData = null 27 | } else { 28 | //查询preference 29 | } 30 | 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/kotlin/me/sweetll/evilhide/service/HiddenService.kt: -------------------------------------------------------------------------------- 1 | 2 | package me.sweetll.evilhide.service 3 | 4 | import android.app.IntentService 5 | import android.content.Intent 6 | import android.util.Log 7 | 8 | import eu.chainfire.libsuperuser.Shell 9 | import me.sweetll.evilhide.AppApplication 10 | 11 | class HiddenService : IntentService("hidden-service") { 12 | 13 | override fun onCreate() { 14 | super.onCreate() 15 | } 16 | 17 | override fun onHandleIntent(intent: Intent?) { 18 | intent?.let { 19 | val action = it.action 20 | if (!action.isNullOrEmpty()) { 21 | Log.d("evil", "action = $action") 22 | Shell.SU.run(action) 23 | } 24 | } 25 | } 26 | 27 | companion object { 28 | fun performAction(action: String) { 29 | val svc = Intent(AppApplication.get(), HiddenService::class.java) 30 | svc.action = action 31 | AppApplication.get().startService(svc) 32 | } 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/kotlin/me/sweetll/evilhide/viewmodel/AppViewModel.kt: -------------------------------------------------------------------------------- 1 | package me.sweetll.evilhide.viewmodel 2 | 3 | import android.content.Context 4 | import android.databinding.ObservableBoolean 5 | import android.databinding.ObservableField 6 | import android.graphics.drawable.Drawable 7 | import android.support.v7.app.AlertDialog 8 | import android.view.View 9 | import android.widget.EditText 10 | import me.sweetll.evilhide.model.AppInfo 11 | import me.sweetll.evilhide.service.HiddenService 12 | 13 | class AppViewModel(val context: Context, val appInfo: AppInfo) { 14 | val appName: ObservableField = ObservableField() 15 | val appIcon: ObservableField = ObservableField() 16 | val isStar: ObservableBoolean = ObservableBoolean() 17 | val isHidden: ObservableBoolean = ObservableBoolean() 18 | 19 | init { 20 | val pm = context.packageManager 21 | appName.set(pm.getApplicationLabel(appInfo.applicationInfo).toString()) 22 | appIcon.set(pm.getApplicationIcon(appInfo.applicationInfo)) 23 | isStar.set(appInfo.favorite) 24 | isHidden.set(appInfo.hidden) 25 | } 26 | 27 | @Suppress("UNUSED_PARAMETER") 28 | fun onClickApp(view: View) { 29 | val intent = context.packageManager.getLaunchIntentForPackage(appInfo.packageName) 30 | intent?.let { context.startActivity(it) } 31 | } 32 | 33 | @Suppress("UNUSED_PARAMETER") 34 | fun onClickAdd(view: View) { 35 | val passwordEdit = EditText(context) 36 | passwordEdit.setText(appInfo.password) 37 | AlertDialog.Builder(context) 38 | .setTitle("请输入启动该应用的密码") 39 | .setView(passwordEdit) 40 | .setNegativeButton("取消", null) 41 | .setPositiveButton("确定", { 42 | dialog, which -> 43 | appInfo.password = passwordEdit.text.toString() 44 | }) 45 | } 46 | 47 | fun onFavoriteChange(favorite: Boolean) { 48 | appInfo.favorite = favorite 49 | isStar.set(favorite) 50 | } 51 | 52 | fun onCheckChange(hidden: Boolean) { 53 | appInfo.hidden = hidden 54 | isHidden.set(hidden) 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_add.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_settings.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | 20 | 25 | 26 | 27 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_app.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 10 | 11 | 12 | 17 | 18 | 24 | 25 | 38 | 39 |