Skip to content

Commit

Permalink
Merge pull request #86 from flxapps/feat/uninstall-detoxdroid
Browse files Browse the repository at this point in the history
feat(uninstall-detoxdroid): implemented "uninstall DetoxDroid" button showing when Device Admin permission has been granted
  • Loading branch information
flxapps authored Nov 21, 2023
2 parents 247c3ac + ac16e78 commit 461a92c
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 7 deletions.
4 changes: 2 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ android {
applicationId = "com.flx_apps.digitaldetox"
minSdk = 26
targetSdk = 33
versionCode = 20000
versionName = "2.0.0"
versionCode = 20001
versionName = "2.0.1"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
Expand Down
3 changes: 3 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@
<!-- android.permission.KILL_BACKGROUND_PROCESSES permission to kill apps from the overlay -->
<uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" />

<!-- REQUEST_DELETE_PACKAGES permission to self-uninstall -->
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />

<application
android:name=".DetoxDroidApplication"
android:allowBackup="true"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ open class DetoxDroidAccessibilityService : AccessibilityService() {
override fun onDestroy() {
super.onDestroy()
PauseButtonFeature.pauseFeatures(this, stop = true)
unregisterReceiver(screenTurnedOffReceiver)
kotlin.runCatching { unregisterReceiver(screenTurnedOffReceiver) }
instance = null
updateState()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.DeleteForever
import androidx.compose.material.icons.filled.KeyboardArrowRight
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Divider
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExtendedFloatingActionButton
import androidx.compose.material3.Icon
Expand All @@ -29,6 +32,7 @@ import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.SnackbarResult
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
Expand All @@ -54,12 +58,15 @@ import com.flx_apps.digitaldetox.R
import com.flx_apps.digitaldetox.feature_types.Feature
import com.flx_apps.digitaldetox.features.FeaturesProvider
import com.flx_apps.digitaldetox.features.PauseButtonFeature
import com.flx_apps.digitaldetox.system_integration.DetoxDroidDeviceAdminReceiver
import com.flx_apps.digitaldetox.system_integration.DetoxDroidState
import com.flx_apps.digitaldetox.system_integration.UsageStatsProvider
import com.flx_apps.digitaldetox.ui.screens.nav_host.NavViewModel
import com.flx_apps.digitaldetox.ui.widgets.InfoCard
import com.flx_apps.digitaldetox.ui.widgets.SimpleListTile
import com.flx_apps.digitaldetox.util.NavigationUtil
import com.flx_apps.digitaldetox.util.toHrMinString
import kotlinx.coroutines.flow.MutableStateFlow
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle
Expand Down Expand Up @@ -183,7 +190,7 @@ private fun StartStopActionButton(
* The content of the home screen. It displays the screen time chart and a list of all features.
*/
@Composable
private fun HomeScreenContent(it: PaddingValues) {
private fun HomeScreenContent(it: PaddingValues, viewModel: HomeViewModel = viewModel()) {
LazyColumn(
modifier = Modifier.padding(it),
verticalArrangement = Arrangement.spacedBy(8.dp),
Expand All @@ -196,6 +203,8 @@ private fun HomeScreenContent(it: PaddingValues) {
OpenFeatureTile(feature = feature)
}
item {
UninstallDetoxDroidTile()
// bottom logo
Image(
painter = painterResource(id = R.drawable.ic_launcher_foreground),
contentDescription = "Logo",
Expand Down Expand Up @@ -245,6 +254,45 @@ fun OpenFeatureTile(
return
}

/**
* A tile that uninstalls DetoxDroid when clicked. It is only shown if the device admin permission
* is granted. A confirmation dialog is shown when before revoking the device admin permission.
*/
@Composable
fun UninstallDetoxDroidTile(viewModel: HomeViewModel = viewModel()) {
// don't show the uninstall tile if the device admin permission is not granted
if (!DetoxDroidDeviceAdminReceiver.isGranted(LocalContext.current)) return

val showAreYouSureDialog = remember { MutableStateFlow(false) }
if (showAreYouSureDialog.collectAsState().value) {
// confirmation dialog
AlertDialog(title = {
Text(text = stringResource(id = R.string.home_uninstall_dialog_title))
}, text = {
Text(text = stringResource(id = R.string.home_uninstall_dialog_message))
}, onDismissRequest = {
showAreYouSureDialog.value = false
}, confirmButton = {
TextButton(onClick = {
viewModel.uninstallDetoxDroid()
}) {
Text(text = stringResource(id = R.string.home_uninstall))
}
})
}

Divider()
SimpleListTile(leadingIcon = Icons.Default.DeleteForever,
titleText = stringResource(id = R.string.home_uninstall),
subtitleText = stringResource(
id = R.string.home_uninstall_hint
),
onClick = {
showAreYouSureDialog.value = true
})
Divider()
}

/**
* A [DonutPieChart] that displays the screen time of the current day and the apps that were used
* today.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ package com.flx_apps.digitaldetox.ui.screens.home;
import android.app.Application
import android.content.Intent
import android.provider.Settings
import androidx.core.net.toUri
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import com.flx_apps.digitaldetox.DetoxDroidApplication
import com.flx_apps.digitaldetox.system_integration.DetoxDroidAccessibilityService
import com.flx_apps.digitaldetox.system_integration.DetoxDroidDeviceAdminReceiver
import com.flx_apps.digitaldetox.system_integration.DetoxDroidState
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
Expand All @@ -15,6 +17,7 @@ import kotlinx.coroutines.flow.onEmpty
import kotlinx.coroutines.launch
import javax.inject.Inject


val AccessibilityServiceComponent =
DetoxDroidApplication::class.java.`package`!!.name + "/" + DetoxDroidAccessibilityService::class.java.name

Expand Down Expand Up @@ -132,4 +135,19 @@ class HomeViewModel @Inject constructor(
)
)
}

/**
* Stops DetoxDroid and all running features, revokes the device admin permission and uninstalls.
*/
fun uninstallDetoxDroid() {
// call onDestroy() manually to run all cleanup tasks (e.g. stop all features) in a blocking way
// (as application.stopService() is asynchronous)
DetoxDroidAccessibilityService.instance?.onDestroy()
kotlin.runCatching { disableAccessibilityService() } // run this anyway
kotlin.runCatching { DetoxDroidDeviceAdminReceiver.revokePermission(application) }
val intent = Intent(Intent.ACTION_DELETE)
intent.data = "package:${application.packageName}".toUri()
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
application.startActivity(intent)
}
}
7 changes: 4 additions & 3 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,6 @@
<string name="navigation.reportIssue">Report Issue</string>
<string name="navigation.twitter">\@DetoxDroid on Twitter</string>
<string name="navigation.about">About</string>
<string name="navigation.uninstall">Uninstall DetoxDroid</string>
<string name="navigation.uninstall.dialog.title">Attention</string>
<string name="navigation.uninstall.dialog.message">It might not be possible for you to uninstall DetoxDroid from the normal app manager, as you have granted particular permissions by installing it from your computer. Using this, you can revoke these permissions and uninstall DetoxDroid.\n\n<b>If you have apps deactivated right now through DetoxDroid, please make sure to re-activate them first.</b></string>
<!-- endregion -->

<string name="home.start">Start DetoxDroid</string>
Expand All @@ -43,6 +40,10 @@
<string name="home.hint">DetoxDroid will support you in winding down your smartphone use.</string>
<string name="home.screenTime.today">Screen time today</string>
<string name="home.screenTime.unavailable">Screen time unavailable</string>
<string name="home.uninstall">Uninstall DetoxDroid</string>
<string name="home.uninstall.hint">It might not be possible for you to uninstall DetoxDroid normally, as you have granted particular permissions by installing it from your computer. Press here to uninstall.</string>
<string name="home.uninstall.dialog.title">Are You Sure?</string>
<string name="home.uninstall.dialog.message">Pressing \"Uninstall\" will revoke permissions that you granted to DetoxDroid. You will then be able to uninstall it normally.\n\nIf you choose to use DetoxDroid again, you will have to grant these permissions again.</string>

<string name="feature.doNotDisturb">\"Do Not Disturb\" Mode</string>
<string name="feature.doNotDisturb.subtitle">Make it your new default</string>
Expand Down
1 change: 1 addition & 0 deletions fastlane/metadata/android/en-US/changelogs/20001.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Re-implemented "Uninstall DetoxDroid" button showing when Device Admin permission has been granted

0 comments on commit 461a92c

Please sign in to comment.