Ugly material you

This commit is contained in:
Nullptr 2022-07-26 15:45:42 +08:00
commit 6c6af8dd54
No known key found for this signature in database
GPG key ID: 0B9D02052FF536BD
128 changed files with 709 additions and 3235 deletions

View file

@ -4,10 +4,12 @@ import java.io.PrintStream
import java.util.*
plugins {
kotlin("android")
id("com.android.application")
id("com.google.gms.google-services")
id("dev.rikka.tools.materialthemebuilder")
id("dev.rikka.tools.refine")
kotlin("android")
id("androidx.navigation.safeargs.kotlin")
}
android {
@ -71,18 +73,23 @@ dependencies {
implementation(projects.common)
runtimeOnly(projects.xposed)
implementation("androidx.appcompat:appcompat:1.4.2")
implementation("androidx.fragment:fragment-ktx:1.4.1")
implementation("androidx.preference:preference-ktx:1.2.0")
implementation("androidx.navigation:navigation-fragment-ktx:2.5.0")
implementation("androidx.navigation:navigation-ui-ktx:2.5.0")
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
implementation("androidx.work:work-runtime-ktx:2.7.1")
implementation("com.drakeet.about:about:2.5.1")
implementation("com.drakeet.multitype:multitype:4.3.0")
implementation("com.github.kirich1409:viewbindingpropertydelegate:1.5.6")
implementation("com.github.topjohnwu.libsu:core:3.1.2")
implementation("com.google.android.material:material:1.6.1")
implementation("com.google.android.gms:play-services-ads:21.0.0")
implementation("com.google.firebase:firebase-analytics-ktx:21.0.0")
implementation("com.squareup.okhttp3:okhttp:4.9.1")
implementation("com.google.android.gms:play-services-ads:21.1.0")
implementation("com.google.firebase:firebase-analytics-ktx:21.1.0")
implementation("com.squareup.okhttp3:okhttp:4.10.0")
implementation("dev.rikka.hidden:compat:2.3.1")
implementation("dev.rikka.rikkax.material:material:2.5.0")
implementation("dev.rikka.rikkax.material:material-preference:2.0.0")
compileOnly("dev.rikka.hidden:stub:2.3.1")
}
configurations.all {
exclude("androidx.appcompat", "appcompat")
}

View file

@ -1,17 +1,9 @@
# Magic
-keep class com.tsng.hidemyapplist.Magic { *; }
-keep class com.tsng.hidemyapplist.app.MyApplication {
static final com.tsng.hidemyapplist.app.MyApplication$Companion Companion;
}
-keep class com.tsng.hidemyapplist.app.MyApplication$Companion { *; }
# Views
-keep class com.tsng.hidemyapplist.app.ui.activities.ModuleActivity$Fragment { *; }
-keep class com.tsng.hidemyapplist.app.ui.views.FilterRulesView{ *; }
# Xposed
-keepclassmembers class com.tsng.hidemyapplist.app.MyApplication {
static final boolean isModuleActivated;
-keepclassmembers class icu.nullptr.hidemyapplist.MyApp {
boolean isHooked;
}
# Enum class

View file

@ -10,7 +10,7 @@
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name=".app.MyApplication"
android:name="icu.nullptr.hidemyapplist.MyApp"
android:allowBackup="true"
android:fullBackupOnly="true"
android:icon="@mipmap/ic_launcher"
@ -19,30 +19,11 @@
android:supportsRtl="false"
android:theme="@style/AppTheme">
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.tsng.hidemyapplist.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_provider_paths" />
</provider>
<service
android:name=".app.SubmitConfigService"
android:enabled="true" />
<activity android:name=".app.ui.activities.ModuleActivity" />
<activity
android:name="com.tsng.hidemyapplist.app.ui.activities.LogActivity"
android:label="@string/title_logs" />
<activity
android:name="com.tsng.hidemyapplist.app.ui.activities.AboutActivity"
android:name="icu.nullptr.hidemyapplist.ui.activity.AboutActivity"
android:theme="@style/AppTheme.About" />
<activity
android:name=".app.ui.activities.MainActivity"
android:name="icu.nullptr.hidemyapplist.ui.activity.MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@ -54,7 +35,7 @@
android:name=".MainActivityLauncher"
android:exported="true"
android:label="@string/app_name"
android:targetActivity=".app.ui.activities.MainActivity">
android:targetActivity="icu.nullptr.hidemyapplist.ui.activity.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
@ -77,5 +58,4 @@
android:name="xposedscope"
android:resource="@array/xposed_scope" />
</application>
</manifest>

View file

@ -1,36 +0,0 @@
package com.tsng.hidemyapplist.app
import android.widget.Toast
import androidx.annotation.StringRes
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentTransaction
import com.tsng.hidemyapplist.R
import com.tsng.hidemyapplist.app.MyApplication.Companion.appContext
fun makeToast(@StringRes resId: Int) {
Toast.makeText(appContext, resId, Toast.LENGTH_SHORT).show()
}
fun makeToast(text: CharSequence) {
Toast.makeText(appContext, text, Toast.LENGTH_SHORT).show()
}
fun AppCompatActivity.startFragment(fragment: Fragment, addToBackStack: Boolean = true) {
val transaction = supportFragmentManager
.beginTransaction()
.replace(R.id.fragment_container, fragment)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
if (addToBackStack) transaction.addToBackStack(null)
transaction.commit()
}
fun Fragment.startFragment(fragment: Fragment, addToBackStack: Boolean = true) {
val transaction = parentFragmentManager
.beginTransaction()
.hide(this)
.add(R.id.fragment_container, fragment)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
if (addToBackStack) transaction.addToBackStack(null)
transaction.commit()
}

View file

@ -1,55 +0,0 @@
package com.tsng.hidemyapplist.app
import com.tsng.hidemyapplist.BuildConfig
import com.tsng.hidemyapplist.R
import com.tsng.hidemyapplist.app.MyApplication.Companion.appContext
import icu.nullptr.hidemyapplist.common.JsonConfig
import java.io.File
object JsonConfigManager {
val configFile = File("${appContext.filesDir.absolutePath}/config.json")
val globalConfig: JsonConfig
init {
if (!configFile.exists())
configFile.writeText(JsonConfig().toString())
try {
globalConfig = JsonConfig.parse(configFile.readText())
val configVersion = globalConfig.configVersion
if (configVersion < 49) throw RuntimeException("Config version too old")
if (configVersion < 65) migrateFromPre65()
globalConfig.configVersion = BuildConfig.VERSION_CODE
} catch (e: Exception) {
makeToast(R.string.config_damaged)
throw RuntimeException("Config file too old or damaged").apply { addSuppressed(e) }
}
}
@Synchronized
private fun save() {
configFile.writeText(globalConfig.toString())
}
@Synchronized
fun edit(block: JsonConfig.() -> Unit) {
globalConfig.block()
save()
}
private fun migrateFromPre65() {
globalConfig.templates.forEach { (_, data) ->
JsonConfig.Template::class.java.getDeclaredField("queryParamRules").apply {
isAccessible = true
set(data, mutableSetOf<String>())
}
}
globalConfig.scope.forEach { (_, data) ->
JsonConfig.AppConfig::class.java.getDeclaredField("extraQueryParamRules").apply {
isAccessible = true
set(data, mutableSetOf<String>())
}
}
}
}

View file

@ -1,36 +0,0 @@
package com.tsng.hidemyapplist.app
import android.annotation.SuppressLint
import android.app.Application
import android.content.Context
import com.google.android.material.color.DynamicColors
import com.tsng.hidemyapplist.BuildConfig
import com.tsng.hidemyapplist.R
import com.tsng.hidemyapplist.app.helpers.AppInfoHelper
import kotlin.concurrent.thread
import kotlin.system.exitProcess
class MyApplication : Application() {
companion object {
@SuppressLint("StaticFieldLeak")
lateinit var appContext: Context
val isModuleActivated = false
}
@SuppressLint("SdCardPath")
override fun onCreate() {
super.onCreate()
DynamicColors.applyToActivitiesIfAvailable(this);
appContext = applicationContext
if (!appContext.filesDir.absolutePath.startsWith("/data/user/0/")) {
makeToast(R.string.do_not_dual)
exitProcess(0)
}
JsonConfigManager.edit {
scope.remove(BuildConfig.APPLICATION_ID)
}
thread {
AppInfoHelper.getAppInfoList()
}
}
}

View file

@ -1,23 +0,0 @@
package com.tsng.hidemyapplist.app
import android.app.Service
import android.content.Intent
import android.os.IBinder
import com.tsng.hidemyapplist.app.JsonConfigManager.globalConfig
import com.tsng.hidemyapplist.app.helpers.ServiceHelper
import kotlin.concurrent.thread
class SubmitConfigService : Service() {
override fun onBind(intent: Intent): IBinder? {
return null
}
override fun onCreate() {
thread {
while (true) {
ServiceHelper.submitConfig(globalConfig.toString())
Thread.sleep(1000)
}
}
}
}

View file

@ -1,32 +0,0 @@
package com.tsng.hidemyapplist.app.helpers
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
import com.tsng.hidemyapplist.app.MyApplication
object AppInfoHelper {
data class MyAppInfo(
val appName: String,
val packageName: String,
val icon: Drawable,
val isSystemApp: Boolean
)
private lateinit var mAppInfoList: MutableList<MyAppInfo>
fun getAppInfoList(): MutableList<MyAppInfo> {
if (AppInfoHelper::mAppInfoList.isInitialized) return mAppInfoList.toMutableList()
mAppInfoList = mutableListOf()
val pm = MyApplication.appContext.packageManager
for (appInfo in pm.getInstalledApplications(PackageManager.MATCH_UNINSTALLED_PACKAGES)) {
MyAppInfo(
appInfo.loadLabel(pm).toString(),
appInfo.packageName,
appInfo.loadIcon(pm),
appInfo.flags and ApplicationInfo.FLAG_SYSTEM != 0
).also { mAppInfoList.add(it) }
}
return mAppInfoList.toMutableList()
}
}

View file

@ -1,46 +0,0 @@
package com.tsng.hidemyapplist.app.helpers
import androidx.preference.PreferenceDataStore
import com.tsng.hidemyapplist.app.JsonConfigManager.globalConfig
import icu.nullptr.hidemyapplist.common.JsonConfig
class AppConfigDataStorage(
private val packageName: String,
private val appConfig: JsonConfig.AppConfig
) : PreferenceDataStore() {
var isEnabled = globalConfig.scope.containsKey(packageName)
set(value) {
field = value
if (value) {
globalConfig.scope[packageName] = appConfig
} else {
globalConfig.scope.remove(packageName)
}
}
override fun getBoolean(key: String, defValue: Boolean): Boolean {
return if (key == "isEnabled") isEnabled
else JsonConfig.AppConfig::class.java.getDeclaredField(key)
.also { it.isAccessible = true }
.getBoolean(appConfig)
}
override fun putBoolean(key: String, value: Boolean) {
if (key == "isEnabled") isEnabled = value
else JsonConfig.AppConfig::class.java.getDeclaredField(key)
.also { it.isAccessible = true }
.setBoolean(appConfig, value)
}
override fun getStringSet(key: String, defValues: MutableSet<String>?): MutableSet<String> {
return JsonConfig.AppConfig::class.java.getDeclaredField(key)
.also { it.isAccessible = true }
.get(appConfig) as MutableSet<String>
}
override fun putStringSet(key: String, values: MutableSet<String>?) {
JsonConfig.AppConfig::class.java.getDeclaredField(key)
.also { it.isAccessible = true }
.set(appConfig, values)
}
}

View file

@ -1,112 +0,0 @@
package com.tsng.hidemyapplist.app.ui.activities
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.view.*
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.FileProvider
import androidx.fragment.app.Fragment
import com.tsng.hidemyapplist.R
import com.tsng.hidemyapplist.app.helpers.ServiceHelper
import com.tsng.hidemyapplist.databinding.ActivityLogBinding
import com.tsng.hidemyapplist.databinding.FragmentLogRawBinding
import java.io.File
import java.text.SimpleDateFormat
import java.util.*
class LogActivity : AppCompatActivity() {
private lateinit var binding: ActivityLogBinding
var rawLogs: String? = null
var showRawLogs: Boolean = true
override fun onSupportNavigateUp(): Boolean {
onBackPressed()
return true
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.log, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.toolbar_refresh -> {
rawLogs = ServiceHelper.getLogs()
replaceFragment(RawFragment())
// replaceFragment(if (showRawLogs) BuiltifulFragment() else RawFragment())
true
}
R.id.toolbar_export -> {
val date = SimpleDateFormat("MM-dd_HH-mm-ss", Locale.getDefault()).format(Date())
File(cacheDir.path + "/logs").mkdirs()
val tmp = File(cacheDir.path + "/logs/hma_logs_$date.log")
rawLogs?.let { tmp.writeText(it) }
val intent = Intent(Intent.ACTION_SEND).apply {
type = "text/*"
val uri = FileProvider.getUriForFile(
this@LogActivity,
"com.tsng.hidemyapplist.fileprovider",
tmp
)
putExtra(Intent.EXTRA_STREAM, uri)
}
startActivity(Intent.createChooser(intent, title))
true
}
R.id.toolbar_clean -> {
ServiceHelper.cleanLogs()
rawLogs = null
replaceFragment(RawFragment())
// replaceFragment(if (showRawLogs) BuiltifulFragment() else RawFragment())
true
}
/*
R.id.toolbar_raw_logs -> {
item.isChecked = !item.isChecked
showRawLogs = item.isChecked
replaceFragment(if (showRawLogs) BuiltifulFragment() else RawFragment())
true
}
*/
else -> super.onOptionsItemSelected(item)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityLogBinding.inflate(layoutInflater)
setContentView(binding.root)
setSupportActionBar(findViewById(R.id.toolbar))
supportActionBar?.setDisplayHomeAsUpEnabled(true)
rawLogs = ServiceHelper.getLogs()
replaceFragment(RawFragment())
}
private fun replaceFragment(fragment: Fragment) {
supportFragmentManager
.beginTransaction()
.replace(R.id.log_container, fragment)
.commit()
}
class RawFragment : Fragment() {
private lateinit var binding: FragmentLogRawBinding
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentLogRawBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
binding.rawLogText.text = (requireActivity() as LogActivity).rawLogs
}
}
}

View file

@ -1,197 +0,0 @@
package com.tsng.hidemyapplist.app.ui.activities
import android.annotation.SuppressLint
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.text.Html
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.content.res.AppCompatResources
import androidx.preference.PreferenceManager
import com.google.android.gms.ads.AdRequest
import com.google.android.gms.ads.MobileAds
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.tsng.hidemyapplist.BuildConfig
import com.tsng.hidemyapplist.Magic
import com.tsng.hidemyapplist.R
import com.tsng.hidemyapplist.app.MyApplication
import com.tsng.hidemyapplist.app.MyApplication.Companion.appContext
import com.tsng.hidemyapplist.app.SubmitConfigService
import com.tsng.hidemyapplist.app.helpers.ServiceHelper
import com.tsng.hidemyapplist.app.makeToast
import com.tsng.hidemyapplist.databinding.ActivityMainBinding
import okhttp3.OkHttpClient
import okhttp3.Request
import org.json.JSONObject
import java.util.*
import kotlin.concurrent.thread
class MainActivity : AppCompatActivity(), View.OnClickListener {
private lateinit var binding: ActivityMainBinding
@SuppressLint("PackageManagerGetSignatures")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val sig = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
val s = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNING_CERTIFICATES).signingInfo
s.apkContentsSigners[0].toByteArray()
} else {
val s = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES).signatures
s[0].toByteArray()
}
if (sig.contentEquals(Magic.magicNumbers)) {
startService(Intent(this, SubmitConfigService::class.java))
}
binding = ActivityMainBinding.inflate(layoutInflater)
binding.menuDetectionTest.setOnClickListener(this)
binding.menuTemplateManage.setOnClickListener(this)
binding.menuScopeManage.setOnClickListener(this)
binding.menuLogs.setOnClickListener(this)
binding.menuSettings.setOnClickListener(this)
binding.menuAbout.setOnClickListener(this)
setContentView(binding.root)
setSupportActionBar(findViewById(R.id.toolbar))
MobileAds.initialize(appContext)
binding.adBanner.loadAd(AdRequest.Builder().build())
makeUpdateAlert()
}
@SuppressLint("SetTextI18n")
override fun onResume() {
super.onResume()
val serviceVersion = ServiceHelper.getServiceVersion()
if (MyApplication.isModuleActivated) {
if (serviceVersion != 0) {
binding.moduleStatusCard.setCardBackgroundColor(getColor(run {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) android.R.color.system_accent1_600
else R.color.primary
}))
binding.moduleStatusIcon.setImageDrawable(AppCompatResources.getDrawable(this, R.drawable.ic_activited))
binding.moduleStatusText.text = getString(R.string.xposed_activated)
} else {
binding.moduleStatusCard.setCardBackgroundColor(getColor(R.color.error))
binding.moduleStatusIcon.setImageDrawable(AppCompatResources.getDrawable(this, R.drawable.ic_service_not_running))
binding.moduleStatusText.text = getString(R.string.xposed_activated)
}
} else {
binding.moduleStatusCard.setCardBackgroundColor(getColor(R.color.gray))
binding.moduleStatusIcon.setImageDrawable(AppCompatResources.getDrawable(this, R.drawable.ic_not_activated))
binding.moduleStatusText.text = getString(R.string.xposed_not_activated)
}
if (serviceVersion != 0) {
binding.serviceStatusText.text =
if (serviceVersion != icu.nullptr.hidemyapplist.common.BuildConfig.SERVICE_VERSION)
getString(R.string.xposed_service_old)
else
getString(R.string.xposed_service_on) + " [$serviceVersion]"
val text = getString(R.string.xposed_serve_times).split("#")
binding.serveTimes.visibility = View.VISIBLE
binding.serveTimes.text = text[0] + ServiceHelper.getServeTimes() + text[2]
} else {
binding.serveTimes.visibility = View.GONE
binding.serviceStatusText.text = getString(R.string.xposed_service_off)
}
}
override fun onClick(v: View) {
when (v.id) {
R.id.menu_detection_test -> {
val intent = packageManager.getLaunchIntentForPackage("icu.nullptr.applistdetector")
if (intent == null) {
MaterialAlertDialogBuilder(this)
.setTitle(R.string.download_test_app_title)
.setMessage(R.string.download_test_app_message)
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(android.R.string.ok) { _, _ ->
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/Dr-TSNG/ApplistDetector/releases")))
}
.show()
} else startActivity(intent)
}
R.id.menu_template_manage ->
startActivity(
Intent(this, ModuleActivity::class.java)
.putExtra("Fragment", ModuleActivity.Fragment.TEMPLATE_MANAGE)
)
R.id.menu_scope_manage ->
startActivity(
Intent(this, ModuleActivity::class.java)
.putExtra("Fragment", ModuleActivity.Fragment.SCOPE_MANAGE)
)
R.id.menu_logs ->
if (ServiceHelper.getServiceVersion() == 0) makeToast(R.string.xposed_service_off)
else startActivity(Intent(this, LogActivity::class.java))
R.id.menu_settings -> startActivity(
Intent(this, ModuleActivity::class.java)
.putExtra("Fragment", ModuleActivity.Fragment.SETTINGS)
)
R.id.menu_about -> startActivity(Intent(this, AboutActivity::class.java))
}
}
private fun makeUpdateAlert() {
if (getSharedPreferences("settings", MODE_PRIVATE).getBoolean("disableUpdate", false)) return
val pref = PreferenceManager.getDefaultSharedPreferences(this)
val oldVersion = pref.getInt("lastVersion", 0)
thread {
try {
val client = OkHttpClient()
val responseData = client.newCall(
Request.Builder()
.url("https://cdn.jsdelivr.net/gh/Dr-TSNG/Hide-My-Applist@updates/updates/latest_version.json")
.build()
).execute().body?.string()
if (responseData != null) {
val json = JSONObject(responseData)
var data = json["Stable"] as JSONObject
var updateLogURL = "https://cdn.jsdelivr.net/gh/Dr-TSNG/Hide-My-Applist@updates/updates/stable-"
if (getSharedPreferences("settings", MODE_PRIVATE).getBoolean("receiveBetaUpdate", false))
if (json["Beta"] != false) {
data = json["Beta"] as JSONObject
updateLogURL = "https://cdn.jsdelivr.net/gh/Dr-TSNG/Hide-My-Applist@updates/updates/beta-"
}
updateLogURL += if (Locale.getDefault().language.contains("zh")) "zh" else "en"
updateLogURL += ".html"
val updateLog = client.newCall(
Request.Builder()
.url(updateLogURL)
.build()
).execute().body?.string()
val githubDownloadUri = Uri.parse(data["DownloadURL"] as String)
if (data.getInt("VersionCode") > BuildConfig.VERSION_CODE) runOnUiThread {
MaterialAlertDialogBuilder(this)
.setTitle(getString(R.string.new_update) + data["VersionName"])
.setMessage(Html.fromHtml(updateLog, Html.FROM_HTML_MODE_COMPACT))
.setPositiveButton("GitHub") { _, _ ->
startActivity(Intent(Intent.ACTION_VIEW, githubDownloadUri))
}
.setNegativeButton("TG Channel") { _, _ ->
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("https://t.me/HideMyApplist")))
}
.setNeutralButton(android.R.string.cancel, null)
.setCancelable(false).show()
} else if (oldVersion < BuildConfig.VERSION_CODE) runOnUiThread {
MaterialAlertDialogBuilder(this)
.setTitle(R.string.update_logs)
.setMessage(Html.fromHtml(updateLog, Html.FROM_HTML_MODE_COMPACT))
.setPositiveButton(android.R.string.ok, null)
.setCancelable(false)
.show()
}
pref.edit().putInt("lastVersion", BuildConfig.VERSION_CODE).apply()
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
}

View file

@ -1,51 +0,0 @@
package com.tsng.hidemyapplist.app.ui.activities
import android.os.Build
import android.os.Bundle
import android.view.WindowManager
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.WindowCompat
import com.tsng.hidemyapplist.R
import com.tsng.hidemyapplist.app.startFragment
import com.tsng.hidemyapplist.app.ui.fragments.ScopeManageFragment
import com.tsng.hidemyapplist.app.ui.fragments.SettingsFragment
import com.tsng.hidemyapplist.app.ui.fragments.TemplateManageFragment
import com.tsng.hidemyapplist.databinding.ActivityModuleBinding
class ModuleActivity : AppCompatActivity() {
enum class Fragment {
TEMPLATE_MANAGE,
SCOPE_MANAGE,
SETTINGS
}
private lateinit var binding: ActivityModuleBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
WindowCompat.setDecorFitsSystemWindows(window, true)
binding = ActivityModuleBinding.inflate(layoutInflater)
setContentView(binding.root)
setSupportActionBar(findViewById(R.id.toolbar))
supportActionBar?.setDisplayHomeAsUpEnabled(true)
when (intent.extras?.get("Fragment") as Fragment) {
Fragment.TEMPLATE_MANAGE -> {
setTitle(R.string.title_template_manage)
startFragment(TemplateManageFragment(), false)
}
Fragment.SCOPE_MANAGE -> {
setTitle(R.string.title_scope_manage)
startFragment(ScopeManageFragment(), false)
}
Fragment.SETTINGS -> {
setTitle(R.string.title_settings)
startFragment(SettingsFragment(), false)
}
}
}
override fun onSupportNavigateUp(): Boolean {
onBackPressed()
return true
}
}

