Skip to content

Commit

Permalink
feat: add setup for optional permissions
Browse files Browse the repository at this point in the history
  • Loading branch information
timschneeb committed Mar 24, 2023
1 parent 0d08023 commit 25b8db9
Show file tree
Hide file tree
Showing 18 changed files with 232 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ class OnboardingActivity : BaseActivity(){
binding = ActivityOnboardingBinding.inflate(layoutInflater)
setContentView(binding.root)

fragment

supportFragmentManager
.beginTransaction()
.replace(R.id.onboarding_fragment_container, fragment)
Expand All @@ -55,9 +53,7 @@ class OnboardingActivity : BaseActivity(){
{
val finished = !fragment.onBackPressed()
if(finished)
{
this.finishAffinity()
}
finish()
return finished
}

Expand All @@ -74,5 +70,6 @@ class OnboardingActivity : BaseActivity(){
{
const val EXTRA_FIX_PERMS = "FixPermissions"
const val EXTRA_ROOT_SETUP_DUMP_PERM = "RootSetupDumpPerm"
const val EXTRA_ROOTLESS_REDO_ADB_SETUP = "RootlessRedoAdbSetup"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import me.timschneeberger.hiddenapi_impl.UserHandle
import me.timschneeberger.rootlessjamesdsp.BuildConfig
import me.timschneeberger.rootlessjamesdsp.R
import me.timschneeberger.rootlessjamesdsp.activity.MainActivity
import me.timschneeberger.rootlessjamesdsp.activity.OnboardingActivity.Companion.EXTRA_ROOTLESS_REDO_ADB_SETUP
import me.timschneeberger.rootlessjamesdsp.activity.OnboardingActivity.Companion.EXTRA_ROOT_SETUP_DUMP_PERM
import me.timschneeberger.rootlessjamesdsp.databinding.OnboardingFragmentBinding
import me.timschneeberger.rootlessjamesdsp.flavor.RootShellImpl
Expand Down Expand Up @@ -71,6 +72,7 @@ class OnboardingFragment : Fragment() {
private lateinit var runtimePermissionLauncher: ActivityResultLauncher<Array<String>>

private var useRoot: Boolean = false
private var redoAdbSetup: Boolean = false
private var shizukuAlive = false

private val prefsApp: Preferences.App by inject()
Expand Down Expand Up @@ -115,6 +117,7 @@ class OnboardingFragment : Fragment() {
bundle: Bundle?
): View {
useRoot = requireActivity().intent.getBooleanExtra(EXTRA_ROOT_SETUP_DUMP_PERM, false)
redoAdbSetup = requireActivity().intent.getBooleanExtra(EXTRA_ROOTLESS_REDO_ADB_SETUP, false)
binding = OnboardingFragmentBinding.inflate(layoutInflater, viewGroup, false)
return binding.root
}
Expand All @@ -127,7 +130,7 @@ class OnboardingFragment : Fragment() {
backButton.setOnClickListener { changePage(false) }
nextButton.setOnClickListener { changePage(true) }

if(useRoot) {
if(useRoot || redoAdbSetup) {
pageMap.remove(PAGE_RUNTIME_PERMISSIONS)
pageMap.remove(PAGE_READY)
goToPage(PAGE_METHOD_SELECT)
Expand Down Expand Up @@ -163,7 +166,6 @@ class OnboardingFragment : Fragment() {
methodPage.methodsShizukuCard.setCardBackgroundColor(
requireContext().resolveColorAttribute(com.google.android.material.R.attr.colorSecondaryContainer)
)

}

methodPage.methodsRootCard.setOnClickListener {
Expand Down Expand Up @@ -333,9 +335,11 @@ class OnboardingFragment : Fragment() {
@SuppressLint("ApplySharedPref")
private fun finishSetup() {

val intent = context?.let { Intent(it, MainActivity::class.java) } ?: return
intent.putExtra(MainActivity.EXTRA_FORCE_SHOW_CAPTURE_PROMPT, true)
startActivity(intent)
if(!redoAdbSetup) {
val intent = context?.let { Intent(it, MainActivity::class.java) } ?: return
intent.putExtra(MainActivity.EXTRA_FORCE_SHOW_CAPTURE_PROMPT, true)
startActivity(intent)
}
activity?.finish()

// Mark setup as done
Expand Down Expand Up @@ -414,8 +418,8 @@ class OnboardingFragment : Fragment() {
return
}

// Root setup; cut-off first two pages
if(useRoot && !forward && (currentPage - 1) <= PAGE_LIMITATIONS) {
// Root setup or rootless re-setup; cut-off first two pages
if((redoAdbSetup || useRoot) && !forward && (currentPage - 1) <= PAGE_LIMITATIONS) {
requireActivity().finish()
return
}
Expand All @@ -426,8 +430,9 @@ class OnboardingFragment : Fragment() {
private fun requestNextPage(nextPage: Int, forward: Boolean): Int
{
val shouldSkip = when (nextPage) {
PAGE_METHOD_SELECT -> requireContext().hasDumpPermission()
PAGE_ADB_SETUP -> requireContext().hasDumpPermission()
// Don't skip ADB setup if redoAdbSetup is set
PAGE_METHOD_SELECT -> requireContext().hasDumpPermission() && !redoAdbSetup
PAGE_ADB_SETUP -> requireContext().hasDumpPermission() && !redoAdbSetup
PAGE_RUNTIME_PERMISSIONS -> {
requireContext().hasNotificationPermission() && requireContext().hasRecordPermission()
}
Expand All @@ -452,8 +457,11 @@ class OnboardingFragment : Fragment() {
}

private fun ensureDumpPermission(): Boolean{
// Permission already granted?
if(requireContext().hasDumpPermission()) {
/* Permission already granted?
* Note: If were are redoing the ADB setup, make sure that the Shizuku setup
* can run regardless to grant optional permissions
*/
if(requireContext().hasDumpPermission() && (!redoAdbSetup || selectedSetupMethod != SetupMethods.Shizuku)) {
Timber.d("DUMP permission granted")
return true
}
Expand Down Expand Up @@ -484,6 +492,22 @@ class OnboardingFragment : Fragment() {
Timber.e(ex)
}

// Grant permanent SYSTEM_ALERT_WINDOW op as shell
try {
val result = ShizukuSystemServerApi.AppOpsService_setMode(
ShizukuSystemServerApi.APP_OPS_OP_SYSTEM_ALERT_WINDOW,
uid,
pkg,
ShizukuSystemServerApi.APP_OPS_MODE_ALLOW
)
if(!result)
Timber.e("AppOpsService_setMode for system_alert_window failed")
}
catch (ex: Exception) {
Timber.e("AppOpsService_setMode for system_alert_window threw an exception")
Timber.e(ex)
}

// Grant permanent PROJECT_MEDIA op as shell
try {
val result = ShizukuSystemServerApi.AppOpsService_setMode(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,39 @@
package me.timschneeberger.rootlessjamesdsp.fragment.settings

import android.content.Intent
import android.os.Bundle
import android.provider.Settings
import android.util.Patterns
import androidx.core.content.ContextCompat
import androidx.preference.EditTextPreference
import androidx.preference.Preference
import me.timschneeberger.rootlessjamesdsp.BuildConfig
import me.timschneeberger.rootlessjamesdsp.R
import me.timschneeberger.rootlessjamesdsp.activity.OnboardingActivity
import me.timschneeberger.rootlessjamesdsp.api.AutoEqClient
import me.timschneeberger.rootlessjamesdsp.flavor.CrashlyticsImpl
import me.timschneeberger.rootlessjamesdsp.utils.extensions.AssetManagerExtensions.installPrivateAssets
import me.timschneeberger.rootlessjamesdsp.preference.IconPreference
import me.timschneeberger.rootlessjamesdsp.preference.MaterialSwitchPreference
import me.timschneeberger.rootlessjamesdsp.utils.Constants
import me.timschneeberger.rootlessjamesdsp.utils.extensions.AssetManagerExtensions.installPrivateAssets
import me.timschneeberger.rootlessjamesdsp.utils.extensions.ContextExtensions.showAlert
import me.timschneeberger.rootlessjamesdsp.utils.extensions.ContextExtensions.showYesNoAlert
import me.timschneeberger.rootlessjamesdsp.utils.extensions.ContextExtensions.toast
import me.timschneeberger.rootlessjamesdsp.utils.extensions.PermissionExtensions.hasProjectMediaAppOp
import me.timschneeberger.rootlessjamesdsp.utils.preferences.Preferences
import org.koin.android.ext.android.inject
import java.util.Locale

class SettingsMiscFragment : SettingsBaseFragment() {

private val autoStartNotify by lazy { findPreference<Preference>(getString(R.string.key_autostart_prompt_at_boot)) }
private val autoStartNotify by lazy { findPreference<MaterialSwitchPreference>(getString(R.string.key_autostart_prompt_at_boot)) }
private val repairAssets by lazy { findPreference<Preference>(getString(R.string.key_troubleshooting_repair_assets)) }
private val crashReports by lazy { findPreference<Preference>(getString(R.string.key_share_crash_reports)) }
private val aeqApiUrl by lazy { findPreference<EditTextPreference>(getString(R.string.key_network_autoeq_api_url)) }
private val debugDatabase by lazy { findPreference<Preference>(getString(R.string.key_debug_database)) }
private val permSkipPrompt by lazy { findPreference<IconPreference>(getString(R.string.key_misc_permission_skip_prompt)) }
private val permAutoStart by lazy { findPreference<IconPreference>(getString(R.string.key_misc_permission_auto_start)) }
private val permRestartSetup by lazy { findPreference<Preference>(getString(R.string.key_misc_permission_restart_setup)) }

private val preferences: Preferences.App by inject()

Expand Down Expand Up @@ -56,9 +66,7 @@ class SettingsMiscFragment : SettingsBaseFragment() {
client.queryProfiles(
"conntest",
onResponse = { _, _ ->
context?.let {
it.toast(R.string.network_autoeq_conntest_done, false)
}
context?.toast(R.string.network_autoeq_conntest_done, false)
},
onFailure = { error ->
context?.showYesNoAlert(
Expand All @@ -83,9 +91,63 @@ class SettingsMiscFragment : SettingsBaseFragment() {
true
}

permRestartSetup?.setOnPreferenceClickListener {
startActivity(Intent(context, OnboardingActivity::class.java).apply {
putExtra(OnboardingActivity.EXTRA_ROOTLESS_REDO_ADB_SETUP, true)
})
true
}

updatePermissionStates()

crashReports?.parent?.isVisible = !BuildConfig.FOSS_ONLY
debugDatabase?.parent?.isVisible = BuildConfig.DEBUG
autoStartNotify?.isVisible = BuildConfig.ROOTLESS
permRestartSetup?.parent?.isVisible = BuildConfig.ROOTLESS
}

override fun onResume() {
updatePermissionStates()
super.onResume()
}

private fun updatePermissionStates() {
if(!BuildConfig.ROOTLESS)
return

val allowSkipPrompt = context?.hasProjectMediaAppOp() == true
val allowAutoStart = allowSkipPrompt && Settings.canDrawOverlays(context)

autoStartNotify?.title = getString(
if(allowAutoStart) R.string.autostart_service_at_boot
else R.string.autostart_prompt_at_boot
)
autoStartNotify?.summaryOn = getString(
if(allowAutoStart) R.string.autostart_service_at_boot_on
else R.string.autostart_prompt_at_boot_on
)
autoStartNotify?.summaryOff = getString(
if(allowAutoStart) R.string.autostart_service_at_boot_off
else R.string.autostart_prompt_at_boot_off
)

fun getIcon(allowed: Boolean) = context?.let {
ContextCompat.getDrawable(it,
if(allowed) R.drawable.ic_twotone_check_circle_24dp
else R.drawable.ic_twotone_warning_24dp
)
}

fun getSummary(allowed: Boolean) = context?.getString(
if(allowed) R.string.permission_allowed
else R.string.permission_not_allowed
)

permSkipPrompt?.summary = getSummary(allowSkipPrompt)
permAutoStart?.summary = getSummary(allowAutoStart)
permSkipPrompt?.icon = getIcon(allowSkipPrompt)
permAutoStart?.icon = getIcon(allowAutoStart)
permRestartSetup?.isVisible = !allowSkipPrompt || !allowAutoStart
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package me.timschneeberger.rootlessjamesdsp.preference

import android.content.Context
import android.util.AttributeSet
import androidx.preference.Preference
import me.timschneeberger.rootlessjamesdsp.R


open class IconPreference(
mContext: Context, val attrs: AttributeSet?,
defStyleAttr: Int, defStyleRes: Int,
) : Preference(mContext, attrs, defStyleAttr, defStyleRes) {

@JvmOverloads
constructor(
context: Context, attrs: AttributeSet? = null,
defStyle: Int = androidx.preference.R.attr.preferenceStyle,
) : this(context, attrs, defStyle, 0) {
layoutResource = R.layout.preference_icon
}
}
2 changes: 1 addition & 1 deletion app/src/main/res/drawable/ic_numeric_1_circle_outline.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<!-- drawable/numeric_1_circle_outline.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:tint="#000000"
android:tint="?attr/colorControlNormal"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/res/drawable/ic_numeric_2_circle_outline.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<!-- drawable/numeric_2_circle_outline.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:tint="@android:color/black"
android:tint="?attr/colorControlNormal"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/res/drawable/ic_numeric_3_circle_outline.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<!-- drawable/numeric_3_circle_outline.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:tint="@android:color/black"
android:tint="?attr/colorControlNormal"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/res/drawable/ic_numeric_4_circle_outline.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<!-- drawable/numeric_4_circle_outline.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:tint="@android:color/black"
android:tint="?attr/colorControlNormal"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/res/drawable/ic_numeric_5_circle_outline.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<!-- drawable/numeric_5_circle_outline.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:tint="@android:color/black"
android:tint="?attr/colorControlNormal"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/res/drawable/ic_twotone_check_circle_24dp.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<vector android:height="24dp" android:tint="#000000"
<vector android:height="24dp" android:tint="?attr/colorControlNormal"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillAlpha="0.3"
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/res/drawable/ic_twotone_error_24dp.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<vector android:height="24dp" android:tint="#000000"
<vector android:height="24dp" android:tint="?attr/colorControlNormal"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillAlpha="0.3"
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/res/drawable/ic_twotone_warning_24dp.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<vector android:height="24dp" android:tint="#000000"
<vector android:height="24dp" android:tint="?attr/colorControlNormal"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillAlpha="0.3"
Expand Down
52 changes: 52 additions & 0 deletions app/src/main/res/layout/preference_icon.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:gravity="start"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="12dp"
android:paddingBottom="8dp"
android:baselineAligned="false"
android:minHeight="?android:attr/listPreferredItemHeightSmall"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">

<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">

<TextView android:textAppearance="?android:attr/textAppearanceListItem"
android:ellipsize="marquee"
android:id="@android:id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
tools:text="Title" />

<TextView android:textAppearance="?android:attr/textAppearanceListItemSecondary"
android:ellipsize="marquee"
android:gravity="start"
android:id="@android:id/summary"
android:scrollbars="none"
android:fadingEdge="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:singleLine="true"
tools:text="Value" />

</LinearLayout>

<androidx.appcompat.widget.AppCompatImageView
android:id="@android:id/icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center"
tools:srcCompat="@drawable/ic_twotone_edit_24dp" />

</LinearLayout>
Loading

0 comments on commit 25b8db9

Please sign in to comment.