Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 17 additions & 5 deletions PRIVACY_POLICY.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,34 @@ Welcome to the RecentApps app for Android!

This is a utility app designed to add a custom shortcut to your device. As the developer, I take your privacy very seriously. I have not programmed this app to collect any personally identifiable information.

---

### 🚨 Prominent Disclosure Regarding Accessibility Services

To provide its core functionality (shortcut for recent apps), RecentApps uses the **Android Accessibility API**.

**What this means for you:**
* **Purpose:** RecentApps uses the Accessibility Service solely to detect a **long-press gesture** on the system's Home button or Gesture Navigation Bar.
* **Action:** When this gesture is detected, the app performs a system action to open the "Recent Applications" screen.
* **Data Collection & Sharing:** RecentApps **does not collect, store, or share** any personal or sensitive data through the Accessibility API. We do not monitor your screen content, log your keystrokes, or track your notifications.
* **User Consent:** This service is only activated if you explicitly grant permission in your device's Accessibility settings. You can revoke this permission at any time.

---

### Data Collected by the App

I hereby state that RecentApps **does not collect, store, or transmit any personally identifiable information**. All data created by the app, such as user preferences, is stored locally in your device's private storage. This data can be erased at any time by clearing the app's data or uninstalling it. No analytics or tracking software is present in the app.

### Explanation of Permissions Requested in the App

The list of permissions required by the app can be found in the `AndroidManifest.xml` file. Below is an explanation for why each permission is necessary for the app to function.

| Permission | Why it is required |
| :---: | --- |
| `android.permission.BIND_ACCESSIBILITY_SERVICE` | This is the core permission that enables the app's main feature. The Accessibility Service is used to listen for `AccessibilityEvent`s of type `TYPE_VIEW_LONG_CLICKED` on the system's home button or gesture navigation bar. **The service does not monitor, log, or store any text you type, your notifications, or any other content on your screen.** Its sole purpose is to provide the long-press shortcut. |
| `android.permission.VIBRATE` | This permission is used to provide brief haptic feedback (a small vibration) when a valid long-press gesture is successfully detected. This confirms to the user that the action was registered. |
| `android.permission.BIND_ACCESSIBILITY_SERVICE` | Required to detect the long-press gesture on system navigation elements. |
| `android.permission.VIBRATE` | Used to provide brief haptic feedback (a small vibration) when a valid gesture is detected. |

<hr style="border:1px solid gray">

If you have any questions or concerns regarding how the app protects your privacy, please feel free to send me an email.
If you have any questions or concerns regarding how the app protects your privacy, please feel free to contact me.

Yours sincerely,
Crispim.
3 changes: 2 additions & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,13 @@ android {
buildTypes {
release {
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
ndk {
debugSymbolLevel = "SYMBOL_TABLE"
debugSymbolLevel = "FULL"
}
}
}
Expand Down
50 changes: 47 additions & 3 deletions app/src/main/java/com/crispim/recentapps/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Info
import androidx.compose.material.icons.rounded.ScreenRotation
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
Expand All @@ -36,6 +37,7 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
Expand Down Expand Up @@ -97,6 +99,7 @@ class MainActivity : ComponentActivity() {
@Composable
fun SettingsScreen() {
val context = LocalContext.current
var showDisclosureDialog by remember { mutableStateOf(false) }
var hasAccessibilityPermission by remember {
mutableStateOf(isAccessibilityServiceEnabled(context, MainService::class.java))
}
Expand All @@ -114,6 +117,17 @@ class MainActivity : ComponentActivity() {
}
}

if (showDisclosureDialog) {
AccessibilityDisclosureDialog(
onConfirm = {
showDisclosureDialog = false
val intent = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)
context.startActivity(intent)
},
onDismiss = { showDisclosureDialog = false }
)
}

Column(
modifier = Modifier
.fillMaxSize()
Expand Down Expand Up @@ -170,8 +184,7 @@ class MainActivity : ComponentActivity() {
ConfigButton(
text = "Enable Accessibility Service",
onClick = {
val intent = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)
context.startActivity(intent)
showDisclosureDialog = true
}
)
}
Expand All @@ -193,7 +206,7 @@ class MainActivity : ComponentActivity() {
fontWeight = FontWeight.SemiBold
)
Text(
text = if (hasAccessibilityPermission) "Service is ready. Hold the home button to use" else "Service needs permission",
text = if (hasAccessibilityPermission) "Service is active. Long-press the Home button to open Recent Apps. (Note: Gesture navigation is not supported yet)" else "Service needs permission",
style = MaterialTheme.typography.bodySmall,
color = Color.Gray
)
Expand Down Expand Up @@ -301,6 +314,37 @@ class MainActivity : ComponentActivity() {
}
}

@Composable
fun AccessibilityDisclosureDialog(onConfirm: () -> Unit, onDismiss: () -> Unit) {
AlertDialog(
onDismissRequest = onDismiss,
title = {
Text(
text = "Accessibility API Usage",
fontWeight = FontWeight.Bold
)
},
text = {
Text(
text = "This app requires the Accessibility Service API to provide its core functionality: Long-press the Home button to open Recent Apps.\n\n" +
"• We DO NOT collect, store, or share any personal or sensitive data from your screen.\n" +
"• You can disable this permission at any time in the system settings."
)
},
confirmButton = {
Button(onClick = onConfirm) {
Text("Accept and Enable")
}
},
dismissButton = {
TextButton(onClick = onDismiss) {
Text("Decline")
}
},
shape = RoundedCornerShape(24.dp)
)
}

private fun isAccessibilityServiceEnabled(context: Context, service: Class<*>): Boolean {
val enabledServices = Settings.Secure.getString(
context.contentResolver,
Expand Down
3 changes: 1 addition & 2 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<resources>
<string name="app_name">RecentApps</string>
<string name="fgs_rotation_reason">Overlay para controle de rotação em telas dobráveis</string>
<string name="accessibility_service_description">O CoverSpin precisa deste serviço para manter a rotação ativa e executar gestos na tela de capa.</string>
<string name="accessibility_service_description">RecentApps uses this service to detect user commands and quickly switch between recently opened applications.</string>
</resources>
1 change: 0 additions & 1 deletion app/src/main/res/xml/accessibility_service_config.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:description="@string/accessibility_service_description"
android:isAccessibilityTool="false"
android:accessibilityEventTypes="typeAllMask"
android:accessibilityFeedbackType="feedbackGeneric"
android:notificationTimeout="100"
Expand Down