View file

@ -1,87 +0,0 @@
package com.tsng.hidemyapplist.app.ui.adapters
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.*
import androidx.recyclerview.widget.RecyclerView
import com.tsng.hidemyapplist.R
import com.tsng.hidemyapplist.app.MyApplication.Companion.appContext
import com.tsng.hidemyapplist.app.helpers.AppInfoHelper.MyAppInfo
import java.util.*
class AppSelectAdapter(
var isShowSystemApp: Boolean,
private val hasCheckBox: Boolean,
private val appList: List<MyAppInfo>,
private val selectedApps: MutableSet<String>,
private val onClick: (ViewHolder.() -> Unit)? = null
) : Filterable,
RecyclerView.Adapter<AppSelectAdapter.ViewHolder>() {
inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val imageView: ImageView = view.findViewById(R.id.app_icon)
val appNameTextView: TextView = view.findViewById(R.id.app_name)
val packageNameTextView: TextView = view.findViewById(R.id.app_package_name)
val summaryTextView: TextView = view.findViewById(R.id.app_summary)
val checkBox: CheckBox = view.findViewById(R.id.checkbox)
}
private var mFilteredList: List<MyAppInfo> = listOf()
init {
filter.filter("")
}
override fun getFilter() = object : Filter() {
override fun performFiltering(constraint: CharSequence): FilterResults {
val filteredList = mutableListOf<MyAppInfo>()
for (appInfo in appList) {
if (!appInfo.isSystemApp || isShowSystemApp)
if (constraint.isEmpty() ||
appInfo.appName.lowercase(Locale.getDefault()).contains(constraint) ||
appInfo.packageName.lowercase(Locale.getDefault()).contains(constraint)
) filteredList.add(appInfo)
}
return FilterResults().apply { values = filteredList }
}
override fun publishResults(constraint: CharSequence, results: FilterResults) {
mFilteredList = results.values as List<MyAppInfo>
notifyDataSetChanged()
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.recycler_app_select, parent, false)
val viewHolder = ViewHolder(view)
onClick?.let { viewHolder.it() }
if (!hasCheckBox) viewHolder.checkBox.visibility = View.GONE
return viewHolder
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val appInfo = mFilteredList[position]
holder.imageView.setImageDrawable(appInfo.icon)
holder.appNameTextView.text = appInfo.appName
holder.packageNameTextView.text = appInfo.packageName
if (!hasCheckBox && selectedApps.contains(appInfo.packageName))
holder.appNameTextView.setTextColor(appContext.resources.getColor(android.R.color.holo_green_light))
else
holder.appNameTextView.setTextColor(appContext.resources.getColor(android.R.color.black))
if (appInfo.isSystemApp) {
holder.summaryTextView.visibility = View.VISIBLE
holder.summaryTextView.setText(R.string.system_app)
} else holder.summaryTextView.visibility = View.INVISIBLE
if (hasCheckBox) {
holder.checkBox.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) selectedApps.add(appInfo.packageName)
else selectedApps.remove(appInfo.packageName)
}
holder.checkBox.isChecked = selectedApps.contains(appInfo.packageName)
}
}
override fun getItemCount() = mFilteredList.size
}

