diff --git a/app/src/main/java/dev/pranav/applock/AppLockApplication.kt b/app/src/main/java/dev/pranav/applock/AppLockApplication.kt
index 07fb0ed..360c06a 100644
--- a/app/src/main/java/dev/pranav/applock/AppLockApplication.kt
+++ b/app/src/main/java/dev/pranav/applock/AppLockApplication.kt
@@ -4,6 +4,7 @@ import android.app.Application
import android.content.Context
import android.os.Build
import android.util.Log
+import dev.pranav.applock.core.utils.LocaleManager
import dev.pranav.applock.core.utils.LogUtils
import dev.pranav.applock.data.repository.AppLockRepository
import org.lsposed.hiddenapibypass.HiddenApiBypass
@@ -16,7 +17,7 @@ class AppLockApplication : Application() {
private set
override fun attachBaseContext(base: Context?) {
- super.attachBaseContext(base)
+ super.attachBaseContext(LocaleManager.wrap(base ?: return))
initializeHiddenApiBypass()
}
diff --git a/app/src/main/java/dev/pranav/applock/MainActivity.kt b/app/src/main/java/dev/pranav/applock/MainActivity.kt
index 22d8d95..f9d0a66 100644
--- a/app/src/main/java/dev/pranav/applock/MainActivity.kt
+++ b/app/src/main/java/dev/pranav/applock/MainActivity.kt
@@ -1,5 +1,6 @@
package dev.pranav.applock
+import android.content.Context
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
@@ -10,12 +11,17 @@ import androidx.navigation.compose.rememberNavController
import dev.pranav.applock.core.navigation.AppNavHost
import dev.pranav.applock.core.navigation.NavigationManager
import dev.pranav.applock.core.navigation.Screen
+import dev.pranav.applock.core.utils.LocaleManager
import dev.pranav.applock.ui.theme.AppLockTheme
class MainActivity : FragmentActivity() {
private lateinit var navigationManager: NavigationManager
+ override fun attachBaseContext(newBase: Context?) {
+ super.attachBaseContext(LocaleManager.wrap(newBase ?: return))
+ }
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
diff --git a/app/src/main/java/dev/pranav/applock/core/navigation/AppNavigator.kt b/app/src/main/java/dev/pranav/applock/core/navigation/AppNavigator.kt
index 2b73fb3..27f8a94 100644
--- a/app/src/main/java/dev/pranav/applock/core/navigation/AppNavigator.kt
+++ b/app/src/main/java/dev/pranav/applock/core/navigation/AppNavigator.kt
@@ -1,5 +1,6 @@
package dev.pranav.applock.core.navigation
+import android.content.Context
import android.util.Log
import androidx.activity.compose.LocalActivity
import androidx.biometric.BiometricManager
@@ -9,6 +10,7 @@ import androidx.compose.animation.fadeIn
import androidx.compose.animation.scaleIn
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.stringResource
import androidx.core.content.ContextCompat
import androidx.fragment.app.FragmentActivity
import androidx.navigation.NavController
@@ -16,6 +18,7 @@ import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import dev.pranav.applock.AppLockApplication
+import dev.pranav.applock.R
import dev.pranav.applock.core.utils.LogUtils
import dev.pranav.applock.data.repository.PreferencesRepository
import dev.pranav.applock.features.antiuninstall.ui.AntiUninstallScreen
@@ -189,18 +192,18 @@ private fun handleBiometricAuthentication(
}
)
- val promptInfo = createBiometricPromptInfo()
+ val promptInfo = createBiometricPromptInfo(context)
biometricPrompt.authenticate(promptInfo)
} catch (e: Exception) {
Log.e(TAG, "Error during biometric authentication", e)
}
}
-private fun createBiometricPromptInfo(): BiometricPrompt.PromptInfo {
+private fun createBiometricPromptInfo(context: Context): BiometricPrompt.PromptInfo {
return BiometricPrompt.PromptInfo.Builder()
- .setTitle(BIOMETRIC_TITLE)
- .setSubtitle(BIOMETRIC_SUBTITLE)
- .setNegativeButtonText(BIOMETRIC_NEGATIVE_BUTTON)
+ .setTitle(context.getString(R.string.biometric_prompt_title))
+ .setSubtitle(context.getString(R.string.biometric_prompt_subtitle))
+ .setNegativeButtonText(context.getString(R.string.biometric_prompt_negative_button))
.setAllowedAuthenticators(
BiometricManager.Authenticators.BIOMETRIC_WEAK or
BiometricManager.Authenticators.BIOMETRIC_STRONG
@@ -226,6 +229,3 @@ private fun navigateToMain(navController: NavHostController) {
private const val TAG = "AppNavHost"
private const val ANIMATION_DURATION = 400
private const val SCALE_INITIAL = 0.9f
-private const val BIOMETRIC_TITLE = "Confirm password"
-private const val BIOMETRIC_SUBTITLE = "Confirm biometric to continue"
-private const val BIOMETRIC_NEGATIVE_BUTTON = "Use PIN"
diff --git a/app/src/main/java/dev/pranav/applock/core/utils/AppLockUtils.kt b/app/src/main/java/dev/pranav/applock/core/utils/AppLockUtils.kt
index 4f73ff6..29d47b2 100644
--- a/app/src/main/java/dev/pranav/applock/core/utils/AppLockUtils.kt
+++ b/app/src/main/java/dev/pranav/applock/core/utils/AppLockUtils.kt
@@ -11,6 +11,7 @@ import android.provider.Settings
import android.util.Log
import android.widget.Toast
import androidx.core.net.toUri
+import dev.pranav.applock.R
import dev.pranav.applock.AppLockApplication
import dev.pranav.applock.data.repository.AppLockRepository
@@ -53,7 +54,7 @@ fun launchBatterySettings(context: Context) {
if (requestIgnoreIntent.resolveActivity(pm) != null) {
context.startActivity(requestIgnoreIntent)
- showBatteryOptimizationToast(context, "Battery optimization request sent")
+ showBatteryOptimizationToast(context, context.getString(R.string.utils_battery_optimization_sent_toast))
} else {
Log.w(
TAG,
@@ -63,7 +64,7 @@ fun launchBatterySettings(context: Context) {
}
} catch (e: Exception) {
Log.e(TAG, "Failed to launch battery settings", e)
- showBatteryOptimizationToast(context, "Failed to open battery settings")
+ showBatteryOptimizationToast(context, context.getString(R.string.utils_battery_open_failed_toast))
}
}
@@ -92,7 +93,7 @@ private fun launchGeneralBatterySettings(context: Context) {
context.startActivity(generalBatteryIntent)
showBatteryOptimizationToast(
context,
- "Please find and configure this app in battery settings"
+ context.getString(R.string.utils_battery_configure_toast)
)
} catch (e: Exception) {
Log.e(TAG, "Failed to launch general battery settings", e)
diff --git a/app/src/main/java/dev/pranav/applock/core/utils/LocaleManager.kt b/app/src/main/java/dev/pranav/applock/core/utils/LocaleManager.kt
new file mode 100644
index 0000000..cfe88f5
--- /dev/null
+++ b/app/src/main/java/dev/pranav/applock/core/utils/LocaleManager.kt
@@ -0,0 +1,25 @@
+package dev.pranav.applock.core.utils
+
+import android.content.Context
+import android.content.res.Configuration
+import dev.pranav.applock.data.repository.PreferencesRepository
+import java.util.Locale
+
+object LocaleManager {
+
+ fun wrap(context: Context): Context {
+ val prefs = context.getSharedPreferences("app_lock_settings", Context.MODE_PRIVATE)
+ val language = prefs.getString(PreferencesRepository.KEY_LANGUAGE, PreferencesRepository.LANGUAGE_SYSTEM)
+ ?: PreferencesRepository.LANGUAGE_SYSTEM
+ if (language == PreferencesRepository.LANGUAGE_SYSTEM) {
+ return context
+ }
+
+ val locale = Locale.forLanguageTag(language)
+ Locale.setDefault(locale)
+
+ val config = Configuration(context.resources.configuration)
+ config.setLocale(locale)
+ return context.createConfigurationContext(config)
+ }
+}
diff --git a/app/src/main/java/dev/pranav/applock/data/repository/AppLockRepository.kt b/app/src/main/java/dev/pranav/applock/data/repository/AppLockRepository.kt
index 3e92870..dce1387 100644
--- a/app/src/main/java/dev/pranav/applock/data/repository/AppLockRepository.kt
+++ b/app/src/main/java/dev/pranav/applock/data/repository/AppLockRepository.kt
@@ -98,6 +98,9 @@ class AppLockRepository(private val context: Context) {
fun isLoggingEnabled(): Boolean = preferencesRepository.isLoggingEnabled()
fun setLoggingEnabled(enabled: Boolean) = preferencesRepository.setLoggingEnabled(enabled)
+ fun setLanguage(code: String) = preferencesRepository.setLanguage(code)
+ fun getLanguage(): String = preferencesRepository.getLanguage()
+
fun setActiveBackend(backend: BackendImplementation) =
backendServiceManager.setActiveBackend(backend)
diff --git a/app/src/main/java/dev/pranav/applock/data/repository/PreferencesRepository.kt b/app/src/main/java/dev/pranav/applock/data/repository/PreferencesRepository.kt
index 522d712..893b56b 100644
--- a/app/src/main/java/dev/pranav/applock/data/repository/PreferencesRepository.kt
+++ b/app/src/main/java/dev/pranav/applock/data/repository/PreferencesRepository.kt
@@ -170,6 +170,14 @@ class PreferencesRepository(context: Context) {
settingsPrefs.edit { putBoolean(KEY_LOGGING_ENABLED, enabled) }
}
+ fun setLanguage(code: String) {
+ settingsPrefs.edit { putString(KEY_LANGUAGE, code) }
+ }
+
+ fun getLanguage(): String {
+ return settingsPrefs.getString(KEY_LANGUAGE, LANGUAGE_SYSTEM) ?: LANGUAGE_SYSTEM
+ }
+
companion object {
private const val PREFS_NAME_APP_LOCK = "app_lock_prefs"
private const val PREFS_NAME_SETTINGS = "app_lock_settings"
@@ -197,5 +205,10 @@ class PreferencesRepository(context: Context) {
const val LOCK_TYPE_PIN = "pin"
const val LOCK_TYPE_PATTERN = "pattern"
const val LOCK_TYPE_PASSWORD = "password"
+
+ const val KEY_LANGUAGE = "app_language"
+ const val LANGUAGE_SYSTEM = "system"
+ const val LANGUAGE_ENGLISH = "en"
+ const val LANGUAGE_PORTUGUESE_BR = "pt-BR"
}
}
diff --git a/app/src/main/java/dev/pranav/applock/features/admin/AdminDisableActivity.kt b/app/src/main/java/dev/pranav/applock/features/admin/AdminDisableActivity.kt
index 16e9cc7..c5a4463 100644
--- a/app/src/main/java/dev/pranav/applock/features/admin/AdminDisableActivity.kt
+++ b/app/src/main/java/dev/pranav/applock/features/admin/AdminDisableActivity.kt
@@ -2,6 +2,7 @@ package dev.pranav.applock.features.admin
import android.app.admin.DevicePolicyManager
import android.content.ComponentName
+import android.content.Context
import android.os.Bundle
import android.widget.Toast
import androidx.activity.ComponentActivity
@@ -27,6 +28,7 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import dev.pranav.applock.R
import dev.pranav.applock.core.broadcast.DeviceAdmin
+import dev.pranav.applock.core.utils.LocaleManager
import dev.pranav.applock.core.utils.SecurityUtils
import dev.pranav.applock.core.utils.appLockRepository
import dev.pranav.applock.data.repository.AppLockRepository
@@ -41,6 +43,10 @@ class AdminDisableActivity : ComponentActivity() {
private lateinit var devicePolicyManager: DevicePolicyManager
private lateinit var deviceAdminComponentName: ComponentName
+ override fun attachBaseContext(newBase: Context?) {
+ super.attachBaseContext(LocaleManager.wrap(newBase ?: return))
+ }
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
devicePolicyManager = getSystemService(DEVICE_POLICY_SERVICE) as DevicePolicyManager
diff --git a/app/src/main/java/dev/pranav/applock/features/antiuninstall/ui/AntiUninstallScreen.kt b/app/src/main/java/dev/pranav/applock/features/antiuninstall/ui/AntiUninstallScreen.kt
index 37304f0..2816175 100644
--- a/app/src/main/java/dev/pranav/applock/features/antiuninstall/ui/AntiUninstallScreen.kt
+++ b/app/src/main/java/dev/pranav/applock/features/antiuninstall/ui/AntiUninstallScreen.kt
@@ -53,6 +53,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
@@ -63,6 +64,7 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
+import dev.pranav.applock.R
import dev.pranav.applock.core.utils.appLockRepository
import dev.pranav.applock.core.utils.blockUninstallForUser
import dev.pranav.applock.core.utils.unblockUninstallForUser
@@ -263,7 +265,7 @@ fun AntiUninstallScreen(
modifier = Modifier.fillMaxSize(),
topBar = {
SearchTopBar(
- title = "Anti-Uninstall Protection",
+ title = stringResource(R.string.anti_uninstall_screen_title),
searchQuery = searchQuery,
onSearchQueryChange = viewModel::updateSearchQuery,
onBack = { navController.navigateUp() },
@@ -283,7 +285,7 @@ fun AntiUninstallScreen(
) {
item {
Text(
- text = "Select apps that will be protected from uninstallation.",
+ text = stringResource(R.string.anti_uninstall_screen_desc),
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
@@ -310,7 +312,7 @@ fun AntiUninstallScreen(
if (protectedNotInList.isNotEmpty()) {
item {
Text(
- text = "Manually Added Packages",
+ text = stringResource(R.string.anti_uninstall_manual_packages_title),
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold,
modifier = Modifier.padding(bottom = 4.dp)
@@ -324,7 +326,7 @@ fun AntiUninstallScreen(
}
item {
Text(
- text = "Installed Apps",
+ text = stringResource(R.string.anti_uninstall_installed_apps_title),
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold,
modifier = Modifier.padding(top = 8.dp, bottom = 4.dp)
@@ -347,19 +349,19 @@ fun AntiUninstallScreen(
AlertDialog(
onDismissRequest = { showManualAddDialog.value = false },
properties = DialogProperties(usePlatformDefaultWidth = false),
- title = { Text("Add Package Manually") },
+ title = { Text(stringResource(R.string.anti_uninstall_manual_add_dialog_title)) },
text = {
Column(Modifier.fillMaxWidth(0.8f)) {
Text(
- text = "Enter the package name of the app you want to protect:",
+ text = stringResource(R.string.anti_uninstall_manual_add_dialog_text),
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier.padding(bottom = 16.dp)
)
OutlinedTextField(
value = manualPackageName,
onValueChange = viewModel::updateManualPackageName,
- label = { Text("Package Name") },
- placeholder = { Text("com.example.app") },
+ label = { Text(stringResource(R.string.anti_uninstall_package_name_label)) },
+ placeholder = { Text(stringResource(R.string.anti_uninstall_package_name_placeholder)) },
modifier = Modifier.fillMaxWidth(),
singleLine = true
)
@@ -372,10 +374,10 @@ fun AntiUninstallScreen(
showManualAddDialog.value = false
},
enabled = manualPackageName.isNotBlank()
- ) { Text("Add") }
+ ) { Text(stringResource(R.string.add_button)) }
},
dismissButton = {
- TextButton(onClick = { showManualAddDialog.value = false }) { Text("Cancel") }
+ TextButton(onClick = { showManualAddDialog.value = false }) { Text(stringResource(R.string.cancel_button)) }
}
)
}
@@ -391,10 +393,10 @@ fun AntiUninstallScreen(
title = {
Text(
text = when (shizukuState) {
- ShizukuState.NOT_INSTALLED -> "Shizuku Not Installed"
- ShizukuState.NOT_RUNNING -> "Shizuku Not Running"
- ShizukuState.PERMISSION_DENIED -> "Permission Required"
- else -> "Shizuku Setup"
+ ShizukuState.NOT_INSTALLED -> stringResource(R.string.anti_uninstall_shizuku_not_installed_title)
+ ShizukuState.NOT_RUNNING -> stringResource(R.string.anti_uninstall_shizuku_not_running_title)
+ ShizukuState.PERMISSION_DENIED -> stringResource(R.string.anti_uninstall_shizuku_permission_required_title)
+ else -> stringResource(R.string.anti_uninstall_shizuku_setup_title)
}
)
},
@@ -402,9 +404,9 @@ fun AntiUninstallScreen(
Column(Modifier.fillMaxWidth(0.8f)) {
Text(
text = when (shizukuState) {
- ShizukuState.NOT_INSTALLED -> "Shizuku is required to configure Anti-Uninstall protection without root.\n\nPlease install Shizuku from the Play Store or GitHub to continue."
- ShizukuState.NOT_RUNNING -> "Shizuku is installed, but the background service has not been started yet (Binder hasn't been received).\n\nPlease open the Shizuku application and start the service via Wireless Debugging or Root."
- ShizukuState.PERMISSION_DENIED -> "Shizuku is active, but this application needs your permission authorization to configure package restrictions.\n\nThankfully, Shizuku is only used while blocking/unblocking uninstalls, so it doesn't need to stay running forever!"
+ ShizukuState.NOT_INSTALLED -> stringResource(R.string.anti_uninstall_shizuku_not_installed_text)
+ ShizukuState.NOT_RUNNING -> stringResource(R.string.anti_uninstall_shizuku_not_running_text)
+ ShizukuState.PERMISSION_DENIED -> stringResource(R.string.anti_uninstall_shizuku_permission_required_text)
else -> ""
},
style = MaterialTheme.typography.bodyMedium
@@ -441,10 +443,10 @@ fun AntiUninstallScreen(
) {
Text(
text = when (shizukuState) {
- ShizukuState.NOT_INSTALLED -> "Install Shizuku"
- ShizukuState.NOT_RUNNING -> "Open Shizuku"
- ShizukuState.PERMISSION_DENIED -> "Grant Permission"
- else -> "Confirm"
+ ShizukuState.NOT_INSTALLED -> stringResource(R.string.anti_uninstall_install_shizuku_button)
+ ShizukuState.NOT_RUNNING -> stringResource(R.string.anti_uninstall_open_shizuku_button)
+ ShizukuState.PERMISSION_DENIED -> stringResource(R.string.grant_permission_button)
+ else -> stringResource(R.string.confirm_button)
}
)
}
@@ -452,7 +454,7 @@ fun AntiUninstallScreen(
dismissButton = {
TextButton(onClick = {
navController.popBackStack()
- }) { Text("Go Back") }
+ }) { Text(stringResource(R.string.go_back_button)) }
}
)
}
@@ -484,7 +486,7 @@ private fun SearchTopBar(
verticalAlignment = Alignment.CenterVertically
) {
IconButton(onClick = onBack) {
- Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back")
+ Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = stringResource(R.string.back_cd))
}
Text(
text = title,
@@ -492,7 +494,7 @@ private fun SearchTopBar(
modifier = Modifier.weight(1f)
)
IconButton(onClick = onAdd) {
- Icon(Icons.Default.Add, contentDescription = "Add package manually")
+ Icon(Icons.Default.Add, contentDescription = stringResource(R.string.anti_uninstall_add_manual_cd))
}
}
@@ -505,7 +507,7 @@ private fun SearchTopBar(
OutlinedTextField(
value = searchQuery,
onValueChange = onSearchQueryChange,
- placeholder = { Text("Search apps or package names...") },
+ placeholder = { Text(stringResource(R.string.anti_uninstall_search_placeholder)) },
leadingIcon = { Icon(Icons.Default.Search, contentDescription = null) },
modifier = Modifier.fillMaxWidth(),
singleLine = true,
@@ -593,7 +595,7 @@ private fun ManualPackageItem(packageName: String, onToggle: () -> Unit) {
overflow = TextOverflow.Ellipsis
)
Text(
- text = "Manually added package",
+ text = stringResource(R.string.anti_uninstall_manually_added_label),
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
diff --git a/app/src/main/java/dev/pranav/applock/features/appintro/ui/AppIntroScreen.kt b/app/src/main/java/dev/pranav/applock/features/appintro/ui/AppIntroScreen.kt
index 7713a14..75adfdf 100644
--- a/app/src/main/java/dev/pranav/applock/features/appintro/ui/AppIntroScreen.kt
+++ b/app/src/main/java/dev/pranav/applock/features/appintro/ui/AppIntroScreen.kt
@@ -503,6 +503,7 @@ fun AppIntroScreen(navController: NavController) {
showSkipButton = false,
useAnimatedPager = true,
nextButtonText = stringResource(R.string.next_button),
+ backButtonText = stringResource(R.string.back_button),
finishButtonText = stringResource(R.string.get_started_button)
)
}
diff --git a/app/src/main/java/dev/pranav/applock/features/applist/ui/MainScreen.kt b/app/src/main/java/dev/pranav/applock/features/applist/ui/MainScreen.kt
index 9e9cf81..8a652ef 100644
--- a/app/src/main/java/dev/pranav/applock/features/applist/ui/MainScreen.kt
+++ b/app/src/main/java/dev/pranav/applock/features/applist/ui/MainScreen.kt
@@ -183,7 +183,7 @@ fun MainScreen(
)
Spacer(Modifier.width(6.dp))
Text(
- text = if (applockEnabled) "ON" else "OFF",
+ text = if (applockEnabled) stringResource(R.string.main_screen_protection_on) else stringResource(R.string.main_screen_protection_off),
style = MaterialTheme.typography.labelLarge,
color = if (applockEnabled) MaterialTheme.colorScheme.onPrimaryContainer else MaterialTheme.colorScheme.onSurfaceVariant
)
@@ -211,7 +211,7 @@ fun MainScreen(
) {
Icon(
imageVector = Icons.Default.Block,
- contentDescription = "Trigger exclusions",
+ contentDescription = stringResource(R.string.main_screen_trigger_exclusions_cd),
tint = MaterialTheme.colorScheme.onSurface
)
}
@@ -224,7 +224,7 @@ fun MainScreen(
) {
Icon(
imageVector = Icons.Outlined.Lock,
- contentDescription = "Anti-Uninstall",
+ contentDescription = stringResource(R.string.main_screen_anti_uninstall_cd),
tint = MaterialTheme.colorScheme.onSurface
)
}
@@ -244,7 +244,7 @@ fun MainScreen(
) {
Icon(
imageVector = Icons.Default.Add,
- contentDescription = stringResource(R.string.main_screen_search_cd) // Update ideally
+ contentDescription = stringResource(R.string.main_screen_add_button_cd)
)
}
}
@@ -432,13 +432,13 @@ private fun EmptyDashboardState(modifier: Modifier = Modifier) {
)
Spacer(modifier = Modifier.height(24.dp))
Text(
- text = "No Protected Apps",
+ text = stringResource(R.string.main_screen_empty_title),
style = MaterialTheme.typography.headlineSmall,
color = MaterialTheme.colorScheme.onSurface
)
Spacer(modifier = Modifier.height(8.dp))
Text(
- text = "Tap the + button to selectively secure your apps.",
+ text = stringResource(R.string.main_screen_empty_desc),
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
textAlign = androidx.compose.ui.text.style.TextAlign.Center,
@@ -496,7 +496,7 @@ private fun AddProtectedAppsSheetContent(
verticalAlignment = Alignment.CenterVertically
) {
Text(
- text = "Select Apps",
+ text = stringResource(R.string.main_screen_select_apps_title),
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Bold
)
@@ -504,7 +504,7 @@ private fun AddProtectedAppsSheetContent(
onClick = onSave,
enabled = selectedPackages.isNotEmpty()
) {
- Text("Protect (${selectedPackages.size})")
+ Text(stringResource(R.string.main_screen_protect_count_button, selectedPackages.size))
}
}
@@ -593,7 +593,7 @@ private fun ProtectedAppItem(
},
supportingContent = {
Text(
- text = "Protected",
+ text = stringResource(R.string.main_screen_protected_label),
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.primary,
maxLines = 1,
@@ -624,7 +624,7 @@ private fun ProtectedAppItem(
IconButton(onClick = onUnlock) {
Icon(
imageVector = Icons.Outlined.LockOpen,
- contentDescription = "Unlock ${appName ?: "app"}",
+ contentDescription = stringResource(R.string.main_screen_unlock_app_cd, appName ?: "app"),
tint = MaterialTheme.colorScheme.onSurfaceVariant
)
}
@@ -811,7 +811,7 @@ private fun PermissionWarningBanner(
) {
Icon(
imageVector = Icons.Rounded.Warning,
- contentDescription = "Warning",
+ contentDescription = stringResource(R.string.main_screen_warning_cd),
tint = MaterialTheme.colorScheme.primary,
modifier = Modifier.size(28.dp)
)
diff --git a/app/src/main/java/dev/pranav/applock/features/lockscreen/ui/AlphanumericPasswordOverlayScreen.kt b/app/src/main/java/dev/pranav/applock/features/lockscreen/ui/AlphanumericPasswordOverlayScreen.kt
index d125de1..32876a8 100644
--- a/app/src/main/java/dev/pranav/applock/features/lockscreen/ui/AlphanumericPasswordOverlayScreen.kt
+++ b/app/src/main/java/dev/pranav/applock/features/lockscreen/ui/AlphanumericPasswordOverlayScreen.kt
@@ -94,7 +94,7 @@ fun AlphanumericPasswordOverlayScreen(
) {
Icon(
imageVector = Icons.Default.Close,
- contentDescription = "Close",
+ contentDescription = stringResource(R.string.alphanumeric_password_overlay_close_cd),
tint = MaterialTheme.colorScheme.onSurface
)
}
@@ -111,7 +111,7 @@ fun AlphanumericPasswordOverlayScreen(
) {
Text(
text = if (!fromMainActivity && !lockedAppName.isNullOrEmpty())
- "Continue to $lockedAppName"
+ stringResource(R.string.alphanumeric_overlay_continue_to_app, lockedAppName ?: "")
else
stringResource(R.string.enter_password_to_continue),
style = MaterialTheme.typography.headlineMediumEmphasized,
diff --git a/app/src/main/java/dev/pranav/applock/features/lockscreen/ui/LockScreenOverlayManager.kt b/app/src/main/java/dev/pranav/applock/features/lockscreen/ui/LockScreenOverlayManager.kt
index ee2c228..fe9c7d1 100644
--- a/app/src/main/java/dev/pranav/applock/features/lockscreen/ui/LockScreenOverlayManager.kt
+++ b/app/src/main/java/dev/pranav/applock/features/lockscreen/ui/LockScreenOverlayManager.kt
@@ -26,6 +26,8 @@ import androidx.savedstate.SavedStateRegistry
import androidx.savedstate.SavedStateRegistryController
import androidx.savedstate.SavedStateRegistryOwner
import androidx.savedstate.setViewTreeSavedStateRegistryOwner
+import dev.pranav.applock.R
+import dev.pranav.applock.core.utils.LocaleManager
import dev.pranav.applock.core.utils.appLockRepository
import dev.pranav.applock.data.repository.PreferencesRepository
import dev.pranav.applock.services.AppLockManager
@@ -66,7 +68,7 @@ class LockScreenOverlayManager(private val context: Context):
isStateRestored = true
}
- composeView = ComposeView(context).apply {
+ composeView = ComposeView(LocaleManager.wrap(context)).apply {
setViewTreeLifecycleOwner(this@LockScreenOverlayManager)
setViewTreeSavedStateRegistryOwner(this@LockScreenOverlayManager)
setViewTreeViewModelStoreOwner(this@LockScreenOverlayManager)
@@ -86,7 +88,7 @@ class LockScreenOverlayManager(private val context: Context):
pm.getApplicationLabel(pm.getApplicationInfo(lockedPackageName, 0))
.toString()
} catch (_: Exception) {
- "App"
+ context.getString(R.string.lock_screen_overlay_default_app_name)
}
val onPinAttemptCallback = { pin: String ->
diff --git a/app/src/main/java/dev/pranav/applock/features/lockscreen/ui/PasswordOverlayScreen.kt b/app/src/main/java/dev/pranav/applock/features/lockscreen/ui/PasswordOverlayScreen.kt
index c485bd3..2314ab2 100644
--- a/app/src/main/java/dev/pranav/applock/features/lockscreen/ui/PasswordOverlayScreen.kt
+++ b/app/src/main/java/dev/pranav/applock/features/lockscreen/ui/PasswordOverlayScreen.kt
@@ -1,6 +1,5 @@
package dev.pranav.applock.features.lockscreen.ui
-import dev.pranav.applock.features.lockscreen.ui.AlphanumericPasswordOverlayScreen
import android.content.Context
import android.content.res.Configuration
import android.os.Build
@@ -47,6 +46,7 @@ import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.lifecycleScope
import dev.pranav.applock.R
import dev.pranav.applock.core.ui.shapes
+import dev.pranav.applock.core.utils.LocaleManager
import dev.pranav.applock.core.utils.appLockRepository
import dev.pranav.applock.core.utils.vibrate
import dev.pranav.applock.data.repository.AppLockRepository
@@ -72,6 +72,10 @@ class PasswordOverlayActivity: FragmentActivity() {
private val TAG = "PasswordOverlayActivity"
+ override fun attachBaseContext(newBase: Context?) {
+ super.attachBaseContext(LocaleManager.wrap(newBase ?: return))
+ }
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -388,7 +392,7 @@ fun PinPasswordOverlayScreen(
) {
Icon(
imageVector = Icons.Default.Close,
- contentDescription = "Close",
+ contentDescription = stringResource(R.string.password_overlay_close_cd),
tint = MaterialTheme.colorScheme.onSurface
)
}
@@ -412,15 +416,15 @@ fun PinPasswordOverlayScreen(
.padding(end = 32.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
- ) {
- Text(
- text = if (!fromMainActivity && !lockedAppName.isNullOrEmpty())
- "Continue to $lockedAppName"
- else
- stringResource(R.string.enter_password_to_continue),
- style = MaterialTheme.typography.titleLarge,
- textAlign = TextAlign.Center
- )
+ ) {
+ Text(
+ text = if (!fromMainActivity && !lockedAppName.isNullOrEmpty())
+ stringResource(R.string.password_overlay_continue_to_app, lockedAppName)
+ else
+ stringResource(R.string.enter_password_to_continue),
+ style = MaterialTheme.typography.titleLarge,
+ textAlign = TextAlign.Center
+ )
// if (!fromMainActivity && !triggeringPackageName.isNullOrEmpty()) {
// Spacer(modifier = Modifier.height(8.dp))
@@ -485,7 +489,7 @@ fun PinPasswordOverlayScreen(
Text(
text = if (!fromMainActivity && !lockedAppName.isNullOrEmpty())
- "Continue to $lockedAppName"
+ stringResource(R.string.password_overlay_continue_to_app, lockedAppName)
else
stringResource(R.string.enter_password_to_continue),
style = if (!fromMainActivity && !lockedAppName.isNullOrEmpty())
diff --git a/app/src/main/java/dev/pranav/applock/features/lockscreen/ui/PatternLockScreen.kt b/app/src/main/java/dev/pranav/applock/features/lockscreen/ui/PatternLockScreen.kt
index aae4483..0f1e6da 100644
--- a/app/src/main/java/dev/pranav/applock/features/lockscreen/ui/PatternLockScreen.kt
+++ b/app/src/main/java/dev/pranav/applock/features/lockscreen/ui/PatternLockScreen.kt
@@ -111,7 +111,7 @@ fun PatternLockScreen(
) {
Text(
text = if (!fromMainActivity && !lockedAppName.isNullOrEmpty())
- "Continue to $lockedAppName"
+ stringResource(R.string.pattern_lock_continue_to_app, lockedAppName)
else
stringResource(R.string.enter_pattern_to_continue),
style = MaterialTheme.typography.titleMedium,
@@ -183,7 +183,7 @@ fun PatternLockScreen(
) {
Text(
text = if (!fromMainActivity && !lockedAppName.isNullOrEmpty())
- "Continue to $lockedAppName"
+ stringResource(R.string.pattern_lock_continue_to_app, lockedAppName)
else
stringResource(R.string.enter_pattern_to_continue),
style = MaterialTheme.typography.headlineMedium,
@@ -252,7 +252,7 @@ fun PatternLockScreen(
) {
Icon(
imageVector = Icons.Default.Close,
- contentDescription = "Close",
+ contentDescription = stringResource(R.string.pattern_lock_close_cd),
tint = MaterialTheme.colorScheme.onSurface
)
}
diff --git a/app/src/main/java/dev/pranav/applock/features/lockscreen/ui/TransparentBiometricActivity.kt b/app/src/main/java/dev/pranav/applock/features/lockscreen/ui/TransparentBiometricActivity.kt
index 9231384..1698df3 100644
--- a/app/src/main/java/dev/pranav/applock/features/lockscreen/ui/TransparentBiometricActivity.kt
+++ b/app/src/main/java/dev/pranav/applock/features/lockscreen/ui/TransparentBiometricActivity.kt
@@ -1,5 +1,6 @@
package dev.pranav.applock.features.lockscreen.ui
+import android.content.Context
import android.os.Bundle
import android.util.Log
import androidx.activity.compose.setContent
@@ -12,6 +13,7 @@ import androidx.compose.ui.Modifier
import androidx.core.content.ContextCompat
import androidx.fragment.app.FragmentActivity
import dev.pranav.applock.R
+import dev.pranav.applock.core.utils.LocaleManager
import dev.pranav.applock.services.AppLockManager
import dev.pranav.applock.ui.theme.AppLockTheme
@@ -19,6 +21,10 @@ class TransparentBiometricActivity: FragmentActivity() {
private val TAG = "TransparentBiometric"
private var lockedPackageName: String? = null
+ override fun attachBaseContext(newBase: Context?) {
+ super.attachBaseContext(LocaleManager.wrap(newBase ?: return))
+ }
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lockedPackageName = intent.getStringExtra("locked_package")
diff --git a/app/src/main/java/dev/pranav/applock/features/setpassword/ui/PatternSetPasswordScreen.kt b/app/src/main/java/dev/pranav/applock/features/setpassword/ui/PatternSetPasswordScreen.kt
index 450291d..8b65c24 100644
--- a/app/src/main/java/dev/pranav/applock/features/setpassword/ui/PatternSetPasswordScreen.kt
+++ b/app/src/main/java/dev/pranav/applock/features/setpassword/ui/PatternSetPasswordScreen.kt
@@ -213,9 +213,9 @@ fun PatternSetPasswordScreen(
) {
Text(
text = when {
- isVerifyOldPasswordMode -> "Enter current Pattern"
- isConfirmationMode -> "Confirm pattern"
- else -> "Create a pattern"
+ isVerifyOldPasswordMode -> stringResource(R.string.pattern_set_password_enter_current_label)
+ isConfirmationMode -> stringResource(R.string.pattern_set_password_confirm_label)
+ else -> stringResource(R.string.pattern_set_password_create_label)
},
style = MaterialTheme.typography.titleMedium,
textAlign = TextAlign.Center
@@ -225,7 +225,7 @@ fun PatternSetPasswordScreen(
if (showMismatchError) {
Text(
- text = "Incorrect Pattern",
+ text = stringResource(R.string.pattern_set_password_incorrect_error),
color = MaterialTheme.colorScheme.error,
style = MaterialTheme.typography.bodySmall,
textAlign = TextAlign.Center
@@ -233,7 +233,7 @@ fun PatternSetPasswordScreen(
}
if (showMinLengthError) {
Text(
- text = "Patter length should be at least 4",
+ text = stringResource(R.string.pattern_set_password_min_length_error),
color = MaterialTheme.colorScheme.error,
style = MaterialTheme.typography.bodySmall,
textAlign = TextAlign.Center
@@ -241,7 +241,7 @@ fun PatternSetPasswordScreen(
}
if (showInvalidOldPasswordError) {
Text(
- text = "Incorrect pattern",
+ text = stringResource(R.string.pattern_set_password_incorrect_short_error),
color = MaterialTheme.colorScheme.error,
style = MaterialTheme.typography.bodySmall,
textAlign = TextAlign.Center
@@ -379,7 +379,7 @@ fun PatternSetPasswordScreen(
if (showMinLengthError) {
Spacer(modifier = Modifier.height(8.dp))
Text(
- text = "Patter length should be at least 4",
+ text = stringResource(R.string.pattern_set_password_min_length_error),
color = MaterialTheme.colorScheme.error,
style = MaterialTheme.typography.labelLarge,
textAlign = TextAlign.Center
@@ -389,7 +389,7 @@ fun PatternSetPasswordScreen(
if (showInvalidOldPasswordError) {
Spacer(modifier = Modifier.height(8.dp))
Text(
- text = "Incorrect pattern",
+ text = stringResource(R.string.pattern_set_password_incorrect_short_error),
color = MaterialTheme.colorScheme.error,
style = MaterialTheme.typography.labelLarge,
textAlign = TextAlign.Center
diff --git a/app/src/main/java/dev/pranav/applock/features/settings/ui/SettingsScreen.kt b/app/src/main/java/dev/pranav/applock/features/settings/ui/SettingsScreen.kt
index 7438a6a..e899a0f 100644
--- a/app/src/main/java/dev/pranav/applock/features/settings/ui/SettingsScreen.kt
+++ b/app/src/main/java/dev/pranav/applock/features/settings/ui/SettingsScreen.kt
@@ -6,6 +6,7 @@ import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.widget.Toast
+import androidx.activity.compose.LocalActivity
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.biometric.BiometricManager
@@ -37,6 +38,7 @@ import androidx.compose.ui.unit.dp
import androidx.core.net.toUri
import androidx.navigation.NavController
import dev.pranav.applock.R
+import dev.pranav.applock.core.broadcast.AppLockServiceStarter
import dev.pranav.applock.core.broadcast.DeviceAdmin
import dev.pranav.applock.core.navigation.Screen
import dev.pranav.applock.core.utils.LogUtils
@@ -45,15 +47,21 @@ import dev.pranav.applock.core.utils.isAccessibilityServiceEnabled
import dev.pranav.applock.core.utils.openAccessibilitySettings
import dev.pranav.applock.data.repository.AppLockRepository
import dev.pranav.applock.data.repository.BackendImplementation
+import dev.pranav.applock.data.repository.PreferencesRepository
import dev.pranav.applock.features.admin.AdminDisableActivity
-import dev.pranav.applock.services.ShizukuAppLockService
-import dev.pranav.applock.services.UsageLockService
+import dev.pranav.applock.services.AppLockManager
import dev.pranav.applock.ui.components.DonateButton
import dev.pranav.applock.ui.icons.*
import rikka.shizuku.Shizuku
import rikka.shizuku.ShizukuProvider
import kotlin.math.abs
+private val languageOptions = listOf(
+ PreferencesRepository.LANGUAGE_SYSTEM to R.string.settings_screen_language_system,
+ PreferencesRepository.LANGUAGE_ENGLISH to R.string.settings_screen_language_english,
+ PreferencesRepository.LANGUAGE_PORTUGUESE_BR to R.string.settings_screen_language_portuguese_brazil
+)
+
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SettingsScreen(
@@ -61,6 +69,7 @@ fun SettingsScreen(
) {
val context = LocalContext.current
val appLockRepository = remember { AppLockRepository(context) }
+ val activity = LocalActivity.current
var showDialog by remember { mutableStateOf(false) }
var showUnlockTimeDialog by remember { mutableStateOf(false) }
@@ -94,6 +103,8 @@ fun SettingsScreen(
var showPermissionDialog by remember { mutableStateOf(false) }
var showDeviceAdminDialog by remember { mutableStateOf(false) }
var showAccessibilityDialog by remember { mutableStateOf(false) }
+ var showLanguageDialog by remember { mutableStateOf(false) }
+ var selectedLanguage by remember { mutableStateOf(appLockRepository.getLanguage()) }
val biometricManager = remember { BiometricManager.from(context) }
val isBiometricAvailable = remember {
@@ -188,6 +199,52 @@ fun SettingsScreen(
)
}
+ if (showLanguageDialog) {
+ val languages = languageOptions
+ AlertDialog(
+ onDismissRequest = { showLanguageDialog = false },
+ title = { Text(stringResource(R.string.settings_screen_language_title)) },
+ text = {
+ Column {
+ languages.forEach { (code, labelRes) ->
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .clickable { selectedLanguage = code }
+ .padding(vertical = 8.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ RadioButton(
+ selected = selectedLanguage == code,
+ onClick = { selectedLanguage = code }
+ )
+ Text(
+ text = stringResource(labelRes),
+ modifier = Modifier.padding(start = 8.dp)
+ )
+ }
+ }
+ }
+ },
+ confirmButton = {
+ TextButton(onClick = {
+ appLockRepository.setLanguage(selectedLanguage)
+ AppLockManager.stopAllServices(context)
+ AppLockServiceStarter.startAppropriateServices(context, appLockRepository)
+ showLanguageDialog = false
+ activity?.recreate()
+ }) {
+ Text(stringResource(R.string.confirm_button))
+ }
+ },
+ dismissButton = {
+ TextButton(onClick = { showLanguageDialog = false }) {
+ Text(stringResource(R.string.cancel_button))
+ }
+ }
+ )
+ }
+
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
Scaffold(
@@ -232,7 +289,7 @@ fun SettingsScreen(
null
}
}
- val versionName = packageInfo?.versionName ?: "Unknown"
+ val versionName = packageInfo?.versionName ?: stringResource(R.string.settings_screen_version_unknown)
Text(
text = stringResource(R.string.settings_screen_version_template, versionName),
style = MaterialTheme.typography.bodyMedium,
@@ -245,6 +302,26 @@ fun SettingsScreen(
DonateButton()
}
+ item {
+ SectionTitle(text = stringResource(R.string.settings_screen_general_title))
+ }
+
+ item {
+ SettingsGroup(
+ items = listOf(
+ ActionSettingItem(
+ icon = Icons.Default.Language,
+ title = stringResource(R.string.settings_screen_language_title),
+ subtitle = stringResource(
+ languageOptions.firstOrNull { it.first == selectedLanguage }?.second
+ ?: R.string.settings_screen_language_system
+ ),
+ onClick = { showLanguageDialog = true }
+ )
+ )
+ )
+ }
+
item {
SectionTitle(text = stringResource(R.string.settings_screen_lock_screen_customization_title))
}
@@ -320,7 +397,7 @@ fun SettingsScreen(
icon = Timer,
title = stringResource(R.string.settings_screen_unlock_duration_title),
subtitle = if (unlockTimeDuration > 0) {
- if (unlockTimeDuration > 10_000) "Until screen off"
+ if (unlockTimeDuration > 10_000) stringResource(R.string.settings_screen_unlock_duration_until_screen_off)
else stringResource(
R.string.settings_screen_unlock_duration_summary_minutes,
unlockTimeDuration
@@ -388,7 +465,7 @@ fun SettingsScreen(
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
context.startActivity(
- Intent.createChooser(shareIntent, "Share audit logs")
+ Intent.createChooser(shareIntent, context.getString(R.string.settings_screen_share_audit_logs_title))
)
} else {
Toast.makeText(
@@ -412,7 +489,7 @@ fun SettingsScreen(
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
context.startActivity(
- Intent.createChooser(shareIntent, "Share logs")
+ Intent.createChooser(shareIntent, context.getString(R.string.settings_screen_share_logs_title))
)
} else {
Toast.makeText(
@@ -425,8 +502,8 @@ fun SettingsScreen(
),
ToggleSettingItem(
icon = Icons.Default.Troubleshoot,
- title = "Logging",
- subtitle = "Enable debug logging for troubleshooting",
+ title = stringResource(R.string.settings_screen_logging_title),
+ subtitle = stringResource(R.string.settings_screen_logging_desc),
checked = loggingEnabled,
enabled = true,
onCheckedChange = { isChecked ->
@@ -725,7 +802,7 @@ fun UnlockTimeDurationDialog(
duration
)
60 -> stringResource(R.string.settings_screen_unlock_duration_dialog_option_hour)
- Integer.MAX_VALUE -> "Until Screen Off"
+ Integer.MAX_VALUE -> stringResource(R.string.settings_screen_unlock_duration_dialog_option_until_screen_off)
else -> stringResource(
R.string.settings_screen_unlock_duration_summary_minutes,
duration
@@ -771,6 +848,7 @@ fun BackendSelectionCard(
backend = backend,
isSelected = selectedBackend == backend,
onClick = {
+ if (selectedBackend == backend) return@BackendSelectionItem
when (backend) {
BackendImplementation.SHIZUKU -> {
if (!Shizuku.pingBinder() || Shizuku.checkSelfPermission() == PackageManager.PERMISSION_DENIED) {
@@ -790,9 +868,6 @@ fun BackendSelectionCard(
appLockRepository.setBackendImplementation(
BackendImplementation.SHIZUKU
)
- context.startService(
- Intent(context, ShizukuAppLockService::class.java)
- )
}
}
BackendImplementation.USAGE_STATS -> {
@@ -810,9 +885,6 @@ fun BackendSelectionCard(
}
selectedBackend = backend
appLockRepository.setBackendImplementation(BackendImplementation.USAGE_STATS)
- context.startService(
- Intent(context, UsageLockService::class.java)
- )
}
BackendImplementation.ACCESSIBILITY -> {
if (!context.isAccessibilityServiceEnabled()) {
@@ -823,6 +895,8 @@ fun BackendSelectionCard(
appLockRepository.setBackendImplementation(BackendImplementation.ACCESSIBILITY)
}
}
+ AppLockManager.stopAllServices(context)
+ AppLockServiceStarter.startAppropriateServices(context, appLockRepository)
}
)
}
@@ -904,19 +978,21 @@ fun BackendSelectionItem(
)
}
+@Composable
private fun getBackendDisplayName(backend: BackendImplementation): String {
return when (backend) {
- BackendImplementation.ACCESSIBILITY -> "Accessibility Service"
- BackendImplementation.USAGE_STATS -> "Usage Statistics"
- BackendImplementation.SHIZUKU -> "Shizuku Service"
+ BackendImplementation.ACCESSIBILITY -> stringResource(R.string.settings_screen_backend_accessibility_name)
+ BackendImplementation.USAGE_STATS -> stringResource(R.string.settings_screen_backend_usage_stats_name)
+ BackendImplementation.SHIZUKU -> stringResource(R.string.settings_screen_backend_shizuku_name)
}
}
+@Composable
private fun getBackendDescription(backend: BackendImplementation): String {
return when (backend) {
- BackendImplementation.ACCESSIBILITY -> "Standard method that works on most devices"
- BackendImplementation.USAGE_STATS -> "Experimental method using app usage statistics"
- BackendImplementation.SHIZUKU -> "Advanced method using Shizuku and internal APIs"
+ BackendImplementation.ACCESSIBILITY -> stringResource(R.string.settings_screen_backend_accessibility_desc_short)
+ BackendImplementation.USAGE_STATS -> stringResource(R.string.settings_screen_backend_usage_stats_desc_short)
+ BackendImplementation.SHIZUKU -> stringResource(R.string.settings_screen_backend_shizuku_desc_short)
}
}
@@ -1015,12 +1091,12 @@ fun LinksSection() {
val context = LocalContext.current
Column {
- SectionTitle(text = "Links")
+ SectionTitle(text = stringResource(R.string.settings_screen_links_section_title))
Column {
SettingsCard(index = 0, listSize = 3) {
LinkItem(
- title = "Discord Community",
+ title = stringResource(R.string.settings_screen_link_discord),
icon = Discord,
onClick = {
val intent = Intent(
@@ -1034,7 +1110,7 @@ fun LinksSection() {
SettingsCard(index = 1, listSize = 3) {
LinkItem(
- title = "Source Code",
+ title = stringResource(R.string.settings_screen_link_source_code),
icon = Icons.Outlined.Code,
onClick = {
val intent = Intent(
@@ -1048,7 +1124,7 @@ fun LinksSection() {
SettingsCard(index = 2, listSize = 3) {
LinkItem(
- title = "Report Issue",
+ title = stringResource(R.string.settings_screen_link_report_issue),
icon = Icons.Outlined.BugReport,
onClick = {
val intent = Intent(
diff --git a/app/src/main/java/dev/pranav/applock/features/triggerexclusions/ui/TriggerExclusionsScreen.kt b/app/src/main/java/dev/pranav/applock/features/triggerexclusions/ui/TriggerExclusionsScreen.kt
index 75f6a5f..b44b5f7 100644
--- a/app/src/main/java/dev/pranav/applock/features/triggerexclusions/ui/TriggerExclusionsScreen.kt
+++ b/app/src/main/java/dev/pranav/applock/features/triggerexclusions/ui/TriggerExclusionsScreen.kt
@@ -17,12 +17,14 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.core.graphics.drawable.toBitmap
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
+import dev.pranav.applock.R
import dev.pranav.applock.features.applist.domain.AppInfo
@OptIn(ExperimentalMaterial3Api::class)
@@ -50,7 +52,7 @@ fun TriggerExclusionsScreen(
modifier = Modifier.fillMaxSize(),
topBar = {
SearchTopBar(
- title = "Trigger Exclusions",
+ title = stringResource(R.string.trigger_exclusions_screen_title),
searchQuery = searchQuery,
onSearchQueryChange = viewModel::updateSearchQuery,
onBack = { navController.navigateUp() },
@@ -70,7 +72,7 @@ fun TriggerExclusionsScreen(
) {
item {
Text(
- text = "Select apps that will NOT trigger locks when switching to locked apps.",
+ text = stringResource(R.string.trigger_exclusions_screen_desc),
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
@@ -94,7 +96,7 @@ fun TriggerExclusionsScreen(
if (excludedNotInList.isNotEmpty()) {
item {
Text(
- text = "Manually Added Packages",
+ text = stringResource(R.string.trigger_exclusions_manual_packages_title),
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold,
modifier = Modifier.padding(bottom = 4.dp)
@@ -108,7 +110,7 @@ fun TriggerExclusionsScreen(
}
item {
Text(
- text = "Installed Apps",
+ text = stringResource(R.string.trigger_exclusions_installed_apps_title),
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold,
modifier = Modifier.padding(top = 8.dp, bottom = 4.dp)
@@ -130,19 +132,19 @@ fun TriggerExclusionsScreen(
if (showManualAddDialog.value) {
AlertDialog(
onDismissRequest = { showManualAddDialog.value = false },
- title = { Text("Add Package Manually") },
+ title = { Text(stringResource(R.string.trigger_exclusions_manual_add_dialog_title)) },
text = {
Column {
Text(
- text = "Enter the package name of the app you want to exclude:",
+ text = stringResource(R.string.trigger_exclusions_manual_add_dialog_text),
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier.padding(bottom = 16.dp)
)
OutlinedTextField(
value = manualPackageName,
onValueChange = viewModel::updateManualPackageName,
- label = { Text("Package Name") },
- placeholder = { Text("com.example.app") },
+ label = { Text(stringResource(R.string.trigger_exclusions_package_name_label)) },
+ placeholder = { Text(stringResource(R.string.trigger_exclusions_package_name_placeholder)) },
modifier = Modifier.fillMaxWidth(),
singleLine = true
)
@@ -155,10 +157,10 @@ fun TriggerExclusionsScreen(
showManualAddDialog.value = false
},
enabled = manualPackageName.isNotBlank()
- ) { Text("Add") }
+ ) { Text(stringResource(R.string.add_button)) }
},
dismissButton = {
- TextButton(onClick = { showManualAddDialog.value = false }) { Text("Cancel") }
+ TextButton(onClick = { showManualAddDialog.value = false }) { Text(stringResource(R.string.cancel_button)) }
}
)
}
@@ -190,7 +192,7 @@ private fun SearchTopBar(
verticalAlignment = Alignment.CenterVertically
) {
IconButton(onClick = onBack) {
- Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back")
+ Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = stringResource(R.string.back_cd))
}
Text(
text = title,
@@ -198,7 +200,7 @@ private fun SearchTopBar(
modifier = Modifier.weight(1f)
)
IconButton(onClick = onAdd) {
- Icon(Icons.Default.Add, contentDescription = "Add package manually")
+ Icon(Icons.Default.Add, contentDescription = stringResource(R.string.trigger_exclusions_add_manual_cd))
}
}
@@ -211,7 +213,7 @@ private fun SearchTopBar(
OutlinedTextField(
value = searchQuery,
onValueChange = onSearchQueryChange,
- placeholder = { Text("Search apps or package names...") },
+ placeholder = { Text(stringResource(R.string.trigger_exclusions_search_placeholder)) },
leadingIcon = { Icon(Icons.Default.Search, contentDescription = null) },
modifier = Modifier.fillMaxWidth(),
singleLine = true,
@@ -294,7 +296,7 @@ private fun ManualPackageItem(packageName: String, onToggle: () -> Unit) {
overflow = TextOverflow.Ellipsis
)
Text(
- text = "Manually added package",
+ text = stringResource(R.string.trigger_exclusions_manually_added_label),
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
diff --git a/app/src/main/java/dev/pranav/applock/services/AppLockAccessibilityService.kt b/app/src/main/java/dev/pranav/applock/services/AppLockAccessibilityService.kt
index dad4b8a..682f75a 100644
--- a/app/src/main/java/dev/pranav/applock/services/AppLockAccessibilityService.kt
+++ b/app/src/main/java/dev/pranav/applock/services/AppLockAccessibilityService.kt
@@ -16,7 +16,9 @@ import android.view.accessibility.AccessibilityNodeInfo
import android.view.inputmethod.InputMethodManager
import android.widget.Toast
import androidx.core.content.getSystemService
+import dev.pranav.applock.R
import dev.pranav.applock.core.broadcast.DeviceAdmin
+import dev.pranav.applock.core.utils.LocaleManager
import dev.pranav.applock.core.utils.LogUtils
import dev.pranav.applock.core.utils.appLockRepository
import dev.pranav.applock.core.utils.enableAccessibilityServiceWithShizuku
@@ -38,6 +40,10 @@ class AppLockAccessibilityService : AccessibilityService() {
private var overlayManager: LockScreenOverlayManager? = null
private lateinit var mainHandler: Handler
+ override fun attachBaseContext(newBase: Context?) {
+ super.attachBaseContext(LocaleManager.wrap(newBase ?: return))
+ }
+
enum class BiometricState {
IDLE, AUTH_STARTED
}
@@ -444,7 +450,7 @@ class AppLockAccessibilityService : AccessibilityService() {
performGlobalAction(GLOBAL_ACTION_LOCK_SCREEN)
Toast.makeText(
this,
- "Disable anti-uninstall from AppLock settings to remove this restriction.",
+ getString(R.string.accessibility_block_admin_deactivation_toast),
Toast.LENGTH_LONG
).show()
Log.w(TAG, "Blocked device admin deactivation attempt.")
diff --git a/app/src/main/java/dev/pranav/applock/services/AppLockManager.kt b/app/src/main/java/dev/pranav/applock/services/AppLockManager.kt
index f529644..fc3d4ca 100644
--- a/app/src/main/java/dev/pranav/applock/services/AppLockManager.kt
+++ b/app/src/main/java/dev/pranav/applock/services/AppLockManager.kt
@@ -89,7 +89,8 @@ object AppLockManager {
private val ALL_APP_LOCK_SERVICES = setOf(
ShizukuAppLockService::class.java,
- UsageLockService::class.java
+ UsageLockService::class.java,
+ AppLockAccessibilityService::class.java,
)
fun unlockApp(packageName: String) {
@@ -134,12 +135,17 @@ object AppLockManager {
}
fun stopAllOtherServices(context: Context, excludeService: Class<*>) {
- ALL_APP_LOCK_SERVICES
- .filter { it != excludeService }
- .forEach {
- context.stopService(Intent(context, it))
- }
+ (ALL_APP_LOCK_SERVICES - AppLockAccessibilityService::class.java - excludeService).forEach {
+ context.stopService(Intent(context, it))
+ }
LogUtils.d(TAG, "Stopped all main app lock services except ${excludeService.simpleName}.")
}
+ fun stopAllServices(context: Context) {
+ ALL_APP_LOCK_SERVICES.forEach {
+ context.stopService(Intent(context, it))
+ }
+ LogUtils.d(TAG, "Stopped all app lock services.")
+ }
+
}
diff --git a/app/src/main/java/dev/pranav/applock/services/ShizukuAppLockService.kt b/app/src/main/java/dev/pranav/applock/services/ShizukuAppLockService.kt
index ff7fa5b..8a3fe7c 100644
--- a/app/src/main/java/dev/pranav/applock/services/ShizukuAppLockService.kt
+++ b/app/src/main/java/dev/pranav/applock/services/ShizukuAppLockService.kt
@@ -6,6 +6,7 @@ import android.app.NotificationManager
import android.app.Service
import android.app.admin.DevicePolicyManager
import android.content.ComponentName
+import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.content.pm.ServiceInfo
@@ -17,6 +18,7 @@ import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat
import dev.pranav.applock.R
import dev.pranav.applock.core.broadcast.DeviceAdmin
+import dev.pranav.applock.core.utils.LocaleManager
import dev.pranav.applock.core.utils.LogUtils
import dev.pranav.applock.core.utils.appLockRepository
import dev.pranav.applock.data.repository.AppLockRepository
@@ -31,6 +33,10 @@ class ShizukuAppLockService : Service() {
private var shizukuActivityManager: ShizukuActivityManager? = null
private var previousForegroundPackage = ""
+ override fun attachBaseContext(newBase: Context?) {
+ super.attachBaseContext(LocaleManager.wrap(newBase ?: return))
+ }
+
private val notificationManager: NotificationManager by lazy {
getSystemService(NotificationManager::class.java)
}
@@ -152,7 +158,7 @@ class ShizukuAppLockService : Service() {
private fun createNotificationChannel() {
val serviceChannel = NotificationChannel(
CHANNEL_ID,
- "AppLock Service",
+ getString(R.string.shizuku_service_channel_name),
NotificationManager.IMPORTANCE_DEFAULT
)
notificationManager.createNotificationChannel(serviceChannel)
@@ -160,8 +166,8 @@ class ShizukuAppLockService : Service() {
private fun createNotification(): Notification {
return NotificationCompat.Builder(this, CHANNEL_ID)
- .setContentTitle("AppLock")
- .setContentText("Protecting your apps with Shizuku")
+ .setContentTitle(getString(R.string.shizuku_service_notification_title))
+ .setContentText(getString(R.string.shizuku_service_notification_text))
.setSmallIcon(R.drawable.baseline_shield_24)
.setPriority(NotificationCompat.PRIORITY_MIN)
.setOngoing(true)
diff --git a/app/src/main/java/dev/pranav/applock/services/UsageLockService.kt b/app/src/main/java/dev/pranav/applock/services/UsageLockService.kt
index c454f24..7e2935f 100644
--- a/app/src/main/java/dev/pranav/applock/services/UsageLockService.kt
+++ b/app/src/main/java/dev/pranav/applock/services/UsageLockService.kt
@@ -8,6 +8,7 @@ import android.app.admin.DevicePolicyManager
import android.app.usage.UsageEvents
import android.app.usage.UsageStatsManager
import android.content.ComponentName
+import android.content.Context
import android.content.Intent
import android.content.pm.ServiceInfo
import android.os.Build
@@ -20,6 +21,7 @@ import androidx.core.content.ContextCompat
import androidx.core.content.getSystemService
import dev.pranav.applock.R
import dev.pranav.applock.core.broadcast.DeviceAdmin
+import dev.pranav.applock.core.utils.LocaleManager
import dev.pranav.applock.core.utils.LogUtils
import dev.pranav.applock.core.utils.appLockRepository
import dev.pranav.applock.core.utils.hasUsagePermission
@@ -40,6 +42,10 @@ class UsageLockService: Service() {
var isServiceRunning = false
}
+ override fun attachBaseContext(newBase: Context?) {
+ super.attachBaseContext(LocaleManager.wrap(newBase ?: return))
+ }
+
private val appLockRepository: AppLockRepository by lazy { applicationContext.appLockRepository() }
private val usageStatsManager: UsageStatsManager by lazy { getSystemService()!! }
private val notificationManager: NotificationManager by lazy { getSystemService()!! }
@@ -314,7 +320,7 @@ class UsageLockService: Service() {
private fun createNotificationChannel() {
val serviceChannel = NotificationChannel(
CHANNEL_ID,
- "AppLock Service (Usage Stats)",
+ getString(R.string.usage_stats_service_channel_name),
NotificationManager.IMPORTANCE_DEFAULT
)
notificationManager.createNotificationChannel(serviceChannel)
@@ -322,8 +328,8 @@ class UsageLockService: Service() {
private fun createNotification(): Notification {
return NotificationCompat.Builder(this, CHANNEL_ID)
- .setContentTitle("App Lock")
- .setContentText("Protecting your apps")
+ .setContentTitle(getString(R.string.usage_stats_service_notification_title))
+ .setContentText(getString(R.string.usage_stats_service_notification_text))
.setSmallIcon(R.drawable.baseline_shield_24)
.setPriority(NotificationCompat.PRIORITY_MIN)
.setOngoing(true)
diff --git a/app/src/main/java/dev/pranav/applock/ui/components/UsageStatsPermission.kt b/app/src/main/java/dev/pranav/applock/ui/components/UsageStatsPermission.kt
index 2320808..6435a36 100644
--- a/app/src/main/java/dev/pranav/applock/ui/components/UsageStatsPermission.kt
+++ b/app/src/main/java/dev/pranav/applock/ui/components/UsageStatsPermission.kt
@@ -13,9 +13,11 @@ import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.DialogProperties
+import dev.pranav.applock.R
@Composable
fun UsageStatsPermission(
@@ -32,7 +34,7 @@ fun UsageStatsPermission(
),
title = {
Text(
- text = "Grant Usage Stats Permission",
+ text = stringResource(R.string.usage_stats_permission_dialog_title),
style = MaterialTheme.typography.titleLarge
)
},
@@ -42,12 +44,12 @@ fun UsageStatsPermission(
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
- text = "You've enabled Usage Stats mode, but the app requires Uage access permission to function properly.",
+ text = stringResource(R.string.usage_stats_permission_dialog_text_1),
textAlign = TextAlign.Center
)
Spacer(modifier = Modifier.height(8.dp))
Text(
- text = "Press 'Grant Permission' to open the settings and grant the necessary permissions.",
+ text = stringResource(R.string.usage_stats_permission_dialog_text_2),
textAlign = TextAlign.Start,
modifier = Modifier.fillMaxWidth()
)
@@ -55,12 +57,12 @@ fun UsageStatsPermission(
},
confirmButton = {
FilledTonalButton(onClick = onOpenSettings) {
- Text("Grant Permission")
+ Text(stringResource(R.string.grant_permission_button))
}
},
dismissButton = {
TextButton(onClick = onDismiss) {
- Text("Cancel")
+ Text(stringResource(R.string.cancel_button))
}
}
)
diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml
new file mode 100644
index 0000000..e42d49b
--- /dev/null
+++ b/app/src/main/res/values-pt-rBR/strings.xml
@@ -0,0 +1,477 @@
+
+ AppLock
+
+ Impede que pessoas não autorizadas desinstalem o aplicativo.
+ Permite que o AppLock detecte quando aplicativos protegidos são abertos e exiba a verificação de senha.
+
+ Digite sua senha para desativar a permissão de administrador.
+ PIN incorreto. Tente novamente.
+ Senha incorreta. Tente novamente.
+ Senha verificada. Agora você pode desativar a permissão de administrador.
+ Escolha o método de detecção de aplicativos
+ Selecione como o AppLock deve detectar quando aplicativos protegidos são iniciados.
+ Permissão de notificações
+ O AppLock precisa da permissão para exibir notificações, mantê-lo informado e continuar funcionando corretamente em segundo plano. Toque em "Próximo" para conceder a permissão.
+ A permissão de notificações é concedida automaticamente na sua versão do Android.
+ Boas-vindas ao AppLock
+ Proteja seus aplicativos e sua privacidade com o AppLock. Vamos guiá-lo por uma configuração rápida.
+ Proteja seus aplicativos
+ Mantenha seus aplicativos privados protegidos com mecanismos avançados de bloqueio.
+ Exibir sobre outros aplicativos
+ O AppLock precisa da permissão para aparecer sobre outros aplicativos e exibir a tela de bloqueio. Toque em "Próximo" e conceda a permissão.
+
+
+ A permissão de notificações é necessária para que o AppLock funcione corretamente.
+ Permissão do Shizuku concedida.
+ A permissão do Shizuku é necessária para recursos avançados.
+ Permita que o AppLock seja exibido sobre outros aplicativos.
+ Desativar otimização de bateria
+ Para garantir que o AppLock funcione de forma confiável em segundo plano, desative as otimizações de bateria para o aplicativo. Toque em "Próximo" para abrir as configurações.
+ Serviço de acessibilidade
+ Método padrão que funciona na maioria dos dispositivos. Requer a permissão de acessibilidade. Pode apresentar pequenos atrasos em alguns fabricantes.
+ Estatísticas de uso
+ Método experimental que utiliza as estatísticas de uso do sistema. Funciona melhor em alguns dispositivos.
+ Serviço Shizuku
+ Método avançado com melhor desempenho e experiência superior. Requer o aplicativo Shizuku instalado e configurado via ADB.
+ Você pode alterar isso mais tarde nas configurações.
+
+O serviço de acessibilidade é necessário para que o AppLock funcione corretamente.
+
+Se a mensagem "Configuração restrita" for exibida, acesse manualmente Configurações > Aplicativos > AppLock > menu no canto superior direito e toque em "Permitir configurações restritas".
+
+Toque em "Próximo" para habilitá-lo.
+
+ Permissão de acesso às estatísticas de uso
+
+Esta permissão é necessária para detectar quando aplicativos bloqueados são iniciados.
+
+Se a mensagem "Configuração restrita" for exibida, acesse manualmente Configurações > Aplicativos > AppLock > menu no canto superior direito e toque em "Permitir configurações restritas".
+
+Toque em "Próximo" para habilitá-la.
+
+
+O Shizuku fornece recursos avançados, como bloquear aplicativos do sistema e muito mais.
+
+Certifique-se de que o Shizuku esteja instalado e configurado via ADB. Toque em "Próximo" para conceder a permissão.
+
+ Privacidade total
+ Seus dados nunca saem do dispositivo. O AppLock protege sua privacidade o tempo todo.
+ Todas as permissões são necessárias para continuar.
+ Próximo
+ Começar
+ Voltar
+
+
+ Aplicativo
+ este aplicativo
+ Desbloquear %1$s
+ Confirme sua biometria para continuar
+ Usar PIN
+ Digite a senha para continuar
+ Autenticação biométrica
+
+
+ Defina um PIN para continuar.
+ Autentique-se para redefinir o PIN
+ Use o PIN, padrão ou senha do dispositivo.
+ Digite o PIN atual
+ Confirme o PIN
+ Defina um novo PIN
+ Crie um PIN para proteger seus aplicativos bloqueados. Esse PIN será solicitado sempre que você tentar acessar um aplicativo protegido.
+ Digite seu PIN atual
+ Confirme seu novo PIN
+ Crie um novo PIN
+ Digite seu PIN atual para continuar.
+ Digite o mesmo PIN novamente para confirmar.
+ Crie um PIN com pelo menos 4 dígitos.
+ Informações
+ Os PINs não coincidem. Tente novamente.
+ O PIN deve ter pelo menos 4 dígitos.
+ Digite novamente o novo PIN para confirmar.
+ Redefinir usando a senha do dispositivo
+ Cancelar
+ Recomeçar
+ Senha definida com sucesso.
+ Usar padrão
+ Usar senha
+ Usar PIN
+
+
+ Definir senha
+ Crie uma senha
+ Confirme sua senha
+ Digite a senha atual
+ Digite a senha
+ A senha deve ter pelo menos 4 caracteres.
+ A senha deve ter no máximo 64 caracteres.
+ As senhas não coincidem.
+ Senha incorreta. Tente novamente.
+
+
+ A proteção contra desinstalação está ativada.
+ Shizuku não disponível. Instale e configure o Shizuku.
+ Permissão para sobreposição necessária
+ Conceda ao aplicativo a permissão para exibir sobre outros aplicativos nas configurações. Isso é necessário para que a tela de bloqueio possa ser exibida.
+ Abrir configurações
+ Conceda a permissão do Shizuku manualmente.
+ O AppLock requer a permissão de Administrador do dispositivo para impedir sua desinstalação. Isso não afetará o funcionamento normal do seu dispositivo.
+ Participe da comunidade
+ Participe da nossa comunidade no Discord para receber atualizações e obter suporte.
+ Entrar no Discord
+ Agora não
+ Apoie o desenvolvimento
+ Doar
+ Proteção de aplicativos
+ Configurações
+ Carregando aplicativos...
+ Pesquisar aplicativos
+ Pesquisar
+ Nenhum aplicativo encontrado
+ Tente ajustar sua pesquisa.
+
+
+ Configurações
+ Voltar
+ Apoie o desenvolvimento
+ Olá! Eu sou Pranav, desenvolvedor do AppLock. Sou um estudante apaixonado por criar aplicativos úteis. Se o AppLock tem sido útil para você, considere apoiar seu desenvolvimento com uma pequena doação. Qualquer valor é muito bem-vindo e me ajuda a continuar melhorando o aplicativo e desenvolvendo novos recursos. Muito obrigado pelo seu apoio!
+ Doar
+ Permissão necessária
+ Para ativar a proteção contra desinstalação, o AppLock precisa de duas permissões: Administrador do dispositivo e Serviço de acessibilidade. Elas são necessárias para impedir desinstalações não autorizadas e garantir uma proteção robusta. Deseja conceder essas permissões agora?
+ Continuar
+ Permissão de Administrador do dispositivo
+ O AppLock precisa da permissão de Administrador do dispositivo para impedir desinstalações não autorizadas. Conceda essa permissão na próxima tela. Isso não afetará o funcionamento normal do seu dispositivo.
+ Conceder permissão
+ Permissão do Serviço de acessibilidade
+ O AppLock também precisa da permissão do Serviço de acessibilidade para monitorar tentativas de desinstalação e reativar o Administrador do dispositivo caso ele seja desativado. Isso garante que a proteção contra desinstalação continue funcionando corretamente. Ative o Serviço de acessibilidade do AppLock nas configurações.
+ Abrir configurações
+
+ Personalização da tela de bloqueio
+ Brilho máximo
+ Exibe a tela de bloqueio com brilho máximo para melhor visibilidade.
+
+ Autenticação biométrica
+ Use impressão digital ou reconhecimento facial, se disponível.
+ A autenticação biométrica não está disponível neste dispositivo.
+
+ Solicitar biometria automaticamente
+ Exibe automaticamente a solicitação de biometria na tela de bloqueio.
+
+ Comportamento do bloqueio
+
+ Comportamento após o desbloqueio
+ Desative esta opção caso o aplicativo reinicie após o desbloqueio.
+ Fechar a tela de bloqueio
+ Fechar a tela de bloqueio e abrir o aplicativo
+
+ Desbloqueio automático
+ Desbloqueia o aplicativo assim que o PIN correto for digitado.
+
+ Tempo de desbloqueio
+ Mantém os aplicativos desbloqueados por um período após o primeiro desbloqueio.
+ Definir tempo de desbloqueio
+ Os aplicativos permanecerão desbloqueados durante esse período após o primeiro desbloqueio. Selecione 0 segundos para desativar esse recurso.
+ segundos
+
+ Segurança
+
+ Alterar PIN
+ Atualize seu PIN atual.
+
+ Proteção contra desinstalação
+ Impede que o AppLock seja desinstalado sem autorização.
+
+ Resposta tátil
+ Desativa a vibração ao pressionar as teclas.
+
+ Método de detecção
+ Selecione o método que o AppLock utilizará para detectar o aplicativo em primeiro plano.
+
+ Serviço de acessibilidade
+ Método padrão. Pode apresentar pequenos atrasos em alguns fabricantes.
+
+ Estatísticas de uso
+ Método experimental que funciona melhor em alguns dispositivos.
+
+ Shizuku
+ Método avançado que requer o Shizuku configurado via ADB.
+ Shizuku indisponível ou permissão negada.
+
+ Recursos experimentais do Shizuku
+ Ativa recursos experimentais para o backend do Shizuku (por exemplo, bloquear atividades específicas). Isso pode causar instabilidade.
+
+ Sobre
+ Desenvolvedor
+ Pranav Purwar
+
+ Código-fonte
+ Veja o código-fonte no GitHub.
+
+ Apoiar o desenvolvimento
+ Ajude a manter o desenvolvimento deste aplicativo.
+
+
+ Permita que o AppLock ignore as otimizações de bateria.
+ Solicitação para ignorar otimizações de bateria enviada.
+ Falha ao abrir as configurações de bateria.
+ Localize e configure este aplicativo nas configurações de bateria.
+ Não foi possível localizar as configurações específicas. Remova o aplicativo das restrições de bateria.
+ Não foi possível abrir as configurações de bateria.
+ Erro ao verificar a permissão de acesso ao uso:
+
+
+ Conceder permissão ao Shizuku
+ Você ativou o modo Shizuku, mas o aplicativo precisa de permissões adicionais para funcionar corretamente.
+ Toque em "Conceder permissão" para abrir as configurações e conceder as permissões necessárias.
+ Conceder permissão
+ Abrir configurações
+
+
+ Permissão de Administrador do dispositivo necessária
+ Você ativou a proteção contra desinstalação. Ela requer a permissão de Administrador do dispositivo para proteger o aplicativo.
+
+
+ Permissão de acessibilidade necessária
+ Você ativou a proteção contra desinstalação, mas o aplicativo precisa da permissão de acessibilidade para aplicá-la.
+
+
+ Verificação de senha necessária
+ Digite a senha do AppLock para desativar as permissões de administrador.
+ Senha
+ A senha não pode ficar em branco.
+ Senha incorreta.
+ Verificar
+
+
+ Ativar o Serviço de acessibilidade
+ O AppLock precisa da permissão de acessibilidade para detectar quando aplicativos protegidos são abertos.
+ Siga estas etapas:
+
+1. Toque em "Abrir configurações" abaixo\n
+2. Ative a chave\n
+3. Toque em "Permitir" na caixa de confirmação.
+
+ Abrir configurações
+
+ Sobre e suporte
+ Participar da comunidade
+ %1$d minutos
+ Bloquear imediatamente
+
+ Use a impressão digital ou o reconhecimento facial para desbloquear aplicativos.
+ A autenticação biométrica não está configurada ou não está disponível.
+
+ Permissão do Shizuku concedida.
+ A permissão do Shizuku é necessária para recursos avançados.
+ O Shizuku não está em execução.
+ Conceda a permissão de acesso ao uso.
+
+ Escolha por quanto tempo os aplicativos permanecerão desbloqueados após a digitação do PIN:
+ Bloquear imediatamente
+ %1$d minuto
+ 1 hora
+
+ Para ativar a proteção contra desinstalação, o AppLock precisa de duas permissões:
+ 1. Administrador do dispositivo – impede a desinstalação\n2. Serviço de acessibilidade – monitora o uso dos aplicativos\n\nEssas permissões ajudam a proteger o aplicativo contra remoções sem sua autorização.
+
+ O AppLock precisa da permissão de Administrador do dispositivo para impedir sua desinstalação.
+ Esta permissão permite que o aplicativo:\n• Impeça sua própria desinstalação\n• Proteja as configurações do AppLock\n\nEla não afetará o funcionamento normal do seu dispositivo.
+
+ O AppLock precisa da permissão do Serviço de acessibilidade para monitorar o uso dos aplicativos.
+ Esta permissão permite que o aplicativo:\n• Detecte quando aplicativos são abertos\n• Exiba a tela de bloqueio quando necessário\n• Forneça proteção contínua aos aplicativos
+ Sua privacidade é protegida: nenhum dado é coletado ou compartilhado.
+
+ Avançado
+
+ Selecionar método alternativo
+ Escolha um método alternativo para ser utilizado quando o método principal falhar:
+
+ Confirmar
+ Ativar
+
+ Avançado
+
+ Exportar registros
+ Exporte os registros do aplicativo para depuração.
+
+ Exportar registros de segurança
+ Exporte os registros de segurança para análise.
+
+ Registros exportados.
+ Falha ao exportar os registros.
+
+ Versão %1$s
+
+
+ Desenhe o padrão para continuar.
+ Padrão incorreto. Tente novamente.
+
+
+ Exibir sobre outros aplicativos
+ Exibe a tela de bloqueio sobre os aplicativos protegidos.
+
+ Serviço de acessibilidade
+ Detecta quando aplicativos protegidos são abertos.
+
+ Estatísticas de uso
+ Detecta quando aplicativos protegidos são abertos.
+
+ Permissão do Shizuku
+ Detecta com segurança quando aplicativos protegidos são abertos.
+
+ Administrador do dispositivo
+ Impede que o AppLock seja desinstalado.
+
+ necessário(a)
+ Participar da comunidade
+ Participe da nossa comunidade no Discord para conversar com outros usuários, compartilhar sugestões e ficar por dentro das últimas novidades e recursos.
+ Entrar no Discord
+ Agora não
+
+ Doar
+ Ajude a manter o AppLock gratuito e sem anúncios para sempre.
+ Apoie o desenvolvimento
+
+ Olá! Eu sou o Pranav. Desenvolvo o AppLock no meu tempo livre para mantê-lo privado e sem anúncios para todos.
+ Conciliar este projeto com meus estudos é um desafio, mas o seu apoio torna isso possível.
+ Se o AppLock tem sido útil para você, considere fazer uma pequena doação. Qualquer valor faz toda a diferença e ajuda a manter este projeto vivo.
+ Muito obrigado por acreditar no meu trabalho!
+
+
+
+ Geral
+ Idioma
+ Sistema
+ English
+ Português (Brasil)
+
+
+ Desconhecida
+ Até desligar a tela
+ Compartilhar registros de segurança
+ Compartilhar registros
+ Registro de eventos
+ Ative o registro de depuração para ajudar na solução de problemas.
+ Até desligar a tela
+
+ Serviço de acessibilidade
+ Estatísticas de uso
+ Serviço Shizuku
+
+ Método padrão que funciona na maioria dos dispositivos.
+ Método experimental que utiliza as estatísticas de uso dos aplicativos.
+ Método avançado que utiliza o Shizuku e APIs internas.
+
+ Links
+ Comunidade no Discord
+ Código-fonte
+ Relatar problema
+
+
+ ATIVADO
+ DESATIVADO
+ Exclusões de acionamento
+ Proteção contra desinstalação
+ Nenhum aplicativo protegido
+ Toque no botão + para selecionar os aplicativos que deseja proteger.
+ Selecionar aplicativos
+ Proteger (%1$d)
+ Protegido
+ Desbloquear %1$s
+ Aviso
+ Adicionar aplicativos
+
+
+ Desenhe o padrão atual
+ Confirme o padrão
+ Crie um padrão
+ Padrão incorreto.
+ O padrão deve conter pelo menos 4 pontos.
+ Padrão incorreto.
+
+
+ Fechar
+ Continuar para %1$s
+ Continuar para %1$s
+ Fechar
+ Fechar
+ Continuar para %1$s
+
+
+ Confirmar identidade
+ Confirme sua biometria para continuar.
+ Usar PIN
+
+
+ Proteção contra desinstalação
+ Selecione os aplicativos que serão protegidos contra desinstalação.
+
+ Pacotes adicionados manualmente
+ Aplicativos instalados
+
+ Adicionar pacote manualmente
+ Digite o nome do pacote do aplicativo que deseja proteger:
+
+ Nome do pacote
+ com.example.app
+
+ Pesquisar aplicativos ou nomes de pacotes...
+
+ Shizuku não instalado
+ Shizuku não está em execução
+ Permissão necessária
+ Configuração do Shizuku
+
+ O Shizuku é necessário para configurar a proteção contra desinstalação sem acesso root.\n\nInstale o Shizuku pela Play Store ou pelo GitHub para continuar.
+
+ O Shizuku está instalado, mas o serviço em segundo plano ainda não foi iniciado (Binder ainda não recebido).\n\nAbra o aplicativo Shizuku e inicie o serviço usando Depuração sem fio ou Root.
+
+ O Shizuku está ativo, mas este aplicativo precisa da sua autorização para configurar as restrições de pacotes.\n\nFelizmente, o Shizuku é utilizado apenas ao bloquear ou desbloquear desinstalações, portanto ele não precisa permanecer em execução o tempo todo.
+
+ Instalar Shizuku
+ Abrir Shizuku
+
+ Adicionar pacote manualmente
+ Pacote adicionado manualmente
+
+ Voltar
+ Adicionar
+ Voltar
+
+
+ Exclusões de acionamento
+ Selecione os aplicativos que NÃO deverão acionar a tela de bloqueio ao alternar para aplicativos protegidos.
+
+ Pacotes adicionados manualmente
+ Aplicativos instalados
+
+ Adicionar pacote manualmente
+ Digite o nome do pacote do aplicativo que deseja excluir:
+
+ Nome do pacote
+ com.example.app
+
+ Pesquisar aplicativos ou nomes de pacotes...
+
+ Adicionar pacote manualmente
+ Pacote adicionado manualmente
+
+
+ Conceder permissão de acesso às estatísticas de uso
+ Você ativou o modo de estatísticas de uso, mas o aplicativo precisa da permissão de acesso às estatísticas para funcionar corretamente.
+ Toque em "Conceder permissão" para abrir as configurações e conceder as permissões necessárias.
+
+
+ Serviço do AppLock
+ AppLock
+ Protegendo seus aplicativos com o Shizuku
+
+ Serviço do AppLock (estatísticas de uso)
+ AppLock
+ Protegendo seus aplicativos
+
+ Desative a proteção contra desinstalação nas configurações do AppLock para remover esta restrição.
+
+
+ Aplicativo
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 1873486..45cb219 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -58,6 +58,7 @@ Make sure you have Shizuku installed and enabled via ADB. Tap \'Next\' to grant
All permissions are required to proceed.
Next
Get Started
+ Back
App
@@ -190,6 +191,9 @@ Make sure you have Shizuku installed and enabled via ADB. Tap \'Next\' to grant
Please allow our app to ignore battery optimizations.
+ Battery optimization request sent
+ Failed to open battery settings
+ Please find and configure this app in battery settings
Could not find specific settings. Please remove app from battery restrictions.
Could not open any battery settings.
Error checking usage permission:
@@ -316,4 +320,121 @@ Make sure you have Shizuku installed and enabled via ADB. Tap \'Next\' to grant
If AppLock has helped you, please consider a small donation—any amount means the world to me and keeps this project alive.
Thank you for believing in my work!
+
+
+ General
+ Language
+ System
+ English
+ Português (Brasil)
+
+
+ Unknown
+ Until screen off
+ Share audit logs
+ Share logs
+ Logging
+ Enable debug logging for troubleshooting
+ Until Screen Off
+ Accessibility Service
+ Usage Statistics
+ Shizuku Service
+ Standard method that works on most devices
+ Experimental method using app usage statistics
+ Advanced method using Shizuku and internal APIs
+ Links
+ Discord Community
+ Source Code
+ Report Issue
+
+
+ ON
+ OFF
+ Trigger exclusions
+ Anti-Uninstall
+ No Protected Apps
+ Tap the + button to selectively secure your apps.
+ Select Apps
+ Protect (%1$d)
+ Protected
+ Unlock %1$s
+ Warning
+ Add Apps
+
+
+ Enter current Pattern
+ Confirm pattern
+ Create a pattern
+ Incorrect Pattern
+ Pattern length should be at least 4
+ Incorrect pattern
+
+
+ Close
+ Continue to %1$s
+ Continue to %1$s
+ Close
+ Close
+ Continue to %1$s
+
+
+ Confirm password
+ Confirm biometric to continue
+ Use PIN
+
+
+ Anti-Uninstall Protection
+ Select apps that will be protected from uninstallation.
+ Manually Added Packages
+ Installed Apps
+ Add Package Manually
+ Enter the package name of the app you want to protect:
+ Package Name
+ com.example.app
+ Search apps or package names...
+ Shizuku Not Installed
+ Shizuku Not Running
+ Permission Required
+ Shizuku Setup
+ Shizuku is required to configure Anti-Uninstall protection without root.\n\nPlease install Shizuku from the Play Store or GitHub to continue.
+ Shizuku is installed, but the background service has not been started yet (Binder hasn\'t been received).\n\nPlease open the Shizuku application and start the service via Wireless Debugging or Root.
+ Shizuku is active, but this application needs your permission authorization to configure package restrictions.\n\nThankfully, Shizuku is only used while blocking/unblocking uninstalls, so it doesn\'t need to stay running forever!
+ Install Shizuku
+ Open Shizuku
+ Add package manually
+ Manually added package
+ Go Back
+ Add
+ Back
+
+
+ Trigger Exclusions
+ Select apps that will NOT trigger locks when switching to locked apps.
+ Manually Added Packages
+ Installed Apps
+ Add Package Manually
+ Enter the package name of the app you want to exclude:
+ Package Name
+ com.example.app
+ Search apps or package names...
+ Add package manually
+ Manually added package
+
+
+ Grant Usage Stats Permission
+ You\'ve enabled Usage Stats mode, but the app requires Usage access permission to function properly.
+ Press \'Grant Permission\' to open the settings and grant the necessary permissions.
+
+
+ AppLock Service
+ AppLock
+ Protecting your apps with Shizuku
+ AppLock Service (Usage Stats)
+ App Lock
+ Protecting your apps
+ Disable anti-uninstall from AppLock settings to remove this restriction.
+
+
+ App
+