Redesign 2 (#31)

* Begin design again

* Improve Switch dimensions

* Add empty settings preset fragment

* Finish settings presets fragment

* Add git branch to version name

* Add sorting for preset keys, add new replaced settings

* Remove unneeded string, fix force stop

* Fix CI issue

* Remove animation speed spoofing

* Add "Use system wallpaper" option

* Fix Switch colors

* Add GitHub-only translators into translator list

* Simplify translator getter

* Fix About fragment button colors for wallpaper mode

* Add new SwipeRefreshLayout colors

* Add no transparency mode for home item background color, optimize imports

* Replace ugly serif

* Add enabled/disabled state colors for Switch

* Dirty fix for system wallpaper mode is not activated

* Remove ability to hide something from HMA-OSS itself

* Replace Java codes with Kotlin

* Move CommonUtils to PropertyUtils, move packages into Constants

* Add localBuild as used
This commit is contained in:
Furkan Karcıoğlu 2025-10-25 02:44:33 +03:00 • committed by GitHub
commit 74c9313c46
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
58 changed files with 901 additions and 225 deletions

View file

@ -58,6 +58,18 @@ val officialBuild: Boolean by rootProject.extra
@Suppress("deprecation")
afterEvaluate {
val srcDir = android.sourceSets["main"].assets.srcDirs.first()
logger.lifecycle("Asset dir: $srcDir")
if (!srcDir.exists()) srcDir.mkdirs()
val translatorsMap = mutableMapOf(
// I used GitHub to get translations before moving to Crowdin.
// Since nearly all of GitHub translators are listed in Crowdin
// too, I wanted to add one profile here.
"cvnertnc" to "https://avatars.githubusercontent.com/u/148134890?v=4",
)
if (localBuild || officialBuild) {
val url = URL("https://crowdin.com/api/v2/projects/$crowdinProjectId/members")
val urlConnection = url.openConnection() as HttpURLConnection
@ -70,7 +82,7 @@ afterEvaluate {
val json = JsonParser.parseString(str).asJsonObject
val translators = json.getAsJsonArray("data")
val translatorsMap = mutableMapOf<String, String>()
for (item in translators) {
val translator = item.asJsonObject.getAsJsonObject("data")
val avatarUrl = translator.get("avatarUrl").asString
@ -87,18 +99,10 @@ afterEvaluate {
translatorsMap[username] = avatarUrl
}
}
val translatorJson = JSONObject(translatorsMap).toJSONString()
val srcDir = android.sourceSets["main"].assets.srcDirs.first()
logger.lifecycle("Asset dir: $srcDir")
if (!srcDir.exists()) srcDir.mkdirs()
File(srcDir, "translators.json").writeText(translatorJson)
} else {
val srcDir = android.sourceSets["main"].assets.srcDirs.first()
logger.lifecycle("Asset dir: $srcDir")
if (!srcDir.exists()) srcDir.mkdirs()
File(srcDir, "translators.json").writeText("{}")
}
val translatorJson = JSONObject(translatorsMap).toJSONString()
File(srcDir, "translators.json").writeText(translatorJson)
}
android {

View file

@ -2,10 +2,12 @@ package icu.nullptr.hidemyapplist
import android.annotation.SuppressLint
import android.app.Application
import android.util.Log
import androidx.appcompat.app.AppCompatDelegate
import icu.nullptr.hidemyapplist.receiver.AppChangeReceiver
import icu.nullptr.hidemyapplist.service.ConfigManager
import icu.nullptr.hidemyapplist.service.PrefManager
import icu.nullptr.hidemyapplist.service.ServiceClient
import icu.nullptr.hidemyapplist.ui.util.showToast
import icu.nullptr.hidemyapplist.util.ConfigUtils.Companion.getLocale
import kotlinx.coroutines.CoroutineScope
@ -39,5 +41,11 @@ class MyApp : Application() {
val config = resources.configuration
config.setLocale(getLocale())
resources.updateConfiguration(config, resources.displayMetrics)
val handler = Thread.getDefaultUncaughtExceptionHandler()
Thread.setDefaultUncaughtExceptionHandler { t, e ->
ServiceClient.log(Log.ERROR, t.name, e.stackTraceToString())
handler?.uncaughtException(t, e)
}
}
}

View file

@ -10,9 +10,13 @@ import org.frknkrc44.hma_oss.common.BuildConfig
import java.io.File
object ConfigManager {
enum class PresetType {
APP,
SETTINGS,
}
data class TemplateInfo(val name: String?, val isWhiteList: Boolean)
data class PresetInfo(val name: String, val translation: String)
data class PresetInfo(val name: String, val type: PresetType?, val translation: String)
private const val TAG = "ConfigManager"
private lateinit var config: JsonConfig

View file

@ -13,6 +13,7 @@ object PrefManager {
private const val PREF_LOCALE = "language"
private const val PREF_SYSTEM_WALLPAPER = "system_wallpaper"
private const val PREF_DARK_THEME = "dark_theme"
private const val PREF_BLACK_DARK_THEME = "black_dark_theme"
private const val PREF_FOLLOW_SYSTEM_ACCENT = "follow_system_accent"
@ -45,6 +46,10 @@ object PrefManager {
get() = pref.getInt(PREF_DARK_THEME, AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
set(value) = pref.edit().putInt(PREF_DARK_THEME, value).apply()
var systemWallpaper: Boolean
get() = pref.getBoolean(PREF_SYSTEM_WALLPAPER, false)
set(value) = pref.edit().putBoolean(PREF_SYSTEM_WALLPAPER, value).apply()
var blackDarkTheme: Boolean
get() = pref.getBoolean(PREF_BLACK_DARK_THEME, false)
set(value) = pref.edit().putBoolean(PREF_BLACK_DARK_THEME, value).apply()

View file

@ -1,11 +1,7 @@
package icu.nullptr.hidemyapplist.service
import android.os.IBinder
import android.os.Parcel
import android.os.RemoteException
import android.os.ServiceManager
import android.util.Log
import icu.nullptr.hidemyapplist.common.Constants
import icu.nullptr.hidemyapplist.common.IHMAService
import java.lang.reflect.InvocationHandler
import java.lang.reflect.Method
@ -36,37 +32,6 @@ object ServiceClient : IHMAService, IBinder.DeathRecipient {
binder.linkToDeath(this, 0)
}
private fun getServiceLegacy(): IHMAService? {
if (service != null) return service
val pm = ServiceManager.getService("package")
val data = Parcel.obtain()
val reply = Parcel.obtain()
val remote = try {
data.writeInterfaceToken(Constants.DESCRIPTOR)
data.writeInt(Constants.ACTION_GET_BINDER)
pm.transact(Constants.TRANSACTION, data, reply, 0)
reply.readException()
val binder = reply.readStrongBinder()
IHMAService.Stub.asInterface(binder)
} catch (e: RemoteException) {
Log.d(TAG, "Failed to get binder")
null
} finally {
data.recycle()
reply.recycle()
}
if (remote != null) {
Log.i(TAG, "Binder acquired")
remote.asBinder().linkToDeath(this, 0)
service = Proxy.newProxyInstance(
javaClass.classLoader,
arrayOf(IHMAService::class.java),
ServiceProxy(remote)
) as IHMAService
}
return service
}
override fun binderDied() {
service = null
Log.e(TAG, "Binder died")
@ -74,30 +39,38 @@ object ServiceClient : IHMAService, IBinder.DeathRecipient {
override fun asBinder() = service?.asBinder()
override fun getServiceVersion() = getServiceLegacy()?.serviceVersion ?: 0
override fun getServiceVersion() = service?.serviceVersion ?: 0
override fun getFilterCount() = getServiceLegacy()?.filterCount ?: 0
override fun getFilterCount() = service?.filterCount ?: 0
override fun getLogs() = getServiceLegacy()?.logs
override fun getLogs() = service?.logs
override fun clearLogs() {
getServiceLegacy()?.clearLogs()
service?.clearLogs()
}
override fun handlePackageEvent(eventType: String?, packageName: String?) {
getServiceLegacy()?.handlePackageEvent(eventType, packageName)
service?.handlePackageEvent(eventType, packageName)
}
override fun getPackagesForPreset(presetName: String) =
getServiceLegacy()?.getPackagesForPreset(presetName)
service?.getPackagesForPreset(presetName)
override fun readConfig() = getServiceLegacy()?.readConfig()
override fun readConfig() = service?.readConfig()
override fun writeConfig(json: String) {
getServiceLegacy()?.writeConfig(json)
service?.writeConfig(json)
}
override fun stopService(cleanEnv: Boolean) {
getServiceLegacy()?.stopService(cleanEnv)
service?.stopService(cleanEnv)
}
override fun forceStop(packageName: String, userId: Int) {
service?.forceStop(packageName, userId)
}
override fun log(level: Int, tag: String, message: String) {
service?.log(level, tag, message)
}
}

View file

@ -2,7 +2,7 @@ package icu.nullptr.hidemyapplist.ui.adapter
import android.view.ViewGroup
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import icu.nullptr.hidemyapplist.common.CommonUtils
import icu.nullptr.hidemyapplist.common.Constants
import icu.nullptr.hidemyapplist.service.ConfigManager
import icu.nullptr.hidemyapplist.service.PrefManager
import icu.nullptr.hidemyapplist.ui.view.AppItemView
@ -15,7 +15,7 @@ class AppManageAdapter(
inner class ViewHolder(view: AppItemView) : AppSelectAdapter.ViewHolder(view) {
init {
view.setOnClickListener {
if (!PrefManager.bypassRiskyPackageWarning && CommonUtils.riskyPackages.contains(view.binding.packageName.text)) {
if (!PrefManager.bypassRiskyPackageWarning && Constants.riskyPackages.contains(view.binding.packageName.text)) {
MaterialAlertDialogBuilder(view.context)
.setTitle(R.string.app_warning_risky_package_title)
.setMessage(R.string.app_warning_risky_package_desc)

View file

@ -16,6 +16,8 @@ import androidx.recyclerview.widget.RecyclerView
import by.kirich1409.viewbindingdelegate.viewBinding
import icu.nullptr.hidemyapplist.service.PrefManager
import icu.nullptr.hidemyapplist.ui.adapter.AppSelectAdapter
import icu.nullptr.hidemyapplist.ui.util.ThemeUtils.homeItemBackgroundColor
import icu.nullptr.hidemyapplist.ui.util.ThemeUtils.themeColor
import icu.nullptr.hidemyapplist.ui.util.navController
import icu.nullptr.hidemyapplist.ui.util.setupToolbar
import icu.nullptr.hidemyapplist.util.PackageHelper
@ -118,8 +120,18 @@ abstract class AppSelectFragment : Fragment(R.layout.fragment_app_select) {
}
binding.list.layoutManager = LinearLayoutManager(context)
binding.list.adapter = adapter
binding.swipeRefresh.setOnRefreshListener {
invalidateCache()
with(binding.swipeRefresh) {
setProgressBackgroundColorSchemeColor(homeItemBackgroundColor(true))
setColorSchemeColors(
themeColor(androidx.appcompat.R.attr.colorPrimary),
themeColor(com.google.android.material.R.attr.colorSecondary),
themeColor(com.google.android.material.R.attr.colorTertiary),
)
setOnRefreshListener {
invalidateCache()
}
}
adapter.registerAdapterDataObserver(

View file

@ -3,6 +3,7 @@ package icu.nullptr.hidemyapplist.ui.fragment
import android.annotation.SuppressLint
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.view.View
import android.view.WindowInsets
import android.widget.Toast
@ -23,10 +24,12 @@ import icu.nullptr.hidemyapplist.common.AppPresets
import icu.nullptr.hidemyapplist.common.JsonConfig
import icu.nullptr.hidemyapplist.common.SettingsPresets
import icu.nullptr.hidemyapplist.service.ConfigManager
import icu.nullptr.hidemyapplist.service.ServiceClient
import icu.nullptr.hidemyapplist.ui.util.enabledString
import icu.nullptr.hidemyapplist.ui.util.navController
import icu.nullptr.hidemyapplist.ui.util.navigate
import icu.nullptr.hidemyapplist.ui.util.setupToolbar
import icu.nullptr.hidemyapplist.ui.util.showToast
import icu.nullptr.hidemyapplist.ui.viewmodel.AppSettingsViewModel
import icu.nullptr.hidemyapplist.util.PackageHelper
import org.frknkrc44.hma_oss.BuildConfig
@ -34,6 +37,9 @@ import org.frknkrc44.hma_oss.R
import org.frknkrc44.hma_oss.databinding.FragmentSettingsBinding
class AppSettingsFragment : Fragment(R.layout.fragment_settings) {
companion object {
private const val TAG = "AppSettingsFragment"
}
private val binding by viewBinding<FragmentSettingsBinding>()
private val viewModel by viewModels<AppSettingsViewModel>() {
@ -157,6 +163,24 @@ class AppSettingsFragment : Fragment(R.layout.fragment_settings) {
else getString(R.string.app_extra_apps_invisible_count, pack.config.extraAppList.size)
}
private fun launchMainActivity(packageName: String) {
try {
val pkgMgr = requireContext().packageManager
val pkgInfo = pkgMgr.getPackageInfo(packageName, 0)
if (pkgInfo.applicationInfo?.enabled == true) {
val resolvedIntent = pkgMgr.getLaunchIntentForPackage(packageName)
if (resolvedIntent != null) {
startActivity(resolvedIntent)
}
} else {
throw RuntimeException("Package is disabled")
}
} catch (e: Throwable) {
showToast(R.string.app_launch_failed)
ServiceClient.log(Log.ERROR, TAG, e.stackTraceToString())
}
}
@SuppressLint("DiscouragedApi")
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
preferenceManager.preferenceDataStore = AppPreferenceDataStore(pack)
@ -164,7 +188,27 @@ class AppSettingsFragment : Fragment(R.layout.fragment_settings) {
findPreference<Preference>("appInfo")?.let {
it.icon = PackageHelper.loadAppIcon(pack.app).toDrawable(resources)
it.title = PackageHelper.loadAppLabel(pack.app)
it.summary = PackageHelper.loadPackageInfo(pack.app).packageName
it.summary = pack.app
it.setOnPreferenceClickListener { pref ->
MaterialAlertDialogBuilder(pref.context).apply {
setTitle(it.title)
setItems(
R.array.app_action_texts,
) { dialog, which ->
when (which) {
0 -> {
ServiceClient.forceStop(pack.app, 0)
launchMainActivity(pack.app)
}
1 -> {
launchMainActivity(pack.app)
}
}
}
}.show()
true
}
}
findPreference<SwitchPreferenceCompat>("hideInstallationSource")?.setOnPreferenceChangeListener { _, newValue ->
Toast.makeText(requireContext(),

View file

@ -17,7 +17,7 @@ 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.CommonUtils
import icu.nullptr.hidemyapplist.common.PropertyUtils
import icu.nullptr.hidemyapplist.common.Constants
import icu.nullptr.hidemyapplist.hmaApp
import icu.nullptr.hidemyapplist.service.ConfigManager
@ -92,6 +92,7 @@ class SettingsFragment : Fragment(R.layout.fragment_settings), PreferenceFragmen
override fun getBoolean(key: String, defValue: Boolean): Boolean {
return when (key) {
"followSystemAccent" -> PrefManager.followSystemAccent
"systemWallpaper" -> PrefManager.systemWallpaper
"blackDarkTheme" -> PrefManager.blackDarkTheme
"detailLog" -> ConfigManager.detailLog
"hideIcon" -> PrefManager.hideIcon
@ -118,6 +119,7 @@ class SettingsFragment : Fragment(R.layout.fragment_settings), PreferenceFragmen
override fun putBoolean(key: String, value: Boolean) {
when (key) {
"followSystemAccent" -> PrefManager.followSystemAccent = value
"systemWallpaper" -> PrefManager.systemWallpaper = value
"blackDarkTheme" -> PrefManager.blackDarkTheme = value
"detailLog" -> ConfigManager.detailLog = value
"forceMountData" -> ConfigManager.forceMountData = value
@ -151,7 +153,7 @@ class SettingsFragment : Fragment(R.layout.fragment_settings), PreferenceFragmen
it.summary = getString(R.string.settings_need_reboot) + "\n\n" +
getString(
R.string.settings_default_value,
CommonUtils.isAppDataIsolationEnabled.enabledString(resources)
PropertyUtils.isAppDataIsolationEnabled.enabledString(resources)
)
}
@ -159,7 +161,7 @@ class SettingsFragment : Fragment(R.layout.fragment_settings), PreferenceFragmen
it.summary = getString(R.string.settings_need_reboot) + "\n\n" +
getString(
R.string.settings_default_value,
CommonUtils.isVoldAppDataIsolationEnabled.enabledString(resources)
PropertyUtils.isVoldAppDataIsolationEnabled.enabledString(resources)
)
it.setOnPreferenceChangeListener { _, newValue ->
@ -191,9 +193,9 @@ class SettingsFragment : Fragment(R.layout.fragment_settings), PreferenceFragmen
it.isEnabled -> getString(
R.string.settings_data_isolation_summary,
if (ConfigManager.altAppDataIsolation) getString(R.string.settings_overwritten)
else CommonUtils.isAppDataIsolationEnabled.enabledString(resources),
else PropertyUtils.isAppDataIsolationEnabled.enabledString(resources),
if (ConfigManager.altVoldAppDataIsolation) getString(R.string.settings_overwritten)
else CommonUtils.isVoldAppDataIsolationEnabled.enabledString(resources),
else PropertyUtils.isVoldAppDataIsolationEnabled.enabledString(resources),
ConfigManager.forceMountData.enabledString(resources)
)
else -> getString(R.string.settings_data_isolation_unsupported)
@ -270,9 +272,27 @@ class SettingsFragment : Fragment(R.layout.fragment_settings), PreferenceFragmen
true
}
findPreference<SwitchPreferenceCompat>("blackDarkTheme")?.setOnPreferenceChangeListener { _, _ ->
activity?.recreate()
true
findPreference<SwitchPreferenceCompat>("systemWallpaper")?.apply {
isEnabled = findPreference<SwitchPreferenceCompat>("blackDarkTheme")?.isChecked != true
setOnPreferenceChangeListener { _, value ->
val activity = requireActivity()
if (value as Boolean) {
activity.finish()
startActivity(Intent(activity, activity.javaClass))
} else {
activity.recreate()
}
true
}
}
findPreference<SwitchPreferenceCompat>("blackDarkTheme")?.apply {
isEnabled = findPreference<SwitchPreferenceCompat>("systemWallpaper")?.isChecked != true
setOnPreferenceChangeListener { _, _ ->
activity?.recreate()
true
}
}
configureDataIsolation()

View file

@ -21,8 +21,8 @@ val Fragment.navController get() = NavHostFragment.findNavController(this)
private val navOptions by lazy {
NavOptions.Builder().apply {
setEnterAnim(R.anim.activity_open_enter)
setExitAnim(R.anim.fade_out)
setPopEnterAnim(R.anim.fade_in)
setExitAnim(R.anim.activity_open_exit)
setPopEnterAnim(R.anim.activity_close_enter)
setPopExitAnim(R.anim.activity_close_exit)
}.build()
}

View file

@ -58,24 +58,18 @@ object ThemeUtils {
"MATERIAL_BLUE_GREY" to R.style.ThemeOverlay_Dark_MaterialBlueGrey
)
private const val THEME_DEFAULT = "DEFAULT"
private const val THEME_BLACK = "BLACK"
val isSystemAccent get() = DynamicColors.isDynamicColorAvailable() && PrefManager.followSystemAccent
fun isNightMode(context: Context) = (context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES
fun isUsingBlackTheme(context: Context) = PrefManager.blackDarkTheme && isNightMode(context)
fun getNightTheme(context: Context): String {
return if (isUsingBlackTheme(context))
THEME_BLACK else THEME_DEFAULT
}
@StyleRes
fun getNightThemeStyleRes(context: Context): Int {
return if (isUsingBlackTheme(context))
R.style.ThemeOverlay_Black else R.style.ThemeOverlay
fun getOverlayThemeStyleRes(context: Context): Int {
if (isUsingBlackTheme(context)) return R.style.ThemeOverlay_Black
if (PrefManager.systemWallpaper) return R.style.ThemeOverlay_Wallpaper
return R.style.ThemeOverlay
}
val colorTheme get() = if (isSystemAccent) "SYSTEM" else PrefManager.themeColor
@ -125,11 +119,14 @@ object ThemeUtils {
@ColorRes colorId: Int
) = requireContext().getColor(colorId)
fun Context.homeItemBackgroundColor() = if (isNightMode(this)) {
fun Context.homeItemBackgroundColor(forceNoTrans: Boolean = false) = (if (isNightMode(this)) {
themeColor(com.google.android.material.R.attr.colorSurfaceContainerHighest)
} else {
themeColor(com.google.android.material.R.attr.colorSurfaceContainer)
}).let {
if (!forceNoTrans && PrefManager.systemWallpaper) return@let it - 0x55000000
return@let it
}
fun Fragment.homeItemBackgroundColor() = requireContext().homeItemBackgroundColor()
fun Fragment.homeItemBackgroundColor(forceNoTrans: Boolean = false) = requireContext().homeItemBackgroundColor(forceNoTrans)
}

View file

@ -3,6 +3,7 @@ package icu.nullptr.hidemyapplist.ui.view
import android.content.Context
import android.util.AttributeSet
import android.widget.LinearLayout
import androidx.core.view.isVisible
import by.kirich1409.viewbindingdelegate.CreateMethod
import by.kirich1409.viewbindingdelegate.viewBinding
import icu.nullptr.hidemyapplist.util.PackageHelper
@ -18,7 +19,7 @@ class AppItemView @JvmOverloads constructor(
val binding by viewBinding<AppItemViewBinding>(createMethod = CreateMethod.INFLATE)
var showEnabled: Boolean
get() = binding.enabled.visibility == VISIBLE
get() = binding.enabled.isVisible
set(value) {
binding.enabled.visibility = if (value) VISIBLE else GONE
}

View file

@ -1,11 +1,14 @@
package icu.nullptr.hidemyapplist.ui.view
import android.content.Context
import android.graphics.Typeface
import android.util.AttributeSet
import android.widget.LinearLayout
import androidx.annotation.DrawableRes
import androidx.core.view.isVisible
import by.kirich1409.viewbindingdelegate.CreateMethod
import by.kirich1409.viewbindingdelegate.viewBinding
import icu.nullptr.hidemyapplist.ui.util.ThemeUtils.themeColor
import org.frknkrc44.hma_oss.R
import org.frknkrc44.hma_oss.databinding.ListItemViewBinding
@ -42,8 +45,28 @@ class ListItemView @JvmOverloads constructor(
binding.icon.setImageResource(icon)
}
fun showIcon(show: Boolean) {
binding.icon.isVisible = show
}
fun showAsHeader() {
showIcon(false)
with(binding.text) {
typeface = Typeface.DEFAULT_BOLD
val textColor = context.themeColor(
androidx.appcompat.R.attr.colorPrimary,
)
setTextColor(textColor)
val padding = (resources.displayMetrics.density * 8f).toInt()
setPaddingRelative(paddingStart, padding, paddingEnd, padding)
}
}
override fun setOnClickListener(l: OnClickListener?) {
if (binding.button.visibility == VISIBLE) {
if (binding.button.isVisible) {
binding.button.setOnClickListener(l)
} else {
super.setOnClickListener(l)

View file

@ -32,7 +32,7 @@ open class BaseActivity : AppCompatActivity() {
theme.applyStyle(ThemeUtils.getColorThemeStyleRes(this), true)
}
theme.applyStyle(ThemeUtils.getNightThemeStyleRes(this), true)
theme.applyStyle(ThemeUtils.getOverlayThemeStyleRes(this), true)
}
override fun attachBaseContext(newBase: Context?) {

View file

@ -5,6 +5,7 @@ import android.content.Context
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import icu.nullptr.hidemyapplist.common.AppPresets
import icu.nullptr.hidemyapplist.common.SettingsPresets
import icu.nullptr.hidemyapplist.service.ConfigManager
import icu.nullptr.hidemyapplist.ui.view.ListItemView
import org.frknkrc44.hma_oss.BuildConfig
@ -15,23 +16,31 @@ class AppPresetListAdapter(
private val onClickListener: ((ConfigManager.PresetInfo) -> Unit)?
) : RecyclerView.Adapter<AppPresetListAdapter.ViewHolder>() {
private lateinit var list: List<ConfigManager.PresetInfo>
private var list = mutableListOf<ConfigManager.PresetInfo>()
init {
updateList(context)
}
inner class ViewHolder(view: ListItemView) : RecyclerView.ViewHolder(view) {
init {
view.setOnClickListener {
onClickListener?.invoke(list[absoluteAdapterPosition])
}
}
fun bind(presetName: String) {
fun bind(item: ConfigManager.PresetInfo) {
with(itemView as ListItemView) {
setIcon(R.drawable.baseline_assignment_24)
text = presetName
if (item.type == null) {
showAsHeader()
} else {
setIcon(
when (item.type) {
ConfigManager.PresetType.APP -> R.drawable.baseline_assignment_24
ConfigManager.PresetType.SETTINGS -> R.drawable.baseline_settings_24
}
)
itemView.setOnClickListener {
onClickListener?.invoke(item)
}
}
text = item.translation
}
}
}
@ -49,12 +58,14 @@ class AppPresetListAdapter(
override fun getItemId(position: Int) = list[position].name.hashCode().toLong()
override fun onBindViewHolder(holder: ViewHolder, position: Int) = holder.bind(list[position].translation)
override fun onBindViewHolder(holder: ViewHolder, position: Int) = holder.bind(list[position])
@SuppressLint("NotifyDataSetChanged", "DiscouragedApi")
private fun updateList(context: Context) {
val presetNames = AppPresets.instance.getAllPresetNames()
val presetTranslations = presetNames.map { name ->
list.clear()
val appPresetNames = AppPresets.instance.getAllPresetNames()
val appPresetTranslations = appPresetNames.map { name ->
try {
val id = context.resources.getIdentifier(
"preset_${name}",
@ -68,9 +79,49 @@ class AppPresetListAdapter(
name
}
list = presetNames
.map { ConfigManager.PresetInfo(it, presetTranslations[presetNames.indexOf(it)]) }
list += ConfigManager.PresetInfo(
"preset",
null,
context.getString(R.string.title_preset),
)
list += appPresetNames
.map { ConfigManager.PresetInfo(
it,
ConfigManager.PresetType.APP,
appPresetTranslations[appPresetNames.indexOf(it)]
) }
.sortedWith { a, b -> a.translation.lowercase().compareTo(b.translation.lowercase()) }
list += ConfigManager.PresetInfo(
"settings_preset",
null,
context.getString(R.string.title_settings_preset),
)
val settingsPresetNames = SettingsPresets.instance.getAllPresetNames()
val settingsPresetTranslations = settingsPresetNames.map { name ->
try {
val id = context.resources.getIdentifier(
"settings_preset_${name}",
"string",
BuildConfig.APPLICATION_ID
)
return@map if (id != 0) { context.resources.getString(id) } else { name }
} catch (_: Throwable) {}
name
}
list += settingsPresetNames
.map { ConfigManager.PresetInfo(
it,
ConfigManager.PresetType.SETTINGS,
settingsPresetTranslations[settingsPresetNames.indexOf(it)]
) }
.sortedWith { a, b -> a.translation.lowercase().compareTo(b.translation.lowercase()) }
notifyDataSetChanged()
}
}

View file

@ -0,0 +1,37 @@
package org.frknkrc44.hma_oss.ui.adapter
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
import icu.nullptr.hidemyapplist.common.SettingsPresets
import icu.nullptr.hidemyapplist.common.settings_presets.ReplacementItem
import icu.nullptr.hidemyapplist.ui.view.AppItemView
class SettingsPresetListAdapter(presetName: String) : RecyclerView.Adapter<SettingsPresetListAdapter.ViewHolder>() {
private val presetItems by lazy {
SettingsPresets.instance.getPresetByName(presetName)!!.settingsKVPairs.sortedBy { it.name }
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = AppItemView(parent.context, false)
view.layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) = holder.bind(presetItems[position])
override fun getItemCount() = presetItems.size
class ViewHolder(view: AppItemView) : RecyclerView.ViewHolder(view) {
fun bind(item: ReplacementItem) {
(itemView as AppItemView).apply {
binding.icon.isVisible = false
binding.label.text = item.name
binding.packageName.text = item.value ?: "null"
}
}
}
}

View file

@ -12,6 +12,7 @@ import androidx.core.net.toUri
import androidx.fragment.app.Fragment
import by.kirich1409.viewbindingdelegate.viewBinding
import com.bumptech.glide.Glide
import icu.nullptr.hidemyapplist.service.PrefManager
import icu.nullptr.hidemyapplist.ui.util.ThemeUtils.homeItemBackgroundColor
import icu.nullptr.hidemyapplist.ui.util.navController
import org.frknkrc44.hma_oss.R
@ -73,6 +74,11 @@ class AboutFragment : Fragment(R.layout.fragment_about) {
appName.setText(R.string.app_name)
appVersion.text = BuildConfig.APP_VERSION_NAME
if (PrefManager.systemWallpaper) {
linkGithub.background.alpha = 0xAA
linkTelegram.background.alpha = 0xAA
}
setOnClickUrl(linkGithub, "https://github.com/frknkrc44/HMA-OSS")
setOnClickUrl(linkTelegram, "https://t.me/aerathfuns")

View file

@ -17,6 +17,7 @@ import by.kirich1409.viewbindingdelegate.viewBinding
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import icu.nullptr.hidemyapplist.hmaApp
import icu.nullptr.hidemyapplist.service.ConfigManager
import icu.nullptr.hidemyapplist.service.PrefManager
import icu.nullptr.hidemyapplist.service.ServiceClient
import icu.nullptr.hidemyapplist.ui.util.ThemeUtils.attrDrawable
import icu.nullptr.hidemyapplist.ui.util.ThemeUtils.getColor
@ -115,11 +116,13 @@ class HomeFragment : Fragment(R.layout.fragment_home) {
super.onStart()
val serviceVersion = ServiceClient.serviceVersion
val color = when {
var color = when {
serviceVersion == 0 -> getColor(R.color.invalid)
else -> themeColor(android.R.attr.colorPrimary)
}
if (PrefManager.systemWallpaper) color -= 0x55000000
with(binding.statusCard) {
root.setCardBackgroundColor(color)
root.outlineAmbientShadowColor = color
@ -294,12 +297,20 @@ class HomeFragment : Fragment(R.layout.fragment_home) {
}
}
binding.backupConfig.setOnClickListener {
backupSAFLauncher.launch("HMA_Config_${System.currentTimeMillis()}.json")
with(binding.backupConfig) {
if (PrefManager.systemWallpaper) background.alpha = 0xAA
setOnClickListener {
backupSAFLauncher.launch("HMA_Config_${System.currentTimeMillis()}.json")
}
}
binding.restoreConfig.setOnClickListener {
restoreSAFLauncher.launch("application/json")
with(binding.restoreConfig) {
if (PrefManager.systemWallpaper) background.alpha = 0xAA
setOnClickListener {
restoreSAFLauncher.launch("application/json")
}
}
}

View file

@ -60,7 +60,15 @@ class PresetManageFragment : Fragment(R.layout.fragment_preset_manage) {
}
private fun navigateToPreset(presetInfo: ConfigManager.PresetInfo) {
val args = AppPresetFragmentArgs(presetInfo.name, presetInfo.translation)
navigate(R.id.nav_preset_inner_manage, args.toBundle())
when (presetInfo.type!!) {
ConfigManager.PresetType.APP -> {
val args = AppPresetFragmentArgs(presetInfo.name, presetInfo.translation)
navigate(R.id.nav_preset_inner_manage, args.toBundle())
}
ConfigManager.PresetType.SETTINGS -> {
val args = SettingsPresetFragmentArgs(presetInfo.name, presetInfo.translation)
navigate(R.id.nav_settings_preset_inner_manage, args.toBundle())
}
}
}
}

View file

@ -0,0 +1,83 @@
package org.frknkrc44.hma_oss.ui.fragment
import android.os.Build
import android.os.Bundle
import android.view.View
import android.view.WindowInsets
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.LinearLayoutManager
import by.kirich1409.viewbindingdelegate.viewBinding
import icu.nullptr.hidemyapplist.ui.util.navController
import icu.nullptr.hidemyapplist.ui.util.setupToolbar
import org.frknkrc44.hma_oss.R
import org.frknkrc44.hma_oss.databinding.FragmentSettingsPresetBinding
import org.frknkrc44.hma_oss.ui.adapter.SettingsPresetListAdapter
/**
* Use the [SettingsPresetFragment.newInstance] factory method to
* create an instance of this fragment.
*/
class SettingsPresetFragment : Fragment(R.layout.fragment_settings_preset) {
private val binding by viewBinding<FragmentSettingsPresetBinding>()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val args by navArgs<SettingsPresetFragmentArgs>()
setupToolbar(
toolbar = binding.toolbar,
title = args.presetTitle,
navigationIcon = R.drawable.baseline_arrow_back_24,
navigationOnClick = { navController.navigateUp() }
)
binding.list.layoutManager = LinearLayoutManager(context)
binding.list.adapter = SettingsPresetListAdapter(args.presetName)
binding.root.setOnApplyWindowInsetsListener { v, insets ->
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val barInsets = insets.getInsets(WindowInsets.Type.systemBars())
binding.root.setPadding(
barInsets.left,
barInsets.top,
barInsets.right,
barInsets.bottom,
)
} else {
@Suppress("deprecation")
binding.root.setPadding(
insets.systemWindowInsetLeft,
insets.systemWindowInsetTop,
insets.systemWindowInsetRight,
insets.systemWindowInsetBottom,
)
}
insets
}
}
companion object {
private const val ARG_PRESET_NAME = "presetName"
private const val ARG_PRESET_TITLE = "presetTitle"
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param presetName Name of the preset.
* @param presetTitle Title of the preset.
* @return A new instance of fragment SettingsPresetFragment.
*/
@JvmStatic
fun newInstance(presetName: String, presetTitle: String) =
SettingsPresetFragment().apply {
arguments = Bundle().apply {
putString(ARG_PRESET_NAME, presetName)
putString(ARG_PRESET_TITLE, presetTitle)
}
}
}
}

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