View file

@ -1,54 +0,0 @@
package com.tsng.hidemyapplist.app.ui.adapters
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.RecyclerView
import com.tsng.hidemyapplist.R
import com.tsng.hidemyapplist.app.startFragment
import com.tsng.hidemyapplist.app.ui.fragments.TemplateSettingsFragment
class TemplateListAdapter(
private val templateList: List<Pair<String, Boolean>>,
private val mFragment: Fragment
) : RecyclerView.Adapter<TemplateListAdapter.ViewHolder>() {
inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
var isWhitelist = false
lateinit var templateName: String
val imageView: ImageView = view.findViewById(R.id.template_icon)
val textView: TextView = view.findViewById(R.id.template_name)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.recycler_templates, parent, false)
val viewHolder = ViewHolder(view)
viewHolder.itemView.setOnClickListener {
mFragment.startFragment(
TemplateSettingsFragment.newInstance(
viewHolder.isWhitelist,
viewHolder.templateName
)
)
}
return viewHolder
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val pair = templateList[position]
holder.templateName = pair.first
holder.isWhitelist = pair.second
holder.imageView.setImageResource(
if (pair.second) R.drawable.ic_file_white
else R.drawable.ic_file_black
)
holder.textView.text = pair.first
}
override fun getItemCount() = templateList.size
}

