diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 53b1be0..df6458b 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -59,5 +59,12 @@ jobs: - name: Build debug id: buildDebug run: | - ./gradlew :app:assembleDebug + ./gradlew assembleDebug echo "debugName=$(ls app/build/outputs/apk/debug/*.apk | awk -F '(/|.apk)' '{print $6}')" >> $GITHUB_OUTPUT + + - name: Upload debug + if: success() + uses: actions/upload-artifact@v4 + with: + name: ${{ steps.buildDebug.outputs.debugName }} + path: "app/build/outputs/apk/debug/*.apk" diff --git a/app/src/main/java/icu/nullptr/hidemyapplist/service/ConfigManager.kt b/app/src/main/java/icu/nullptr/hidemyapplist/service/ConfigManager.kt index b710d33..59ffe27 100644 --- a/app/src/main/java/icu/nullptr/hidemyapplist/service/ConfigManager.kt +++ b/app/src/main/java/icu/nullptr/hidemyapplist/service/ConfigManager.kt @@ -5,6 +5,7 @@ import android.util.Log import icu.nullptr.hidemyapplist.common.JsonConfig import icu.nullptr.hidemyapplist.hmaApp import icu.nullptr.hidemyapplist.ui.util.showToast +import icu.nullptr.hidemyapplist.util.PackageHelper import org.frknkrc44.hma_oss.R import org.frknkrc44.hma_oss.common.BuildConfig import java.io.File @@ -179,4 +180,28 @@ object ConfigManager { else config.scope[packageName] = appConfig saveConfig() } + + fun clearUninstalledAppConfigs(onFinish: (success: Boolean) -> Unit) { + PackageHelper.invalidateCache { throwable -> + if (throwable == null) { + val markedToRemove = mutableListOf() + config.scope.keys.forEach { packageName -> + if (!PackageHelper.exists(packageName)) { + markedToRemove.add(packageName) + } + } + + if (markedToRemove.isNotEmpty()) { + markedToRemove.forEach { config.scope.remove(it) } + saveConfig() + } + + ServiceClient.log(Log.INFO, TAG, "Pruned ${markedToRemove.size} app config(s)") + + onFinish(true) + } else { + onFinish(false) + } + } + } } diff --git a/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/AppManageFragment.kt b/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/AppManageFragment.kt index d1ce3bf..c21ee3e 100644 --- a/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/AppManageFragment.kt +++ b/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/AppManageFragment.kt @@ -3,6 +3,7 @@ package icu.nullptr.hidemyapplist.ui.fragment import icu.nullptr.hidemyapplist.service.ConfigManager import icu.nullptr.hidemyapplist.ui.adapter.AppManageAdapter import icu.nullptr.hidemyapplist.ui.util.navigate +import icu.nullptr.hidemyapplist.util.PackageHelper import org.frknkrc44.hma_oss.R class AppManageFragment : AppSelectFragment() { @@ -10,7 +11,9 @@ class AppManageFragment : AppSelectFragment() { override val firstComparator: Comparator = Comparator.comparing(ConfigManager::isHideEnabled).reversed() override val adapter = AppManageAdapter { - val args = AppSettingsFragmentArgs(it) - navigate(R.id.nav_app_settings, args.toBundle()) + if (PackageHelper.exists(it)) { + val args = AppSettingsFragmentArgs(it) + navigate(R.id.nav_app_settings, args.toBundle()) + } } } diff --git a/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/AppSelectFragment.kt b/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/AppSelectFragment.kt index e5f58b4..c4054a5 100644 --- a/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/AppSelectFragment.kt +++ b/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/AppSelectFragment.kt @@ -50,7 +50,7 @@ abstract class AppSelectFragment : Fragment(R.layout.fragment_app_select) { } } - open fun invalidateCache() { + protected open fun invalidateCache() { PackageHelper.invalidateCache() } @@ -81,6 +81,7 @@ abstract class AppSelectFragment : Fragment(R.layout.fragment_app_select) { PrefManager.appFilter_reverseOrder = item.isChecked } } + sortList() } @@ -144,8 +145,12 @@ abstract class AppSelectFragment : Fragment(R.layout.fragment_app_select) { lifecycleScope.launch { PackageHelper.isRefreshing .flowWithLifecycle(lifecycle) - .collect { - binding.swipeRefresh.isRefreshing = it + .collect { isRefreshing -> + binding.swipeRefresh.isRefreshing = isRefreshing + + if (!isRefreshing) { + sortList() + } } } diff --git a/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/SettingsFragment.kt b/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/SettingsFragment.kt index 35f7b33..2173173 100644 --- a/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/SettingsFragment.kt +++ b/app/src/main/java/icu/nullptr/hidemyapplist/ui/fragment/SettingsFragment.kt @@ -9,6 +9,7 @@ import android.view.WindowInsets import androidx.appcompat.app.AppCompatDelegate import androidx.core.net.toUri import androidx.fragment.app.Fragment +import androidx.lifecycle.lifecycleScope import androidx.preference.ListPreference import androidx.preference.Preference import androidx.preference.PreferenceDataStore @@ -17,8 +18,8 @@ import androidx.preference.SwitchPreferenceCompat import by.kirich1409.viewbindingdelegate.viewBinding import com.google.android.material.color.DynamicColors import com.google.android.material.dialog.MaterialAlertDialogBuilder -import icu.nullptr.hidemyapplist.common.PropertyUtils import icu.nullptr.hidemyapplist.common.Constants +import icu.nullptr.hidemyapplist.common.PropertyUtils import icu.nullptr.hidemyapplist.hmaApp import icu.nullptr.hidemyapplist.service.ConfigManager import icu.nullptr.hidemyapplist.service.PrefManager @@ -30,6 +31,7 @@ import icu.nullptr.hidemyapplist.ui.util.showToast import icu.nullptr.hidemyapplist.util.ConfigUtils.Companion.getLocale import icu.nullptr.hidemyapplist.util.LangList import icu.nullptr.hidemyapplist.util.SuUtils +import kotlinx.coroutines.launch import org.frknkrc44.hma_oss.R import org.frknkrc44.hma_oss.databinding.FragmentSettingsBinding import java.util.Locale @@ -287,6 +289,40 @@ class SettingsFragment : Fragment(R.layout.fragment_settings), PreferenceFragmen } } + findPreference("clearUninstalledPackageConfigs")?.apply { + setOnPreferenceClickListener { + MaterialAlertDialogBuilder(requireContext()) + .setTitle(R.string.settings_clear_uninstalled_app_configs) + .setMessage(R.string.settings_no_undone_warning) + .setPositiveButton(android.R.string.ok) { _, _ -> + val progressDialog = MaterialAlertDialogBuilder(requireContext()) + .setTitle(R.string.settings_clear_uninstalled_app_configs) + .setView(R.layout.dialog_loading) + .setCancelable(false) + .create() + + progressDialog.show() + + ConfigManager.clearUninstalledAppConfigs { isSuccess -> + lifecycleScope.launch { + progressDialog.dismiss() + + if (isSuccess) { + showToast(android.R.string.ok) + } + } + } + } + .setNegativeButton(android.R.string.cancel) { _, _ -> + // do nothing + } + .setCancelable(false) + .show() + + true + } + } + findPreference("blackDarkTheme")?.apply { isEnabled = findPreference("systemWallpaper")?.isChecked != true setOnPreferenceChangeListener { _, _ -> diff --git a/app/src/main/java/icu/nullptr/hidemyapplist/util/PackageHelper.kt b/app/src/main/java/icu/nullptr/hidemyapplist/util/PackageHelper.kt index 950fbb2..6147f20 100644 --- a/app/src/main/java/icu/nullptr/hidemyapplist/util/PackageHelper.kt +++ b/app/src/main/java/icu/nullptr/hidemyapplist/util/PackageHelper.kt @@ -1,16 +1,14 @@ package icu.nullptr.hidemyapplist.util import android.content.pm.ApplicationInfo -import android.content.pm.IPackageManager import android.content.pm.PackageInfo -import android.content.pm.PackageManager import android.graphics.Bitmap +import android.graphics.BitmapFactory import icu.nullptr.hidemyapplist.common.Constants import icu.nullptr.hidemyapplist.hmaApp import icu.nullptr.hidemyapplist.service.PrefManager import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking @@ -19,7 +17,6 @@ import java.text.Collator import java.util.Locale object PackageHelper { - class PackageCache( val info: PackageInfo, val label: String, @@ -61,26 +58,22 @@ object PackageHelper { } } - private lateinit var ipm: IPackageManager - private lateinit var pm: PackageManager - private val packageCache = MutableSharedFlow>(replay = 1) - private val mAppList = MutableSharedFlow>(replay = 1) - val appList: SharedFlow> = mAppList + val appList = MutableSharedFlow>(replay = 1) - private val mRefreshing = MutableSharedFlow(replay = 1) - val isRefreshing: SharedFlow = mRefreshing + val isRefreshing = MutableSharedFlow(replay = 1) init { - // TODO: PackageManagerDelegate - pm = hmaApp.packageManager invalidateCache() } - fun invalidateCache() { + fun invalidateCache( + onFinished: ((Throwable?) -> Unit)? = null + ) { hmaApp.globalScope.launch { - mRefreshing.emit(true) + isRefreshing.emit(true) val cache = withContext(Dispatchers.IO) { + val pm = hmaApp.packageManager val packages = pm.getInstalledPackages(0) mutableMapOf().also { for (packageInfo in packages) { @@ -94,8 +87,12 @@ object PackageHelper { } } packageCache.emit(cache) - mAppList.emit(cache.keys.toList()) - mRefreshing.emit(false) + appList.emit(cache.keys.toList()) + isRefreshing.emit(false) + }.apply { + if (onFinished != null) { + invokeOnCompletion(onFinished) + } } } @@ -107,27 +104,37 @@ object PackageHelper { PrefManager.SortMethod.BY_UPDATE_TIME -> Comparators.byUpdateTime } if (PrefManager.appFilter_reverseOrder) comparator = comparator.reversed() - val list = mAppList.first().sortedWith(firstComparator.then(comparator)) - mAppList.emit(list) + val list = appList.first().sortedWith(firstComparator.then(comparator)) + appList.emit(list) + } + + private suspend fun getCacheNoThrow() = try { + packageCache.first() + } catch (_: Throwable) { + mapOf() } fun exists(packageName: String) = runBlocking { - packageCache.first().contains(packageName) + getCacheNoThrow().contains(packageName) } fun loadPackageInfo(packageName: String): PackageInfo = runBlocking { - packageCache.first()[packageName]!!.info + getCacheNoThrow()[packageName]!!.info } fun loadAppLabel(packageName: String): String = runBlocking { - packageCache.first()[packageName]!!.label + getCacheNoThrow()[packageName]?.label ?: packageName } fun loadAppIcon(packageName: String): Bitmap = runBlocking { - packageCache.first()[packageName]!!.icon + getCacheNoThrow()[packageName]?.icon ?: + BitmapFactory.decodeResource( + hmaApp.resources, + android.R.drawable.sym_def_app_icon + ) } fun isSystem(packageName: String): Boolean = runBlocking { - packageCache.first()[packageName]?.info?.applicationInfo?.flags?.and(ApplicationInfo.FLAG_SYSTEM) != 0 + getCacheNoThrow()[packageName]?.info?.applicationInfo?.flags?.and(ApplicationInfo.FLAG_SYSTEM) != 0 } } diff --git a/app/src/main/res/layout/dialog_loading.xml b/app/src/main/res/layout/dialog_loading.xml new file mode 100644 index 0000000..cff8e13 --- /dev/null +++ b/app/src/main/res/layout/dialog_loading.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e009b5b..d6e4d43 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -158,6 +158,9 @@ Disable Activity launch protection This toggle will lift all startActivity protections. If you\'re unsure, don\'t enable it. Use system wallpaper + Clear uninstalled app configs + This option allows you to create a config without remnants but you\'ll have to reconfigure apps when you installed them again. + This action cannot be undone. Language Participate in translation diff --git a/app/src/main/res/xml/settings.xml b/app/src/main/res/xml/settings.xml index 8347cf7..2687035 100644 --- a/app/src/main/res/xml/settings.xml +++ b/app/src/main/res/xml/settings.xml @@ -82,6 +82,11 @@ android:key="hideIcon" android:summary="@string/settings_hide_icon_summary" android:title="@string/settings_hide_icon" /> + diff --git a/build.gradle.kts b/build.gradle.kts index 950a156..3e53ca4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -19,6 +19,7 @@ fun String.execute(currentWorkingDir: File = file("./")): String { val localProperties = Properties() localProperties.load(file("local.properties").inputStream()) +val ciBuild = providers.environmentVariable("CI").isPresent val officialBuild by extra(localProperties.getProperty("officialBuild", "false") == "true") @Suppress("unused") @@ -30,10 +31,15 @@ val crowdinApiKey: String by extra(localProperties.getProperty("crowdinApiKey", fun getUncommittedSuffix(): String { if (officialBuild) return "" + if (ciBuild) { + val headRefVal = providers.environmentVariable("GITHUB_HEAD_REF").orElse("HEAD").get() + return "-$headRefVal" + } + var returnedVal = "" try { - val branch = "git symbolic-ref HEAD".execute().split("/").last() + val branch = "git rev-parse --abbrev-ref HEAD".execute().split("/").last() if (branch != "master") { returnedVal += "-$branch" } @@ -48,7 +54,7 @@ fun getUncommittedSuffix(): String { } val gitHasUncommittedSuffix = getUncommittedSuffix() -val gitCommitCount = "git rev-list HEAD --count".execute().toInt() +val gitCommitCount = "git rev-list refs/remotes/origin/master --count".execute().toInt() // 432 is the count of commits before license changed val gitCommitCountAfterOss = gitCommitCount - 432 diff --git a/common/src/main/java/icu/nullptr/hidemyapplist/common/app_presets/SuspiciousAppsPreset.kt b/common/src/main/java/icu/nullptr/hidemyapplist/common/app_presets/SuspiciousAppsPreset.kt index 3cf90af..919ccfd 100644 --- a/common/src/main/java/icu/nullptr/hidemyapplist/common/app_presets/SuspiciousAppsPreset.kt +++ b/common/src/main/java/icu/nullptr/hidemyapplist/common/app_presets/SuspiciousAppsPreset.kt @@ -29,9 +29,16 @@ class SuspiciousAppsPreset : BasePreset("sus_apps") { "com.amaze.filemanager", ) - val libNames = arrayOf( - "liblsplant.so", + /* + val libNames = arrayOf( + // Note that LSPlant is used in some of games too + // I will not add it as suspicious app indicator anymore + // + // "liblsplant.so", + + // TODO: Add more suspicious apps by checking for libs ) + */ val assetNames = arrayOf( // ~All possible APK editors @@ -101,7 +108,7 @@ class SuspiciousAppsPreset : BasePreset("sus_apps") { } ZipFile(appInfo.sourceDir).use { zipFile -> - if (findAppsFromLibs(zipFile, libNames) || findAppsFromAssets(zipFile, assetNames)) { + if (/*findAppsFromLibs(zipFile, libNames) ||*/ findAppsFromAssets(zipFile, assetNames)) { return true } } diff --git a/common/src/main/java/icu/nullptr/hidemyapplist/common/settings_presets/BasePreset.kt b/common/src/main/java/icu/nullptr/hidemyapplist/common/settings_presets/BasePreset.kt index 29c679c..bfbdee3 100644 --- a/common/src/main/java/icu/nullptr/hidemyapplist/common/settings_presets/BasePreset.kt +++ b/common/src/main/java/icu/nullptr/hidemyapplist/common/settings_presets/BasePreset.kt @@ -1,6 +1,14 @@ package icu.nullptr.hidemyapplist.common.settings_presets abstract class BasePreset(val name: String) { + /* + companion object { + const val SETTINGS_GLOBAL = "global" + const val SETTINGS_SYSTEM = "system" + const val SETTINGS_SECURE = "secure" + } + */ + abstract val settingsKVPairs: List fun getSpoofedValue(key: String) = settingsKVPairs.firstOrNull { it.name == key } diff --git a/common/src/main/java/icu/nullptr/hidemyapplist/common/settings_presets/DeveloperOptionsPreset.kt b/common/src/main/java/icu/nullptr/hidemyapplist/common/settings_presets/DeveloperOptionsPreset.kt index 002c9a9..53f4540 100644 --- a/common/src/main/java/icu/nullptr/hidemyapplist/common/settings_presets/DeveloperOptionsPreset.kt +++ b/common/src/main/java/icu/nullptr/hidemyapplist/common/settings_presets/DeveloperOptionsPreset.kt @@ -16,6 +16,7 @@ class DeveloperOptionsPreset : BasePreset("dev_options") { Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, "0", ), + /* ReplacementItem( "debug.force_rtl", "0" @@ -100,5 +101,6 @@ class DeveloperOptionsPreset : BasePreset("dev_options") { "non_persistent_mac_randomization_force_enabled", "0", ), + */ ) } diff --git a/common/src/main/java/icu/nullptr/hidemyapplist/common/settings_presets/ReplacementItem.kt b/common/src/main/java/icu/nullptr/hidemyapplist/common/settings_presets/ReplacementItem.kt index 5673169..784e7a7 100644 --- a/common/src/main/java/icu/nullptr/hidemyapplist/common/settings_presets/ReplacementItem.kt +++ b/common/src/main/java/icu/nullptr/hidemyapplist/common/settings_presets/ReplacementItem.kt @@ -2,10 +2,12 @@ package icu.nullptr.hidemyapplist.common.settings_presets data class ReplacementItem( val name: String, - val value: String? + val value: String?, + // val database: String = BasePreset.SETTINGS_GLOBAL, ) { - override fun toString() = "ReplacementItem {" + - " 'name': '$name'," + - "'value': '$value'" + + override fun toString() = "ReplacementItem { " + + "'name': '$name', " + + "'value': '$value', " + + // "'database': '$database'" + " }" } \ No newline at end of file