diff --git a/app/build.gradle.kts b/app/build.gradle.kts index b75d5ea..64a8afb 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -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") +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 0aee042..971a41d 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -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 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7ba8ca4..92cb00c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -10,7 +10,7 @@ - - - - - - - - - @@ -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"> @@ -77,5 +58,4 @@ android:name="xposedscope" android:resource="@array/xposed_scope" /> - diff --git a/app/src/main/java/com/tsng/hidemyapplist/app/AppUtils.kt b/app/src/main/java/com/tsng/hidemyapplist/app/AppUtils.kt deleted file mode 100644 index 6cd3afc..0000000 --- a/app/src/main/java/com/tsng/hidemyapplist/app/AppUtils.kt +++ /dev/null @@ -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() -} diff --git a/app/src/main/java/com/tsng/hidemyapplist/app/JsonConfigManager.kt b/app/src/main/java/com/tsng/hidemyapplist/app/JsonConfigManager.kt deleted file mode 100644 index 33ced9d..0000000 --- a/app/src/main/java/com/tsng/hidemyapplist/app/JsonConfigManager.kt +++ /dev/null @@ -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()) - } - } - globalConfig.scope.forEach { (_, data) -> - JsonConfig.AppConfig::class.java.getDeclaredField("extraQueryParamRules").apply { - isAccessible = true - set(data, mutableSetOf()) - } - } - } -} diff --git a/app/src/main/java/com/tsng/hidemyapplist/app/MyApplication.kt b/app/src/main/java/com/tsng/hidemyapplist/app/MyApplication.kt deleted file mode 100644 index 336632e..0000000 --- a/app/src/main/java/com/tsng/hidemyapplist/app/MyApplication.kt +++ /dev/null @@ -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() - } - } -} diff --git a/app/src/main/java/com/tsng/hidemyapplist/app/SubmitConfigService.kt b/app/src/main/java/com/tsng/hidemyapplist/app/SubmitConfigService.kt deleted file mode 100644 index c38acd0..0000000 --- a/app/src/main/java/com/tsng/hidemyapplist/app/SubmitConfigService.kt +++ /dev/null @@ -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) - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tsng/hidemyapplist/app/helpers/AppInfoHelper.kt b/app/src/main/java/com/tsng/hidemyapplist/app/helpers/AppInfoHelper.kt deleted file mode 100644 index 8581a09..0000000 --- a/app/src/main/java/com/tsng/hidemyapplist/app/helpers/AppInfoHelper.kt +++ /dev/null @@ -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 - - fun getAppInfoList(): MutableList { - 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() - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tsng/hidemyapplist/app/helpers/PreferenceDataStorage.kt b/app/src/main/java/com/tsng/hidemyapplist/app/helpers/PreferenceDataStorage.kt deleted file mode 100644 index f73da34..0000000 --- a/app/src/main/java/com/tsng/hidemyapplist/app/helpers/PreferenceDataStorage.kt +++ /dev/null @@ -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?): MutableSet { - return JsonConfig.AppConfig::class.java.getDeclaredField(key) - .also { it.isAccessible = true } - .get(appConfig) as MutableSet - } - - override fun putStringSet(key: String, values: MutableSet?) { - JsonConfig.AppConfig::class.java.getDeclaredField(key) - .also { it.isAccessible = true } - .set(appConfig, values) - } -} diff --git a/app/src/main/java/com/tsng/hidemyapplist/app/ui/activities/LogActivity.kt b/app/src/main/java/com/tsng/hidemyapplist/app/ui/activities/LogActivity.kt deleted file mode 100644 index 535e927..0000000 --- a/app/src/main/java/com/tsng/hidemyapplist/app/ui/activities/LogActivity.kt +++ /dev/null @@ -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 - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tsng/hidemyapplist/app/ui/activities/MainActivity.kt b/app/src/main/java/com/tsng/hidemyapplist/app/ui/activities/MainActivity.kt deleted file mode 100644 index e2dfdb0..0000000 --- a/app/src/main/java/com/tsng/hidemyapplist/app/ui/activities/MainActivity.kt +++ /dev/null @@ -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() - } - } - } -} diff --git a/app/src/main/java/com/tsng/hidemyapplist/app/ui/activities/ModuleActivity.kt b/app/src/main/java/com/tsng/hidemyapplist/app/ui/activities/ModuleActivity.kt deleted file mode 100644 index 0f92cae..0000000 --- a/app/src/main/java/com/tsng/hidemyapplist/app/ui/activities/ModuleActivity.kt +++ /dev/null @@ -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 - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tsng/hidemyapplist/app/ui/adapters/AppSelectAdapter.kt b/app/src/main/java/com/tsng/hidemyapplist/app/ui/adapters/AppSelectAdapter.kt deleted file mode 100644 index 8ff2574..0000000 --- a/app/src/main/java/com/tsng/hidemyapplist/app/ui/adapters/AppSelectAdapter.kt +++ /dev/null @@ -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, - private val selectedApps: MutableSet, - private val onClick: (ViewHolder.() -> Unit)? = null -) : Filterable, - RecyclerView.Adapter() { - 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 = listOf() - - init { - filter.filter("") - } - - override fun getFilter() = object : Filter() { - override fun performFiltering(constraint: CharSequence): FilterResults { - val filteredList = mutableListOf() - 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 - 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 -} \ No newline at end of file diff --git a/app/src/main/java/com/tsng/hidemyapplist/app/ui/adapters/TemplateListAdapter.kt b/app/src/main/java/com/tsng/hidemyapplist/app/ui/adapters/TemplateListAdapter.kt deleted file mode 100644 index 0223a76..0000000 --- a/app/src/main/java/com/tsng/hidemyapplist/app/ui/adapters/TemplateListAdapter.kt +++ /dev/null @@ -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>, - private val mFragment: Fragment -) : RecyclerView.Adapter() { - 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 -} \ No newline at end of file diff --git a/app/src/main/java/com/tsng/hidemyapplist/app/ui/fragments/AppSelectFragment.kt b/app/src/main/java/com/tsng/hidemyapplist/app/ui/fragments/AppSelectFragment.kt deleted file mode 100644 index bf74244..0000000 --- a/app/src/main/java/com/tsng/hidemyapplist/app/ui/fragments/AppSelectFragment.kt +++ /dev/null @@ -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, requestKey: String? = null) = - AppSelectFragment().apply { - arguments = bundleOf( - "selectedApps" to selectedApps, - "requestKey" to requestKey - ) - } - } - - private lateinit var binding: FragmentAppSelectBinding - private lateinit var selectedApps: MutableSet - 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 - } - } -} diff --git a/app/src/main/java/com/tsng/hidemyapplist/app/ui/fragments/AppSettingsFragment.kt b/app/src/main/java/com/tsng/hidemyapplist/app/ui/fragments/AppSettingsFragment.kt deleted file mode 100644 index de8830c..0000000 --- a/app/src/main/java/com/tsng/hidemyapplist/app/ui/fragments/AppSettingsFragment.kt +++ /dev/null @@ -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("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("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("useWhitelist") - ?.setOnPreferenceClickListener { - appConfig.applyTemplates.clear() - appConfig.extraAppList.clear() - updateView() - true - } - - preferenceScreen.findPreference("applyTemplates") - ?.setOnPreferenceClickListener { - val list = mutableListOf() - val checked = mutableListOf() - 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("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("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("applyTemplates")?.title = - getString(R.string.template_applied_count, appConfig.applyTemplates.size) - - preferenceScreen.findPreference("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("extraQueryParamRules")?.title = - getString( - R.string.template_extra_query_param_rules_count, - appConfig.extraQueryParamRules.size - ) - } -} diff --git a/app/src/main/java/com/tsng/hidemyapplist/app/ui/fragments/ScopeManageFragment.kt b/app/src/main/java/com/tsng/hidemyapplist/app/ui/fragments/ScopeManageFragment.kt deleted file mode 100644 index 17f6256..0000000 --- a/app/src/main/java/com/tsng/hidemyapplist/app/ui/fragments/ScopeManageFragment.kt +++ /dev/null @@ -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 - } - } -} diff --git a/app/src/main/java/com/tsng/hidemyapplist/app/ui/fragments/SettingsFragment.kt b/app/src/main/java/com/tsng/hidemyapplist/app/ui/fragments/SettingsFragment.kt deleted file mode 100644 index 21942f6..0000000 --- a/app/src/main/java/com/tsng/hidemyapplist/app/ui/fragments/SettingsFragment.kt +++ /dev/null @@ -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("detailLog") - ?.setOnPreferenceChangeListener { _, newValue -> - JsonConfigManager.edit { detailLog = newValue as Boolean } - true - } - - preferenceScreen.findPreference("maxLogSize")?.apply { - summary = entry - setOnPreferenceChangeListener { _, newValue -> - JsonConfigManager.edit { - maxLogSize = (newValue as String).toInt() - } - summary = entries[findIndexOfValue(newValue as String)] - true - } - } - - preferenceScreen.findPreference("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("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("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("importConfig") - ?.setOnPreferenceClickListener { - backupImportSAFLauncher.launch("application/json") - true - } - preferenceScreen.findPreference("exportConfig") - ?.setOnPreferenceClickListener { - backupExportSAFLauncher.launch("HMA Config.json") - true - } - } -} diff --git a/app/src/main/java/com/tsng/hidemyapplist/app/ui/fragments/TemplateManageFragment.kt b/app/src/main/java/com/tsng/hidemyapplist/app/ui/fragments/TemplateManageFragment.kt deleted file mode 100644 index 3deaaa3..0000000 --- a/app/src/main/java/com/tsng/hidemyapplist/app/ui/fragments/TemplateManageFragment.kt +++ /dev/null @@ -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>() - 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) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tsng/hidemyapplist/app/ui/fragments/TemplateSettingsFragment.kt b/app/src/main/java/com/tsng/hidemyapplist/app/ui/fragments/TemplateSettingsFragment.kt deleted file mode 100644 index eb14245..0000000 --- a/app/src/main/java/com/tsng/hidemyapplist/app/ui/fragments/TemplateSettingsFragment.kt +++ /dev/null @@ -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) - } - } - } - } -} diff --git a/app/src/main/java/com/tsng/hidemyapplist/app/ui/views/Ads.kt b/app/src/main/java/com/tsng/hidemyapplist/app/ui/views/Ads.kt deleted file mode 100644 index de5c17b..0000000 --- a/app/src/main/java/com/tsng/hidemyapplist/app/ui/views/Ads.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.tsng.hidemyapplist.app.ui.views - -import com.google.android.gms.ads.AdRequest - -object Ads { - - val appSelectAd: AdRequest by lazy { - AdRequest.Builder().build() - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tsng/hidemyapplist/app/ui/views/FilterRulesView.kt b/app/src/main/java/com/tsng/hidemyapplist/app/ui/views/FilterRulesView.kt deleted file mode 100644 index ca16f70..0000000 --- a/app/src/main/java/com/tsng/hidemyapplist/app/ui/views/FilterRulesView.kt +++ /dev/null @@ -1,58 +0,0 @@ -package com.tsng.hidemyapplist.app.ui.views - -import android.app.Activity -import android.view.View -import android.widget.ArrayAdapter -import android.widget.Button -import android.widget.EditText -import android.widget.ListView -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import com.tsng.hidemyapplist.R - -object FilterRulesView { - @JvmStatic - fun show(activity: Activity, ruleSet: MutableSet, updateUi: (() -> Unit)? = null) { - val rules = ruleSet.toMutableList() - val adapter = ArrayAdapter(activity, android.R.layout.simple_list_item_1, rules) - MaterialAlertDialogBuilder(activity) - .setTitle(R.string.template_add_filter_rules) - .setView( - View.inflate( - activity, - R.layout.alert_customize_filter_rules, - null - ).apply { - findViewById(R.id.rule_list).apply { - this.adapter = adapter - setOnItemLongClickListener { _, _, position, _ -> - MaterialAlertDialogBuilder(activity) - .setTitle(R.string.template_delete_filter_rule) - .setMessage(rules[position]) - .setNegativeButton(android.R.string.cancel, null) - .setPositiveButton(android.R.string.ok) { _, _ -> - ruleSet.remove(rules[position]) - activity.runOnUiThread { - adapter.remove(rules[position]) - updateUi?.let { it() } - } - }.show() - true - } - } - findViewById