View file

@ -1,120 +0,0 @@
package com.tsng.hidemyapplist.app.ui.fragments
import android.os.Bundle
import android.view.*
import androidx.appcompat.widget.SearchView
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
import androidx.fragment.app.setFragmentResult
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import com.tsng.hidemyapplist.R
import com.tsng.hidemyapplist.app.helpers.AppInfoHelper
import com.tsng.hidemyapplist.app.ui.adapters.AppSelectAdapter
import com.tsng.hidemyapplist.app.ui.views.Ads
import com.tsng.hidemyapplist.databinding.FragmentAppSelectBinding
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.text.Collator
import java.util.*
import kotlin.concurrent.thread
class AppSelectFragment : Fragment() {
companion object {
@JvmStatic
fun newInstance(selectedApps: Array<String>, requestKey: String? = null) =
AppSelectFragment().apply {
arguments = bundleOf(
"selectedApps" to selectedApps,
"requestKey" to requestKey
)
}
}
private lateinit var binding: FragmentAppSelectBinding
private lateinit var selectedApps: MutableSet<String>
private lateinit var requestKey: String
private var adapter: AppSelectAdapter? = null
private var isShowSystemApp = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
selectedApps = arguments?.getStringArray("selectedApps")?.toHashSet() ?: mutableSetOf()
requestKey = arguments?.getString("requestKey", null) ?: "appSelectResult"
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentAppSelectBinding.inflate(inflater, container, false)
binding.adBanner.loadAd(Ads.appSelectAd)
binding.refreshLayout.setOnRefreshListener {
lifecycleScope.launch { refresh() }
}
binding.refreshLayout.isRefreshing = true
lifecycleScope.launch { refresh() }
return binding.root
}
override fun onDestroyView() {
setFragmentResult(
requestKey,
bundleOf("selectedApps" to selectedApps.toTypedArray())
)
super.onDestroyView()
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.appselect, menu)
val searchView = menu.findItem(R.id.toolbar_search).actionView as SearchView
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String) = false
override fun onQueryTextChange(newText: String): Boolean {
adapter?.filter?.filter(newText.lowercase(Locale.getDefault()))
return true
}
})
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.toolbar_show_system_apps -> {
isShowSystemApp = !isShowSystemApp
item.isChecked = isShowSystemApp
adapter?.isShowSystemApp = isShowSystemApp
adapter?.filter?.filter("")
}
else -> return false
}
return true
}
private suspend fun refresh() {
initAppListView()
withContext(Dispatchers.Main) {
binding.refreshLayout.isRefreshing = false
}
}
private suspend fun initAppListView() {
val appInfoList = AppInfoHelper.getAppInfoList()
appInfoList.sortWith { o1, o2 ->
val c1 = selectedApps.contains(o1.packageName)
val c2 = selectedApps.contains(o2.packageName)
if (c1 != c2) return@sortWith if (c1) -1 else 1
Collator.getInstance(Locale.getDefault()).compare(o1.appName, o2.appName)
}
withContext(Dispatchers.Main) {
binding.appSelect.layoutManager = LinearLayoutManager(activity)
adapter = AppSelectAdapter(isShowSystemApp, true, appInfoList, selectedApps)
binding.appSelect.adapter = adapter
}
}
}

