diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 3e03568..8bf2a53 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -33,4 +33,6 @@ android { dependencies { compileOnly("de.robv.android.xposed:api:82") + implementation("androidx.appcompat:appcompat:1.7.0") + implementation("com.google.android.material:material:1.12.0") } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a220d21..00da750 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,6 +2,9 @@ @@ -18,7 +21,8 @@ + android:label="@string/app_name" + android:theme="@style/Theme.Veil"> diff --git a/app/src/main/java/space/bdeshi/veil/SettingsActivity.kt b/app/src/main/java/space/bdeshi/veil/SettingsActivity.kt index 762feca..5284f16 100644 --- a/app/src/main/java/space/bdeshi/veil/SettingsActivity.kt +++ b/app/src/main/java/space/bdeshi/veil/SettingsActivity.kt @@ -1,6 +1,5 @@ package space.bdeshi.veil -import android.app.Activity import android.content.Context import android.content.SharedPreferences import android.graphics.Typeface @@ -8,12 +7,17 @@ import android.os.Bundle import android.text.InputType import android.view.Gravity import android.view.View -import android.widget.Button -import android.widget.CheckBox -import android.widget.EditText import android.widget.LinearLayout import android.widget.ScrollView import android.widget.TextView +import androidx.appcompat.app.AppCompatActivity +import com.google.android.material.button.MaterialButton +import com.google.android.material.card.MaterialCardView +import com.google.android.material.checkbox.MaterialCheckBox +import com.google.android.material.chip.Chip +import com.google.android.material.chip.ChipGroup +import com.google.android.material.textfield.TextInputEditText +import com.google.android.material.textfield.TextInputLayout import java.io.File private val HELP_TIPS: List> = listOf( @@ -35,19 +39,19 @@ private val HELP_TIPS: List> = listOf( "the target app. No log lines from Veil there usually means it never got injected " + "(see the DenyList tip above), not a bug in this module.", "Forgot what an unconfigured app does?" to - "Any app not in the list above gets every feature applied by default — check here " + + "Any app not in the list below gets every feature applied by default — check here " + "first if something's being hidden that you didn't expect.", ) -class SettingsActivity : Activity() { +class SettingsActivity : AppCompatActivity() { private lateinit var prefs: SharedPreferences private lateinit var appListContainer: LinearLayout - private lateinit var hiddenPackagesContainer: LinearLayout - private lateinit var helpContainer: LinearLayout - private lateinit var helpToggle: Button - private lateinit var packageInput: EditText - private lateinit var hiddenPackageInput: EditText + private lateinit var hiddenPackagesChips: ChipGroup + private lateinit var helpContainer: View + private lateinit var helpToggle: MaterialButton + private lateinit var packageInput: TextInputEditText + private lateinit var hiddenPackageInput: TextInputEditText private var helpVisible = false override fun onCreate(savedInstanceState: Bundle?) { @@ -62,11 +66,17 @@ class SettingsActivity : Activity() { root.addView(TextView(this).apply { text = "Veil" - textSize = 20f + textSize = 26f setTypeface(typeface, Typeface.BOLD) }) + root.addView(TextView(this).apply { + text = "Hide device state from specific apps, per app." + textSize = 14f + alpha = 0.7f + setPadding(0, dp(2), 0, dp(16)) + }) - helpToggle = Button(this).apply { + helpToggle = MaterialButton(this).apply { text = "Help & troubleshooting ▾" setOnClickListener { helpVisible = !helpVisible @@ -76,36 +86,34 @@ class SettingsActivity : Activity() { } root.addView(helpToggle) - helpContainer = LinearLayout(this).apply { - orientation = LinearLayout.VERTICAL - setPadding(0, dp(8), 0, dp(8)) - visibility = View.GONE - for ((title, body) in HELP_TIPS) { - addView(TextView(this@SettingsActivity).apply { - text = title - setTypeface(typeface, Typeface.BOLD) - setPadding(0, dp(8), 0, dp(2)) - }) - addView(TextView(this@SettingsActivity).apply { - text = body - textSize = 13f - }) - } + val (helpCard, helpContent) = card() + helpCard.visibility = View.GONE + helpContainer = helpCard + for ((title, body) in HELP_TIPS) { + helpContent.addView(TextView(this).apply { + text = title + setTypeface(typeface, Typeface.BOLD) + setPadding(0, dp(8), 0, dp(2)) + }) + helpContent.addView(TextView(this).apply { + text = body + textSize = 13f + alpha = 0.85f + }) } - root.addView(helpContainer) + root.addView(helpCard, marginTop(dp(8))) + root.addView(sectionTitle("Apps")) root.addView(TextView(this).apply { text = "Add the package name of an app you've scoped this module to in LSPosed, " + - "then choose which checks to hide from it. Apps you haven't added here get " + - "everything hidden by default (matches this module's original behavior)." - setPadding(0, dp(8), 0, dp(16)) + "or just scope it in LSPosed and open the target app once — Veil picks it up " + + "automatically. Apps not listed here get everything hidden by default." + textSize = 13f + alpha = 0.8f + setPadding(0, dp(4), 0, dp(8)) }) - packageInput = EditText(this).apply { - hint = "e.g. com.example.targetapp" - inputType = InputType.TYPE_CLASS_TEXT - layoutParams = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f) - } + packageInput = textInput("e.g. com.example.targetapp") root.addView(addRow(packageInput) { val pkg = packageInput.text.toString().trim() if (pkg.isNotEmpty() && pkg.contains(".")) { @@ -118,28 +126,20 @@ class SettingsActivity : Activity() { appListContainer = LinearLayout(this).apply { orientation = LinearLayout.VERTICAL - setPadding(0, dp(16), 0, 0) + setPadding(0, dp(8), 0, 0) } root.addView(appListContainer) + root.addView(sectionTitle("Packages hidden from PackageManager"), marginTop(dp(24))) root.addView(TextView(this).apply { - text = "Packages hidden from PackageManager" - textSize = 18f - setTypeface(typeface, Typeface.BOLD) - setPadding(0, dp(24), 0, dp(4)) - }) - root.addView(TextView(this).apply { - text = "Used by the \"Hide root / manager apps\" checkbox above — apps in this list " + - "become invisible to PackageManager lookups for any target app with that box " + - "checked." - setPadding(0, 0, 0, dp(8)) + text = "Used by the \"Hide root / manager apps\" checkbox above — apps here become " + + "invisible to PackageManager lookups for any target app with that box checked." + textSize = 13f + alpha = 0.8f + setPadding(0, dp(4), 0, dp(8)) }) - hiddenPackageInput = EditText(this).apply { - hint = "e.g. me.weishu.kernelsu" - inputType = InputType.TYPE_CLASS_TEXT - layoutParams = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f) - } + hiddenPackageInput = textInput("e.g. me.weishu.kernelsu") root.addView(addRow(hiddenPackageInput) { val pkg = hiddenPackageInput.text.toString().trim() if (pkg.isNotEmpty() && pkg.contains(".")) { @@ -152,29 +152,69 @@ class SettingsActivity : Activity() { } }) - hiddenPackagesContainer = LinearLayout(this).apply { - orientation = LinearLayout.VERTICAL + hiddenPackagesChips = ChipGroup(this).apply { setPadding(0, dp(8), 0, 0) } - root.addView(hiddenPackagesContainer) + root.addView(hiddenPackagesChips) setContentView(ScrollView(this).apply { addView(root) }) refreshAppList() refreshHiddenPackages() } - private fun addRow(input: EditText, onAdd: () -> Unit): View { + private fun sectionTitle(text: String): TextView = TextView(this).apply { + this.text = text + textSize = 18f + setTypeface(typeface, Typeface.BOLD) + } + + private fun marginTop(px: Int): LinearLayout.LayoutParams = + LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT).apply { + topMargin = px + } + + private fun textInput(hintText: String): TextInputEditText { + val editText = TextInputEditText(this).apply { + hint = hintText + inputType = InputType.TYPE_CLASS_TEXT + } + return editText + } + + private fun addRow(input: TextInputEditText, onAdd: () -> Unit): View { + val layout = TextInputLayout(this).apply { + layoutParams = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f) + addView(input) + } return LinearLayout(this).apply { orientation = LinearLayout.HORIZONTAL gravity = Gravity.CENTER_VERTICAL - addView(input) - addView(Button(this@SettingsActivity).apply { + addView(layout) + addView(MaterialButton(this@SettingsActivity).apply { text = "Add" setOnClickListener { onAdd() } }) } } + /** A MaterialCardView with an inner padded vertical container, added to root with bottom margin. */ + private fun card(): Pair { + val content = LinearLayout(this).apply { + orientation = LinearLayout.VERTICAL + setPadding(dp(16), dp(16), dp(16), dp(16)) + } + val cardView = MaterialCardView(this).apply { + radius = dp(12).toFloat() + cardElevation = dp(1).toFloat() + layoutParams = LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.WRAP_CONTENT, + ).apply { bottomMargin = dp(12) } + addView(content) + } + return cardView to content + } + private fun hiddenPackages(): Set = prefs.getStringSet(Features.HIDDEN_PACKAGES_KEY, Features.DEFAULT_HIDDEN_PACKAGES) ?: Features.DEFAULT_HIDDEN_PACKAGES @@ -193,6 +233,8 @@ class SettingsActivity : Activity() { appListContainer.addView(TextView(this).apply { text = "No apps yet — scope Veil to a target app in LSPosed Manager and open " + "it once, or add a package name above." + textSize = 13f + alpha = 0.8f }) return } @@ -204,11 +246,8 @@ class SettingsActivity : Activity() { private fun buildPackageCard(pkg: String, isDiscoveredOnly: Boolean): View { val enabled = prefs.getStringSet(Features.keyFor(pkg), Features.ALL) ?: Features.ALL + val (cardView, content) = card() - val card = LinearLayout(this).apply { - orientation = LinearLayout.VERTICAL - setPadding(0, dp(12), 0, dp(12)) - } val titleRow = LinearLayout(this).apply { orientation = LinearLayout.HORIZONTAL gravity = Gravity.CENTER_VERTICAL @@ -217,22 +256,27 @@ class SettingsActivity : Activity() { text = pkg textSize = 16f setTypeface(typeface, Typeface.BOLD) + layoutParams = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f) }) if (isDiscoveredOnly) { - titleRow.addView(TextView(this).apply { - text = " (auto-detected)" - textSize = 13f + titleRow.addView(Chip(this).apply { + text = "Auto-detected" + isClickable = false + isCheckable = false }) } - card.addView(titleRow) - card.addView(TextView(this).apply { - text = "Checking a box below hides that thing from this app specifically — it stays " + - "on for every other app." - setPadding(0, 0, 0, dp(8)) + content.addView(titleRow) + + content.addView(TextView(this).apply { + text = "Checking a box below hides that thing from this app specifically — it " + + "stays on for every other app." + textSize = 12f + alpha = 0.7f + setPadding(0, dp(2), 0, dp(8)) }) for ((id, info) in Features.INFO) { - card.addView(CheckBox(this).apply { + content.addView(MaterialCheckBox(this).apply { text = info.label isChecked = id in enabled setOnCheckedChangeListener { _, isChecked -> @@ -242,15 +286,16 @@ class SettingsActivity : Activity() { fixPermissions() } }) - card.addView(TextView(this).apply { + content.addView(TextView(this).apply { text = info.description textSize = 13f + alpha = 0.7f setPadding(dp(32), 0, 0, dp(8)) }) } - card.addView(Button(this).apply { - text = "Remove (revert to default: hide everything)" + content.addView(MaterialButton(this).apply { + text = "Apply defaults (hide everything)" setOnClickListener { prefs.edit().remove(Features.keyFor(pkg)).apply() fixPermissions() @@ -258,32 +303,16 @@ class SettingsActivity : Activity() { } }) - return card + return cardView } private fun refreshHiddenPackages() { - hiddenPackagesContainer.removeAllViews() - val packages = hiddenPackages().sorted() - - if (packages.isEmpty()) { - hiddenPackagesContainer.addView(TextView(this).apply { - text = "None configured." - }) - return - } - - for (pkg in packages) { - val row = LinearLayout(this).apply { - orientation = LinearLayout.HORIZONTAL - gravity = Gravity.CENTER_VERTICAL - } - row.addView(TextView(this).apply { + hiddenPackagesChips.removeAllViews() + for (pkg in hiddenPackages().sorted()) { + hiddenPackagesChips.addView(Chip(this).apply { text = pkg - layoutParams = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f) - }) - row.addView(Button(this).apply { - text = "Remove" - setOnClickListener { + isCloseIconVisible = true + setOnCloseIconClickListener { val current = hiddenPackages().toMutableSet() current.remove(pkg) prefs.edit().putStringSet(Features.HIDDEN_PACKAGES_KEY, current).apply() @@ -291,7 +320,6 @@ class SettingsActivity : Activity() { refreshHiddenPackages() } }) - hiddenPackagesContainer.addView(row) } } diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..fc1b6cb --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,13 @@ + + + + + diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..c7bd273 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,4 @@ + + + + diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..c7bd273 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,4 @@ + + + + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..d46fea5 --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,3 @@ + + #1B1B3A + diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml new file mode 100644 index 0000000..1de5933 --- /dev/null +++ b/app/src/main/res/values/themes.xml @@ -0,0 +1,3 @@ + +