View file

@ -1,151 +0,0 @@
package com.tsng.hidemyapplist.app.ui.fragments
import android.content.DialogInterface
import android.content.pm.PackageManager
import android.os.Bundle
import androidx.core.os.bundleOf
import androidx.fragment.app.setFragmentResultListener
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreferenceCompat
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.tsng.hidemyapplist.R
import com.tsng.hidemyapplist.app.JsonConfigManager
import com.tsng.hidemyapplist.app.JsonConfigManager.globalConfig
import com.tsng.hidemyapplist.app.MyApplication.Companion.appContext
import com.tsng.hidemyapplist.app.helpers.AppConfigDataStorage
import com.tsng.hidemyapplist.app.makeToast
import com.tsng.hidemyapplist.app.startFragment
import com.tsng.hidemyapplist.app.ui.views.FilterRulesView
import icu.nullptr.hidemyapplist.common.JsonConfig
class AppSettingsFragment : PreferenceFragmentCompat() {
companion object {
@JvmStatic
fun newInstance(packageName: String) =
AppSettingsFragment().apply {
arguments = bundleOf("packageName" to packageName)
}
}
private lateinit var packageName: String
private lateinit var appConfig: JsonConfig.AppConfig
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
packageName = requireArguments().getString("packageName")!!
appConfig = globalConfig.scope.getOrDefault(packageName, JsonConfig.AppConfig())
preferenceManager.preferenceDataStore = AppConfigDataStorage(packageName, appConfig)
setPreferencesFromResource(R.xml.app_preferences, rootKey)
preferenceScreen.findPreference<Preference>("appInfo")?.let {
val appInfo = appContext.packageManager.getApplicationInfo(
packageName,
PackageManager.MATCH_UNINSTALLED_PACKAGES or PackageManager.MATCH_DISABLED_COMPONENTS
)
it.icon = appInfo.loadIcon(appContext.packageManager)
it.title = appInfo.loadLabel(appContext.packageManager)
it.summary = packageName
}
preferenceScreen.findPreference<Preference>("copyConfig")
?.setOnPreferenceClickListener {
startFragment(AppSelectFragment.newInstance(arrayOf(packageName), "copyConfig"))
true
}
setFragmentResultListener("copyConfig") { _, bundle ->
bundle.getStringArray("selectedApps")?.let { arr ->
JsonConfigManager.edit {
arr.forEach { scope[it] = appConfig }
}
makeToast(R.string.copied)
}
}
preferenceScreen.findPreference<SwitchPreferenceCompat>("useWhitelist")
?.setOnPreferenceClickListener {
appConfig.applyTemplates.clear()
appConfig.extraAppList.clear()
updateView()
true
}
preferenceScreen.findPreference<Preference>("applyTemplates")
?.setOnPreferenceClickListener {
val list = mutableListOf<String>()
val checked = mutableListOf<Boolean>()
for ((name, template) in globalConfig.templates)
if (appConfig.useWhitelist == template.isWhitelist) {
list.add(name)
checked.add(appConfig.applyTemplates.contains(name))
}
val arrayList = list.toTypedArray()
val arrayChecked = checked.toBooleanArray()
MaterialAlertDialogBuilder(requireActivity())
.setTitle(R.string.template_choose)
.setMultiChoiceItems(
arrayList,
arrayChecked
) { _: DialogInterface, i: Int, b: Boolean -> arrayChecked[i] = b }
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(android.R.string.ok) { _, _ ->
for (i in arrayList.indices)
if (arrayChecked[i]) appConfig.applyTemplates.add(arrayList[i])
else appConfig.applyTemplates.remove(arrayList[i])
updateView()
}
.show()
true
}
preferenceScreen.findPreference<Preference>("extraAppList")
?.setOnPreferenceClickListener {
startFragment(
AppSelectFragment.newInstance(
appConfig.extraAppList.toTypedArray(),
"extraAppList"
)
)
true
}
setFragmentResultListener("extraAppList") { _, bundle ->
bundle.getStringArray("selectedApps")?.let {
appConfig.extraAppList.clear()
appConfig.extraAppList.addAll(it)
updateView()
}
}
preferenceScreen.findPreference<Preference>("extraQueryParamRules")
?.setOnPreferenceClickListener {
FilterRulesView.show(requireActivity(), appConfig.extraQueryParamRules) {
it.title = getString(
R.string.template_extra_query_param_rules_count,
appConfig.extraQueryParamRules.size
)
}
true
}
updateView()
}
private fun updateView() {
preferenceScreen.findPreference<Preference>("applyTemplates")?.title =
getString(R.string.template_applied_count, appConfig.applyTemplates.size)
preferenceScreen.findPreference<Preference>("extraAppList")?.title =
getString(
if (appConfig.useWhitelist) R.string.template_extra_apps_visible_count
else R.string.template_extra_apps_invisible_count,
appConfig.extraAppList.size
)
preferenceScreen.findPreference<Preference>("extraQueryParamRules")?.title =
getString(
R.string.template_extra_query_param_rules_count,
appConfig.extraQueryParamRules.size
)
}
}

View file

@ -1,110 +0,0 @@
package com.tsng.hidemyapplist.app.ui.fragments
import android.os.Bundle
import android.view.*
import androidx.appcompat.widget.SearchView
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import com.tsng.hidemyapplist.BuildConfig
import com.tsng.hidemyapplist.R
import com.tsng.hidemyapplist.app.JsonConfigManager.globalConfig
import com.tsng.hidemyapplist.app.helpers.AppInfoHelper
import com.tsng.hidemyapplist.app.startFragment
import com.tsng.hidemyapplist.app.ui.adapters.AppSelectAdapter
import com.tsng.hidemyapplist.app.ui.views.Ads
import com.tsng.hidemyapplist.databinding.FragmentAppSelectBinding
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.text.Collator
import java.util.*
class ScopeManageFragment : Fragment() {
private lateinit var binding: FragmentAppSelectBinding
private var adapter: AppSelectAdapter? = null
private var isShowSystemApp = false
private var touchedItemPosition = -1
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentAppSelectBinding.inflate(inflater, container, false)
binding.adBanner.loadAd(Ads.appSelectAd)
binding.refreshLayout.setOnRefreshListener {
lifecycleScope.launch { refresh() }
}
binding.refreshLayout.isRefreshing = true
lifecycleScope.launch { refresh() }
return binding.root
}
override fun onHiddenChanged(hidden: Boolean) {
if (!hidden && touchedItemPosition != -1)
adapter?.notifyItemChanged(touchedItemPosition)
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.appselect, menu)
val searchView = menu.findItem(R.id.toolbar_search).actionView as SearchView
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String) = false
override fun onQueryTextChange(newText: String): Boolean {
adapter?.filter?.filter(newText.lowercase(Locale.getDefault()))
return true
}
})
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.toolbar_show_system_apps -> {
isShowSystemApp = !isShowSystemApp
item.isChecked = isShowSystemApp
adapter?.isShowSystemApp = isShowSystemApp
adapter?.filter?.filter("")
}
else -> return false
}
return true
}
private suspend fun refresh() {
initAppListView()
withContext(Dispatchers.Main) {
binding.refreshLayout.isRefreshing = false
}
}
private suspend fun initAppListView() {
val appInfoList = AppInfoHelper.getAppInfoList()
appInfoList.removeIf { it.packageName == BuildConfig.APPLICATION_ID }
val selectedApps = globalConfig.scope.keys
appInfoList.sortWith { o1, o2 ->
val b1 = selectedApps.contains(o1.packageName)
val b2 = selectedApps.contains(o2.packageName)
if (b1 != b2) return@sortWith if (b1) -1 else 1
Collator.getInstance(Locale.getDefault()).compare(o1.appName, o2.appName)
}
withContext(Dispatchers.Main) {
binding.appSelect.layoutManager = LinearLayoutManager(activity)
adapter = AppSelectAdapter(isShowSystemApp, false, appInfoList, selectedApps) {
itemView.setOnClickListener {
touchedItemPosition = layoutPosition
val packageName = packageNameTextView.text.toString()
startFragment(AppSettingsFragment.newInstance(packageName))
}
}
binding.appSelect.adapter = adapter
}
}
}

View file

@ -1,167 +0,0 @@
package com.tsng.hidemyapplist.app.ui.fragments
import android.content.ComponentName
import android.content.pm.PackageManager
import android.os.Bundle
import androidx.activity.result.contract.ActivityResultContracts
import androidx.preference.ListPreference
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreferenceCompat
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.topjohnwu.superuser.Shell
import com.tsng.hidemyapplist.R
import com.tsng.hidemyapplist.app.JsonConfigManager
import com.tsng.hidemyapplist.app.MyApplication.Companion.appContext
import com.tsng.hidemyapplist.app.helpers.ServiceHelper
import com.tsng.hidemyapplist.app.makeToast
import icu.nullptr.hidemyapplist.common.BuildConfig
import icu.nullptr.hidemyapplist.common.JsonConfig
class SettingsFragment : PreferenceFragmentCompat() {
private val backupImportSAFLauncher =
registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
if (uri == null) return@registerForActivityResult
try {
val backupJson = appContext.contentResolver
.openInputStream(uri)?.reader().use { it?.readText() }
?: throw RuntimeException(getString(R.string.settings_import_file_damaged))
val backup: JsonConfig
try {
backup = JsonConfig.parse(backupJson)
} catch (e: Exception) {
throw RuntimeException(getString(R.string.settings_import_file_damaged), e)
}
if (backup.configVersion > BuildConfig.SERVICE_VERSION)
throw RuntimeException(getString(R.string.settings_import_app_version_too_old))
if (backup.configVersion < BuildConfig.MIN_BACKUP_VERSION)
throw RuntimeException(getString(R.string.settings_import_backup_version_too_old))
JsonConfigManager.edit {
templates.clear()
templates.putAll(backup.templates)
scope.clear()
scope.putAll(backup.scope)
}
makeToast(R.string.settings_import_successful)
} catch (e: Exception) {
e.printStackTrace()
MaterialAlertDialogBuilder(requireActivity())
.setCancelable(false)
.setTitle(R.string.settings_import_failed)
.setMessage(e.message)
.setPositiveButton(android.R.string.ok, null)
.setNegativeButton(R.string.show_crash_log) { _, _ ->
MaterialAlertDialogBuilder(requireActivity())
.setCancelable(false)
.setTitle(R.string.settings_import_failed)
.setMessage(e.stackTraceToString())
.setPositiveButton(android.R.string.ok, null)
.show()
}
.show()
}
}
private val backupExportSAFLauncher =
registerForActivityResult(ActivityResultContracts.CreateDocument()) { uri ->
if (uri == null) return@registerForActivityResult
JsonConfigManager.configFile.inputStream().use { fis ->
appContext.contentResolver.openOutputStream(uri).use { fos ->
if (fos == null) makeToast(R.string.settings_export_failed)
else fis.copyTo(fos)
}
}
makeToast(R.string.settings_exported)
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
preferenceManager.sharedPreferencesName = "settings"
setPreferencesFromResource(R.xml.settings_preferences, rootKey)
module()
service()
backup()
}
private fun module() {
preferenceScreen.findPreference<SwitchPreferenceCompat>("detailLog")
?.setOnPreferenceChangeListener { _, newValue ->
JsonConfigManager.edit { detailLog = newValue as Boolean }
true
}
preferenceScreen.findPreference<ListPreference>("maxLogSize")?.apply {
summary = entry
setOnPreferenceChangeListener { _, newValue ->
JsonConfigManager.edit {
maxLogSize = (newValue as String).toInt()
}
summary = entries[findIndexOfValue(newValue as String)]
true
}
}
preferenceScreen.findPreference<SwitchPreferenceCompat>("hideIcon")
?.setOnPreferenceChangeListener { _, newValue ->
val component =
ComponentName(appContext, "com.tsng.hidemyapplist.MainActivityLauncher")
val status =
if (newValue == true) PackageManager.COMPONENT_ENABLED_STATE_DISABLED
else PackageManager.COMPONENT_ENABLED_STATE_ENABLED
appContext.packageManager
.setComponentEnabledSetting(component, status, PackageManager.DONT_KILL_APP)
true
}
}
private fun service() {
preferenceScreen.findPreference<Preference>("stopSystemService")
?.setOnPreferenceClickListener {
if (ServiceHelper.getServiceVersion() != 0) {
MaterialAlertDialogBuilder(requireActivity())
.setTitle(R.string.settings_is_clean_env)
.setMessage(R.string.settings_is_clean_env_summary)
.setPositiveButton(R.string.yes) { _, _ ->
ServiceHelper.stopSystemService(true)
makeToast(R.string.settings_stop_system_service)
}
.setNegativeButton(R.string.no) { _, _ ->
ServiceHelper.stopSystemService(false)
makeToast(R.string.settings_stop_system_service)
}
.setNeutralButton(android.R.string.cancel, null)
.show()
} else makeToast(R.string.xposed_service_off)
true
}
preferenceScreen.findPreference<Preference>("forceCleanEnv")
?.setOnPreferenceClickListener {
MaterialAlertDialogBuilder(requireActivity())
.setTitle(R.string.settings_force_clean_env)
.setMessage(R.string.settings_is_clean_env_summary)
.setPositiveButton(android.R.string.ok) { _, _ ->
val result =
Shell.su("rm -rf /data/misc/hide_my_applist /data/misc/hma_selinux_test")
.exec().isSuccess
if (result) makeToast(R.string.settings_force_clean_env_toast_successful)
else makeToast(R.string.settings_force_clean_env_toast_failed)
}
.setNegativeButton(android.R.string.cancel, null)
.show()
true
}
}
private fun backup() {
preferenceScreen.findPreference<Preference>("importConfig")
?.setOnPreferenceClickListener {
backupImportSAFLauncher.launch("application/json")
true
}
preferenceScreen.findPreference<Preference>("exportConfig")
?.setOnPreferenceClickListener {
backupExportSAFLauncher.launch("HMA Config.json")
true
}
}
}

View file

@ -1,50 +0,0 @@
package com.tsng.hidemyapplist.app.ui.fragments
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import com.tsng.hidemyapplist.app.JsonConfigManager.globalConfig
import com.tsng.hidemyapplist.app.startFragment
import com.tsng.hidemyapplist.app.ui.adapters.TemplateListAdapter
import com.tsng.hidemyapplist.databinding.FragmentTemplateManageBinding
import java.text.Collator
import java.util.*
class TemplateManageFragment : Fragment() {
private lateinit var binding: FragmentTemplateManageBinding
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentTemplateManageBinding.inflate(inflater, container, false)
binding.createBlacklist.setOnClickListener {
startFragment(TemplateSettingsFragment.newInstance(false, null))
}
binding.createWhitelist.setOnClickListener {
startFragment(TemplateSettingsFragment.newInstance(true, null))
}
buildTemplateList()
return binding.root
}
override fun onHiddenChanged(hidden: Boolean) {
if (!hidden) buildTemplateList()
}
private fun buildTemplateList() {
val adapterList = mutableListOf<Pair<String, Boolean>>()
for ((name, template) in globalConfig.templates)
adapterList.add(name to template.isWhitelist)
adapterList.sortWith { o1, o2 ->
if (o1.second != o2.second) if (o1.second) 1 else -1
else Collator.getInstance(Locale.getDefault()).compare(o1.first, o2.first)
}
binding.templateList.layoutManager = LinearLayoutManager(activity)
binding.templateList.adapter = TemplateListAdapter(adapterList, this)
}
}

View file

@ -1,158 +0,0 @@
package com.tsng.hidemyapplist.app.ui.fragments
import android.app.Activity
import android.os.Bundle
import android.view.*
import androidx.fragment.app.Fragment
import androidx.fragment.app.setFragmentResultListener
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.tsng.hidemyapplist.R
import com.tsng.hidemyapplist.app.JsonConfigManager
import com.tsng.hidemyapplist.app.JsonConfigManager.globalConfig
import com.tsng.hidemyapplist.app.makeToast
import com.tsng.hidemyapplist.app.startFragment
import com.tsng.hidemyapplist.app.ui.views.FilterRulesView
import com.tsng.hidemyapplist.databinding.FragmentTemplateSettingsBinding
import icu.nullptr.hidemyapplist.common.JsonConfig
class TemplateSettingsFragment : Fragment() {
companion object {
@JvmStatic
fun newInstance(isWhitelist: Boolean, templateName: String?) =
TemplateSettingsFragment().apply {
arguments = Bundle().apply {
putBoolean("isWhitelist", isWhitelist)
putString("templateName", templateName)
}
}
}
private lateinit var binding: FragmentTemplateSettingsBinding
private lateinit var activity: Activity
private lateinit var template: JsonConfig.Template
private var oldTemplateName: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
activity = requireActivity()
val isWhitelist = requireArguments().getBoolean("isWhitelist")
oldTemplateName = requireArguments().getString("templateName")
template = if (oldTemplateName == null) JsonConfig.Template(isWhitelist)
else globalConfig.templates[oldTemplateName]!!.copy()
setFragmentResultListener("appSelectResult") { _, bundle ->
bundle.getStringArray("selectedApps")?.let {
template.appList.clear()
template.appList.addAll(it)
binding.appList.setListCount(template.appList.size)
}
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentTemplateSettingsBinding.inflate(inflater, container, false)
if (oldTemplateName != null) binding.templateName.setText(oldTemplateName)
binding.appList.setRawText(
if (template.isWhitelist) getString(R.string.template_apps_visible_count)
else getString(R.string.template_apps_invisible_count)
)
initAppListView()
initQueryParamRulesView()
return binding.root
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.delete_and_save, menu)
if (oldTemplateName == null)
menu.findItem(R.id.toolbar_delete).isVisible = false
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.toolbar_delete -> {
MaterialAlertDialogBuilder(activity)
.setTitle(R.string.template_delete)
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(android.R.string.ok) { _, _ ->
JsonConfigManager.edit {
for ((_, appConfig) in scope)
appConfig.applyTemplates.remove(oldTemplateName)
templates.remove(oldTemplateName)
}
activity.onBackPressed()
}
.show()
return true
}
R.id.toolbar_save -> {
val newTemplateName = binding.templateName.text.toString()
if (newTemplateName.isEmpty()) {
makeToast(R.string.template_name_cannot_be_empty)
return true
}
if (oldTemplateName == null) { // A new template
if (globalConfig.templates.containsKey(newTemplateName)) {
makeToast(R.string.template_name_already_exist)
} else {
JsonConfigManager.edit { templates[newTemplateName] = template }
activity.onBackPressed()
}
} else when { // Existing template
oldTemplateName == newTemplateName -> {
JsonConfigManager.edit { templates[newTemplateName] = template }
activity.onBackPressed()
}
globalConfig.templates.containsKey(newTemplateName) -> {
makeToast(R.string.template_name_already_exist)
}
else -> {
JsonConfigManager.edit {
for ((_, appConfig) in scope)
with(appConfig.applyTemplates) {
if (contains(oldTemplateName)) {
remove(oldTemplateName)
add(newTemplateName)
}
}
templates.remove(oldTemplateName)
templates[newTemplateName] = template
}
activity.onBackPressed()
}
}
return true
}
else -> return false
}
}
private fun initAppListView() {
var cnt = 0
globalConfig.scope.forEach { (_, appConfig) ->
if (appConfig.applyTemplates.contains(oldTemplateName)) cnt++
}
binding.effectiveApps.setListCount(cnt)
with(binding.appList) {
setListCount(template.appList.size)
setOnButtonClickListener {
startFragment(AppSelectFragment.newInstance(template.appList.toTypedArray()))
}
}
}
private fun initQueryParamRulesView() {
with(binding.queryParamRules) {
setListCount(template.queryParamRules.size)
setOnButtonClickListener {
FilterRulesView.show(activity, template.queryParamRules) {
setListCount(template.queryParamRules.size)
}
}
}
}
}

Some files were not shown because too many files have changed in this diff Show more