v1.4.0
remove all preferences migrations, you have to reconfigure Wasted !! .CodeReceiver renamed to .TriggerReceiver !! remove max failed password attempts, migrate to Sentry add copy authentication code with a long click add Chinese Simplified, German, Spanish, Turkish, Ukrainian translations update Italian translation under the hood improvements Thanks to: Alex Ortiz Alexander Sergevich Noah Fisker SuperM TolDYuThad Wong Anny Giovanni Donisi (@gdonisi + @giovannidonisi)
|
@ -0,0 +1,10 @@
|
|||
name: "Validate Gradle Wrapper"
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
validation:
|
||||
name: "Validation"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: gradle/wrapper-validation-action@v1
|
|
@ -1,6 +1,6 @@
|
|||
# Wasted
|
||||
|
||||
Lock a device and wipe its data on danger.
|
||||
Lock a device and wipe its data on emergency.
|
||||
|
||||
[<img
|
||||
src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
|
||||
|
@ -22,8 +22,8 @@ message with authentication code. On trigger, using
|
|||
locks a device and optionally runs wipe.
|
||||
|
||||
Also you can:
|
||||
* limit the maximum number of failed password attempts
|
||||
* wipe a device when it was not unlocked for N days
|
||||
* wipe a device using a duress password (companion app: [Duress](https://github.com/x13a/Duress))
|
||||
|
||||
The app works in `Work Profile` too. Use [Shelter](https://github.com/PeterCxy/Shelter) to install
|
||||
risky apps and `Wasted` in it. Then you can wipe this profile data with one click without wiping
|
||||
|
@ -43,7 +43,7 @@ Broadcast:
|
|||
```sh
|
||||
$ adb shell am broadcast \
|
||||
-a me.lucky.wasted.action.TRIGGER \
|
||||
-n me.lucky.wasted/.CodeReceiver \
|
||||
-n me.lucky.wasted/.TriggerReceiver \
|
||||
-e code "b49a6576-0c27-4f03-b96b-da53501022ba"
|
||||
```
|
||||
|
||||
|
|
|
@ -10,8 +10,8 @@ android {
|
|||
applicationId "me.lucky.wasted"
|
||||
minSdk 23
|
||||
targetSdk 32
|
||||
versionCode 24
|
||||
versionName "1.3.4"
|
||||
versionCode 25
|
||||
versionName "1.4.0"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
@ -39,10 +39,10 @@ android {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'androidx.core:core-ktx:1.7.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.4.1'
|
||||
implementation 'com.google.android.material:material:1.5.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
|
||||
implementation 'androidx.core:core-ktx:1.8.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.4.2'
|
||||
implementation 'com.google.android.material:material:1.6.1'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
</receiver>
|
||||
|
||||
<receiver
|
||||
android:name=".CodeReceiver"
|
||||
android:name=".TriggerReceiver"
|
||||
android:enabled="false"
|
||||
android:exported="true"
|
||||
tools:ignore="ExportedReceiver">
|
||||
|
|
|
@ -13,8 +13,7 @@ class DeviceAdminManager(private val ctx: Context) {
|
|||
private val prefs by lazy { Preferences(ctx) }
|
||||
|
||||
fun remove() = dpm?.removeActiveAdmin(deviceAdmin)
|
||||
fun isActive(): Boolean = dpm?.isAdminActive(deviceAdmin) ?: false
|
||||
fun getCurrentFailedPasswordAttempts(): Int = dpm?.currentFailedPasswordAttempts ?: 0
|
||||
fun isActive() = dpm?.isAdminActive(deviceAdmin) ?: false
|
||||
|
||||
fun lockNow() { if (!lockPrivilegedNow()) dpm?.lockNow() }
|
||||
|
||||
|
@ -31,25 +30,20 @@ class DeviceAdminManager(private val ctx: Context) {
|
|||
return ok
|
||||
}
|
||||
|
||||
fun setMaximumFailedPasswordsForWipe(num: Int) {
|
||||
dpm?.setMaximumFailedPasswordsForWipe(deviceAdmin, num)
|
||||
}
|
||||
|
||||
fun wipeData() {
|
||||
var flags = 0
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
|
||||
flags = flags.or(DevicePolicyManager.WIPE_SILENTLY)
|
||||
if (prefs.isWipeESIM && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
|
||||
if (prefs.isWipeEmbeddedSim && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
|
||||
flags = flags.or(DevicePolicyManager.WIPE_EUICC)
|
||||
dpm?.wipeData(flags)
|
||||
}
|
||||
|
||||
fun makeRequestIntent(): Intent {
|
||||
return Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN)
|
||||
fun makeRequestIntent() =
|
||||
Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN)
|
||||
.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, deviceAdmin)
|
||||
.putExtra(
|
||||
DevicePolicyManager.EXTRA_ADD_EXPLANATION,
|
||||
ctx.getString(R.string.device_admin_description),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,30 +3,12 @@ package me.lucky.wasted
|
|||
import android.app.admin.DeviceAdminReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.UserHandle
|
||||
import android.widget.Toast
|
||||
|
||||
class DeviceAdminReceiver : DeviceAdminReceiver() {
|
||||
override fun onPasswordFailed(context: Context, intent: Intent, user: UserHandle) {
|
||||
super.onPasswordFailed(context, intent, user)
|
||||
val prefs = Preferences(context)
|
||||
val maxFailedPasswordAttempts = prefs.maxFailedPasswordAttempts
|
||||
if (!prefs.isServiceEnabled || maxFailedPasswordAttempts <= 0) return
|
||||
val admin = DeviceAdminManager(context)
|
||||
if (admin.getCurrentFailedPasswordAttempts() >= maxFailedPasswordAttempts)
|
||||
admin.wipeData()
|
||||
}
|
||||
|
||||
override fun onDisabled(context: Context, intent: Intent) {
|
||||
super.onDisabled(context, intent)
|
||||
if (Preferences(context).isServiceEnabled)
|
||||
if (Preferences(context).isEnabled)
|
||||
Toast.makeText(context, R.string.service_unavailable_popup, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
override fun onEnabled(context: Context, intent: Intent) {
|
||||
super.onEnabled(context, intent)
|
||||
DeviceAdminManager(context)
|
||||
.setMaximumFailedPasswordsForWipe(Preferences(context)
|
||||
.maxFailedPasswordAttempts.shl(1))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,7 +54,8 @@ class ForegroundService : Service() {
|
|||
|
||||
private class UnlockReceiver : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
if (context?.getSystemService(KeyguardManager::class.java)?.isDeviceSecure != true ||
|
||||
if (intent?.action != Intent.ACTION_USER_PRESENT ||
|
||||
context?.getSystemService(KeyguardManager::class.java)?.isDeviceSecure != true ||
|
||||
!Preferences(context).isWipeOnInactivity) return
|
||||
Thread(Runner(context, goAsync())).start()
|
||||
}
|
||||
|
@ -64,9 +65,9 @@ class ForegroundService : Service() {
|
|||
private val pendingResult: PendingResult,
|
||||
) : Runnable {
|
||||
override fun run() {
|
||||
val manager = WipeJobManager(ctx)
|
||||
val job = WipeJobManager(ctx)
|
||||
var delay = 1000L
|
||||
while (manager.schedule() != JobScheduler.RESULT_SUCCESS) {
|
||||
while (job.schedule() != JobScheduler.RESULT_SUCCESS) {
|
||||
Thread.sleep(delay)
|
||||
delay = delay.shl(1)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package me.lucky.wasted
|
||||
|
||||
import android.app.job.JobScheduler
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.ComponentName
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
|
@ -13,23 +15,30 @@ import androidx.core.content.ContextCompat
|
|||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import java.util.*
|
||||
import kotlin.concurrent.timerTask
|
||||
|
||||
import me.lucky.wasted.databinding.ActivityMainBinding
|
||||
|
||||
open class MainActivity : AppCompatActivity() {
|
||||
companion object {
|
||||
private const val CLIPBOARD_CLEAR_DELAY = 30_000L
|
||||
}
|
||||
|
||||
private lateinit var binding: ActivityMainBinding
|
||||
private lateinit var prefs: Preferences
|
||||
private lateinit var admin: DeviceAdminManager
|
||||
private val shortcut by lazy { ShortcutManager(this) }
|
||||
private val job by lazy { WipeJobManager(this) }
|
||||
private var clipboardManager: ClipboardManager? = null
|
||||
private var clipboardClearTask: Timer? = null
|
||||
|
||||
private val registerForDeviceAdmin =
|
||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
when (it.resultCode) {
|
||||
RESULT_OK -> setOn()
|
||||
else -> binding.toggle.isChecked = false
|
||||
when (it.resultCode) {
|
||||
RESULT_OK -> setOn()
|
||||
else -> binding.toggle.isChecked = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
@ -45,7 +54,7 @@ open class MainActivity : AppCompatActivity() {
|
|||
}
|
||||
|
||||
private fun update() {
|
||||
if (prefs.isServiceEnabled && !admin.isActive())
|
||||
if (prefs.isEnabled && !admin.isActive())
|
||||
Snackbar.make(
|
||||
binding.toggle,
|
||||
R.string.service_unavailable_popup,
|
||||
|
@ -56,35 +65,32 @@ open class MainActivity : AppCompatActivity() {
|
|||
private fun init() {
|
||||
prefs = Preferences(this)
|
||||
admin = DeviceAdminManager(this)
|
||||
clipboardManager = getSystemService(ClipboardManager::class.java)
|
||||
NotificationManager(this).createNotificationChannels()
|
||||
if (prefs.code == "") prefs.code = makeCode()
|
||||
if (prefs.authenticationCode.isEmpty()) prefs.authenticationCode = makeAuthenticationCode()
|
||||
updateCodeColorState()
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) hideESIM()
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) hideEmbeddedSim()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q &&
|
||||
!packageManager.hasSystemFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN))
|
||||
hideSecureLockScreenRequired()
|
||||
binding.apply {
|
||||
code.text = prefs.code
|
||||
authenticationCode.text = prefs.authenticationCode
|
||||
wipeData.isChecked = prefs.isWipeData
|
||||
wipeESIM.isChecked = prefs.isWipeESIM
|
||||
wipeESIM.isEnabled = wipeData.isChecked
|
||||
maxFailedPasswordAttempts.value = prefs.maxFailedPasswordAttempts.toFloat()
|
||||
wipeEmbeddedSim.isChecked = prefs.isWipeEmbeddedSim
|
||||
wipeEmbeddedSim.isEnabled = wipeData.isChecked
|
||||
wipeOnInactivitySwitch.isChecked = prefs.isWipeOnInactivity
|
||||
toggle.isChecked = prefs.isServiceEnabled
|
||||
toggle.isChecked = prefs.isEnabled
|
||||
}
|
||||
}
|
||||
|
||||
private fun hideESIM() {
|
||||
binding.wipeESIMSpace.visibility = View.GONE
|
||||
binding.wipeESIM.visibility = View.GONE
|
||||
private fun hideEmbeddedSim() {
|
||||
binding.wipeSpace.visibility = View.GONE
|
||||
binding.wipeEmbeddedSim.visibility = View.GONE
|
||||
}
|
||||
|
||||
private fun hideSecureLockScreenRequired() {
|
||||
binding.apply {
|
||||
divider.visibility = View.GONE
|
||||
maxFailedPasswordAttempts.visibility = View.GONE
|
||||
maxFailedPasswordAttemptsDescription.visibility = View.GONE
|
||||
wipeOnInactivitySpace.visibility = View.GONE
|
||||
wipeOnInactivitySwitch.visibility = View.GONE
|
||||
wipeOnInactivityDescription.visibility = View.GONE
|
||||
}
|
||||
|
@ -92,30 +98,31 @@ open class MainActivity : AppCompatActivity() {
|
|||
|
||||
private fun setup() {
|
||||
binding.apply {
|
||||
code.setOnClickListener {
|
||||
authenticationCode.setOnClickListener {
|
||||
showTriggersSettings()
|
||||
}
|
||||
code.setOnLongClickListener {
|
||||
prefs.code = makeCode()
|
||||
code.text = prefs.code
|
||||
authenticationCode.setOnLongClickListener {
|
||||
clipboardManager
|
||||
?.setPrimaryClip(ClipData.newPlainText("", prefs.authenticationCode))
|
||||
if (clipboardManager != null) {
|
||||
scheduleClipboardClear()
|
||||
Snackbar.make(
|
||||
authenticationCode,
|
||||
R.string.copied_popup,
|
||||
Snackbar.LENGTH_SHORT,
|
||||
).show()
|
||||
}
|
||||
true
|
||||
}
|
||||
wipeData.setOnCheckedChangeListener { _, isChecked ->
|
||||
prefs.isWipeData = isChecked
|
||||
wipeESIM.isEnabled = isChecked
|
||||
wipeEmbeddedSim.isEnabled = isChecked
|
||||
}
|
||||
wipeESIM.setOnCheckedChangeListener { _, isChecked ->
|
||||
prefs.isWipeESIM = isChecked
|
||||
}
|
||||
maxFailedPasswordAttempts.addOnChangeListener { _, value, _ ->
|
||||
val num = value.toInt()
|
||||
prefs.maxFailedPasswordAttempts = num
|
||||
try {
|
||||
admin.setMaximumFailedPasswordsForWipe(num.shl(1))
|
||||
} catch (exc: SecurityException) {}
|
||||
wipeEmbeddedSim.setOnCheckedChangeListener { _, isChecked ->
|
||||
prefs.isWipeEmbeddedSim = isChecked
|
||||
}
|
||||
wipeOnInactivitySwitch.setOnCheckedChangeListener { _, isChecked ->
|
||||
setWipeOnInactivityComponentsState(prefs.isServiceEnabled && isChecked)
|
||||
setWipeOnInactivityState(prefs.isEnabled && isChecked)
|
||||
prefs.isWipeOnInactivity = isChecked
|
||||
}
|
||||
wipeOnInactivitySwitch.setOnLongClickListener {
|
||||
|
@ -123,31 +130,21 @@ open class MainActivity : AppCompatActivity() {
|
|||
true
|
||||
}
|
||||
toggle.setOnCheckedChangeListener { _, isChecked ->
|
||||
when (isChecked) {
|
||||
true -> if (!admin.isActive()) requestAdmin() else setOn()
|
||||
false -> setOff()
|
||||
}
|
||||
}
|
||||
toggle.setOnLongClickListener {
|
||||
if (!toggle.isChecked) return@setOnLongClickListener false
|
||||
showPanicDialog()
|
||||
true
|
||||
if (isChecked) requestAdmin() else setOff()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showPanicDialog() {
|
||||
MaterialAlertDialogBuilder(this)
|
||||
.setTitle(R.string.dialog_confirm_panic_title)
|
||||
.setMessage(R.string.dialog_confirm_panic_message)
|
||||
.setPositiveButton(R.string.yes) { _, _ ->
|
||||
try {
|
||||
admin.lockNow()
|
||||
if (prefs.isWipeData) admin.wipeData()
|
||||
} catch (exc: SecurityException) {}
|
||||
private fun scheduleClipboardClear() {
|
||||
clipboardClearTask?.cancel()
|
||||
clipboardClearTask = Timer()
|
||||
clipboardClearTask?.schedule(timerTask {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
clipboardManager?.clearPrimaryClip()
|
||||
} else {
|
||||
clipboardManager?.setPrimaryClip(ClipData.newPlainText("", ""))
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show()
|
||||
}, CLIPBOARD_CLEAR_DELAY)
|
||||
}
|
||||
|
||||
private fun showTriggersSettings() {
|
||||
|
@ -172,25 +169,25 @@ open class MainActivity : AppCompatActivity() {
|
|||
}
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
prefs.triggers = triggers
|
||||
setTriggersState(prefs.isServiceEnabled)
|
||||
setTriggersState(prefs.isEnabled)
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun showWipeOnInactivitySettings() {
|
||||
val items = resources.getStringArray(R.array.wipe_on_inactivity_days)
|
||||
var days = prefs.wipeOnInactivityDays
|
||||
var days = prefs.wipeOnInactivityCount / 24 / 60
|
||||
var checked = items.indexOf(days.toString())
|
||||
if (checked == -1) checked = items
|
||||
.indexOf(Preferences.DEFAULT_WIPE_ON_INACTIVITY_DAYS.toString())
|
||||
.indexOf((Preferences.DEFAULT_WIPE_ON_INACTIVITY_COUNT / 24 / 60).toString())
|
||||
MaterialAlertDialogBuilder(this)
|
||||
.setTitle(R.string.wipe_on_inactivity_days)
|
||||
.setSingleChoiceItems(items, checked) { _, which ->
|
||||
days = items[which].toInt()
|
||||
}
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
prefs.wipeOnInactivityDays = days
|
||||
if (prefs.isServiceEnabled && prefs.isWipeOnInactivity) {
|
||||
prefs.wipeOnInactivityCount = days * 24 * 60
|
||||
if (prefs.isEnabled && prefs.isWipeOnInactivity) {
|
||||
if (job.schedule() == JobScheduler.RESULT_FAILURE)
|
||||
showWipeJobScheduleFailedPopup()
|
||||
}
|
||||
|
@ -199,18 +196,14 @@ open class MainActivity : AppCompatActivity() {
|
|||
}
|
||||
|
||||
private fun updateCodeColorState() {
|
||||
binding.code.setBackgroundColor(getColor(
|
||||
binding.authenticationCode.setBackgroundColor(getColor(
|
||||
if (prefs.triggers != 0) R.color.code_on else R.color.code_off
|
||||
))
|
||||
}
|
||||
|
||||
private fun setOn() {
|
||||
if (!setWipeOnInactivityComponentsState(prefs.isWipeOnInactivity)) {
|
||||
binding.toggle.isChecked = false
|
||||
showWipeJobScheduleFailedPopup()
|
||||
return
|
||||
}
|
||||
prefs.isServiceEnabled = true
|
||||
prefs.isEnabled = true
|
||||
setWipeOnInactivityState(prefs.isWipeOnInactivity)
|
||||
setTriggersState(true)
|
||||
}
|
||||
|
||||
|
@ -220,13 +213,13 @@ open class MainActivity : AppCompatActivity() {
|
|||
setPanicKitState(triggers.and(Trigger.PANIC_KIT.value) != 0)
|
||||
setTileState(triggers.and(Trigger.TILE.value) != 0)
|
||||
shortcut.setState(triggers.and(Trigger.SHORTCUT.value) != 0)
|
||||
setCodeReceiverState(triggers.and(Trigger.BROADCAST.value) != 0)
|
||||
setTriggerReceiverState(triggers.and(Trigger.BROADCAST.value) != 0)
|
||||
setNotificationListenerState(triggers.and(Trigger.NOTIFICATION.value) != 0)
|
||||
} else {
|
||||
setPanicKitState(false)
|
||||
setTileState(false)
|
||||
shortcut.setState(false)
|
||||
setCodeReceiverState(false)
|
||||
setTriggerReceiverState(false)
|
||||
setNotificationListenerState(false)
|
||||
}
|
||||
updateCodeColorState()
|
||||
|
@ -241,16 +234,18 @@ open class MainActivity : AppCompatActivity() {
|
|||
}
|
||||
|
||||
private fun setOff() {
|
||||
prefs.isServiceEnabled = false
|
||||
setWipeOnInactivityComponentsState(false)
|
||||
prefs.isEnabled = false
|
||||
setWipeOnInactivityState(false)
|
||||
setTriggersState(false)
|
||||
admin.remove()
|
||||
try {
|
||||
admin.remove()
|
||||
} catch (exc: SecurityException) {}
|
||||
}
|
||||
|
||||
private fun requestAdmin() = registerForDeviceAdmin.launch(admin.makeRequestIntent())
|
||||
private fun makeCode() = UUID.randomUUID().toString()
|
||||
private fun setCodeReceiverState(value: Boolean) =
|
||||
setComponentState(CodeReceiver::class.java, value)
|
||||
private fun makeAuthenticationCode() = UUID.randomUUID().toString()
|
||||
private fun setTriggerReceiverState(value: Boolean) =
|
||||
setComponentState(TriggerReceiver::class.java, value)
|
||||
private fun setRestartReceiverState(value: Boolean) =
|
||||
setComponentState(RestartReceiver::class.java, value)
|
||||
private fun setTileState(value: Boolean) {
|
||||
|
@ -265,6 +260,11 @@ open class MainActivity : AppCompatActivity() {
|
|||
setComponentState(PanicResponderActivity::class.java, value)
|
||||
}
|
||||
|
||||
private fun setWipeOnInactivityState(value: Boolean) {
|
||||
job.setState(value)
|
||||
setForegroundState(value)
|
||||
}
|
||||
|
||||
private fun setComponentState(cls: Class<*>, value: Boolean) {
|
||||
packageManager.setComponentEnabledSetting(
|
||||
ComponentName(this, cls),
|
||||
|
@ -281,12 +281,8 @@ open class MainActivity : AppCompatActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun setWipeOnInactivityComponentsState(value: Boolean): Boolean {
|
||||
val result = job.setState(value)
|
||||
if (result) {
|
||||
setForegroundServiceState(value)
|
||||
setRestartReceiverState(value)
|
||||
}
|
||||
return result
|
||||
private fun setForegroundState(value: Boolean) {
|
||||
setForegroundServiceState(value)
|
||||
setRestartReceiverState(value)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,11 +22,11 @@ class NotificationListenerService : NotificationListenerService() {
|
|||
override fun onNotificationPosted(sbn: StatusBarNotification?) {
|
||||
super.onNotificationPosted(sbn)
|
||||
if (sbn == null ||
|
||||
!prefs.isServiceEnabled ||
|
||||
!prefs.isEnabled ||
|
||||
prefs.triggers.and(Trigger.NOTIFICATION.value) == 0) return
|
||||
val code = prefs.code
|
||||
if (code == "" ||
|
||||
sbn.notification.extras[Notification.EXTRA_TEXT]?.toString() != code) return
|
||||
val code = prefs.authenticationCode
|
||||
assert(code.isNotEmpty())
|
||||
if (sbn.notification.extras[Notification.EXTRA_TEXT]?.toString() != code) return
|
||||
cancelAllNotifications()
|
||||
try {
|
||||
admin.lockNow()
|
||||
|
|
|
@ -13,7 +13,7 @@ class PanicResponderActivity : AppCompatActivity() {
|
|||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
if (!Panic.isTriggerIntent(intent) ||
|
||||
!prefs.isServiceEnabled ||
|
||||
!prefs.isEnabled ||
|
||||
prefs.triggers.and(Trigger.PANIC_KIT.value) == 0)
|
||||
{
|
||||
finishAndRemoveTask()
|
||||
|
|
|
@ -7,26 +7,18 @@ import androidx.security.crypto.MasterKeys
|
|||
|
||||
class Preferences(ctx: Context) {
|
||||
companion object {
|
||||
const val DEFAULT_WIPE_ON_INACTIVITY_DAYS = 7
|
||||
const val DEFAULT_WIPE_ON_INACTIVITY_COUNT = 7 * 24 * 60
|
||||
|
||||
private const val SERVICE_ENABLED = "service_enabled"
|
||||
private const val CODE = "code"
|
||||
private const val ENABLED = "enabled"
|
||||
private const val AUTHENTICATION_CODE = "authentication_code"
|
||||
private const val WIPE_DATA = "wipe_data"
|
||||
private const val WIPE_ESIM = "wipe_esim"
|
||||
private const val MAX_FAILED_PASSWORD_ATTEMPTS = "max_failed_password_attempts"
|
||||
private const val WIPE_EMBEDDED_SIM = "wipe_embedded_sim"
|
||||
private const val WIPE_ON_INACTIVITY = "wipe_on_inactivity"
|
||||
|
||||
private const val TRIGGERS = "triggers"
|
||||
private const val WIPE_ON_INACTIVITY_DAYS = "wipe_on_inactivity_days"
|
||||
private const val WIPE_ON_INACTIVITY_COUNT = "wipe_on_inactivity_count"
|
||||
|
||||
private const val FILE_NAME = "sec_shared_prefs"
|
||||
|
||||
// migration
|
||||
private const val DO_WIPE = "do_wipe"
|
||||
private const val CODE_ENABLED = "code_enabled"
|
||||
private const val WIPE_ON_INACTIVE = "wipe_on_inactive"
|
||||
private const val WIPE_ON_INACTIVE_DAYS = "wipe_on_inactive_days"
|
||||
private const val LAUNCHERS = "launchers"
|
||||
}
|
||||
|
||||
private val mk = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
|
||||
|
@ -38,49 +30,33 @@ class Preferences(ctx: Context) {
|
|||
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM,
|
||||
)
|
||||
|
||||
var isServiceEnabled: Boolean
|
||||
get() = prefs.getBoolean(SERVICE_ENABLED, false)
|
||||
set(value) = prefs.edit { putBoolean(SERVICE_ENABLED, value) }
|
||||
var isEnabled: Boolean
|
||||
get() = prefs.getBoolean(ENABLED, false)
|
||||
set(value) = prefs.edit { putBoolean(ENABLED, value) }
|
||||
|
||||
var triggers: Int
|
||||
get() = prefs.getInt(
|
||||
TRIGGERS,
|
||||
prefs.getInt(
|
||||
LAUNCHERS,
|
||||
if (prefs.getBoolean(CODE_ENABLED, false)) Trigger.BROADCAST.value else 0
|
||||
),
|
||||
)
|
||||
get() = prefs.getInt(TRIGGERS, 0)
|
||||
set(value) = prefs.edit { putInt(TRIGGERS, value) }
|
||||
|
||||
var code: String
|
||||
get() = prefs.getString(CODE, "") ?: ""
|
||||
set(value) = prefs.edit { putString(CODE, value) }
|
||||
var authenticationCode: String
|
||||
get() = prefs.getString(AUTHENTICATION_CODE, "") ?: ""
|
||||
set(value) = prefs.edit { putString(AUTHENTICATION_CODE, value) }
|
||||
|
||||
var isWipeData: Boolean
|
||||
get() = prefs.getBoolean(WIPE_DATA, prefs.getBoolean(DO_WIPE, false))
|
||||
get() = prefs.getBoolean(WIPE_DATA, false)
|
||||
set(value) = prefs.edit { putBoolean(WIPE_DATA, value) }
|
||||
|
||||
var isWipeESIM: Boolean
|
||||
get() = prefs.getBoolean(WIPE_ESIM, false)
|
||||
set(value) = prefs.edit { putBoolean(WIPE_ESIM, value) }
|
||||
|
||||
var maxFailedPasswordAttempts: Int
|
||||
get() = prefs.getInt(MAX_FAILED_PASSWORD_ATTEMPTS, 0)
|
||||
set(value) = prefs.edit { putInt(MAX_FAILED_PASSWORD_ATTEMPTS, value) }
|
||||
var isWipeEmbeddedSim: Boolean
|
||||
get() = prefs.getBoolean(WIPE_EMBEDDED_SIM, false)
|
||||
set(value) = prefs.edit { putBoolean(WIPE_EMBEDDED_SIM, value) }
|
||||
|
||||
var isWipeOnInactivity: Boolean
|
||||
get() = prefs.getBoolean(
|
||||
WIPE_ON_INACTIVITY,
|
||||
prefs.getBoolean(WIPE_ON_INACTIVE, false),
|
||||
)
|
||||
get() = prefs.getBoolean(WIPE_ON_INACTIVITY, false)
|
||||
set(value) = prefs.edit { putBoolean(WIPE_ON_INACTIVITY, value) }
|
||||
|
||||
var wipeOnInactivityDays: Int
|
||||
get() = prefs.getInt(
|
||||
WIPE_ON_INACTIVITY_DAYS,
|
||||
prefs.getInt(WIPE_ON_INACTIVE_DAYS, DEFAULT_WIPE_ON_INACTIVITY_DAYS),
|
||||
)
|
||||
set(value) = prefs.edit { putInt(WIPE_ON_INACTIVITY_DAYS, value) }
|
||||
var wipeOnInactivityCount: Int
|
||||
get() = prefs.getInt(WIPE_ON_INACTIVITY_COUNT, DEFAULT_WIPE_ON_INACTIVITY_COUNT)
|
||||
set(value) = prefs.edit { putInt(WIPE_ON_INACTIVITY_COUNT, value) }
|
||||
}
|
||||
|
||||
enum class Trigger(val value: Int) {
|
||||
|
|
|
@ -7,11 +7,10 @@ import androidx.core.content.ContextCompat
|
|||
|
||||
class RestartReceiver : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
if (context == null
|
||||
|| (intent?.action != Intent.ACTION_BOOT_COMPLETED
|
||||
&& intent?.action != Intent.ACTION_MY_PACKAGE_REPLACED)) return
|
||||
val prefs = Preferences(context)
|
||||
if (!prefs.isServiceEnabled || !prefs.isWipeOnInactivity) return
|
||||
if (intent?.action != Intent.ACTION_BOOT_COMPLETED &&
|
||||
intent?.action != Intent.ACTION_MY_PACKAGE_REPLACED) return
|
||||
val prefs = Preferences(context ?: return)
|
||||
if (!prefs.isEnabled || !prefs.isWipeOnInactivity) return
|
||||
ContextCompat.startForegroundService(
|
||||
context.applicationContext,
|
||||
Intent(context.applicationContext, ForegroundService::class.java),
|
||||
|
|
|
@ -10,7 +10,7 @@ class ShortcutActivity : AppCompatActivity() {
|
|||
finishAndRemoveTask()
|
||||
return
|
||||
}
|
||||
CodeReceiver.panic(this, intent)
|
||||
TriggerReceiver.panic(this, intent)
|
||||
finishAndRemoveTask()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,9 +20,9 @@ class ShortcutManager(private val ctx: Context) {
|
|||
.setShortLabel(ctx.getString(R.string.shortcut_label))
|
||||
.setIcon(IconCompat.createWithResource(ctx, android.R.drawable.ic_delete))
|
||||
.setIntent(
|
||||
Intent(CodeReceiver.ACTION)
|
||||
Intent(TriggerReceiver.ACTION)
|
||||
.setClass(ctx, ShortcutActivity::class.java)
|
||||
.putExtra(CodeReceiver.KEY, prefs.code)
|
||||
.putExtra(TriggerReceiver.KEY, prefs.authenticationCode)
|
||||
)
|
||||
.build(),
|
||||
)
|
||||
|
|
|
@ -32,14 +32,14 @@ class TileService : TileService() {
|
|||
override fun onStartListening() {
|
||||
super.onStartListening()
|
||||
update(
|
||||
if (prefs.isServiceEnabled && admin.isActive()) Tile.STATE_INACTIVE
|
||||
if (prefs.isEnabled && admin.isActive()) Tile.STATE_INACTIVE
|
||||
else Tile.STATE_UNAVAILABLE
|
||||
)
|
||||
}
|
||||
|
||||
override fun onClick() {
|
||||
super.onClick()
|
||||
if (!prefs.isServiceEnabled || prefs.triggers.and(Trigger.TILE.value) == 0) return
|
||||
if (!prefs.isEnabled || prefs.triggers.and(Trigger.TILE.value) == 0) return
|
||||
if (!prefs.isWipeData) {
|
||||
try {
|
||||
admin.lockNow()
|
||||
|
|
|
@ -4,7 +4,7 @@ import android.content.BroadcastReceiver
|
|||
import android.content.Context
|
||||
import android.content.Intent
|
||||
|
||||
class CodeReceiver : BroadcastReceiver() {
|
||||
class TriggerReceiver : BroadcastReceiver() {
|
||||
companion object {
|
||||
const val KEY = "code"
|
||||
const val ACTION = "me.lucky.wasted.action.TRIGGER"
|
||||
|
@ -12,9 +12,10 @@ class CodeReceiver : BroadcastReceiver() {
|
|||
fun panic(context: Context, intent: Intent?) {
|
||||
if (intent?.action != ACTION) return
|
||||
val prefs = Preferences(context)
|
||||
if (!prefs.isServiceEnabled) return
|
||||
val code = prefs.code
|
||||
if (code == "" || intent.getStringExtra(KEY) != code) return
|
||||
if (!prefs.isEnabled) return
|
||||
val code = prefs.authenticationCode
|
||||
assert(code.isNotEmpty())
|
||||
if (intent.getStringExtra(KEY) != code) return
|
||||
val admin = DeviceAdminManager(context)
|
||||
try {
|
||||
admin.lockNow()
|
||||
|
@ -24,8 +25,7 @@ class CodeReceiver : BroadcastReceiver() {
|
|||
}
|
||||
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
if (context == null ||
|
||||
Preferences(context).triggers.and(Trigger.BROADCAST.value) == 0) return
|
||||
if (Preferences(context ?: return).triggers.and(Trigger.BROADCAST.value) == 0) return
|
||||
panic(context, intent)
|
||||
}
|
||||
}
|
|
@ -16,7 +16,7 @@ class WipeJobManager(private val ctx: Context) {
|
|||
fun schedule(): Int {
|
||||
return scheduler?.schedule(
|
||||
JobInfo.Builder(JOB_ID, ComponentName(ctx, WipeJobService::class.java))
|
||||
.setMinimumLatency(TimeUnit.DAYS.toMillis(prefs.wipeOnInactivityDays.toLong()))
|
||||
.setMinimumLatency(TimeUnit.MINUTES.toMillis(prefs.wipeOnInactivityCount.toLong()))
|
||||
.setBackoffCriteria(0, JobInfo.BACKOFF_POLICY_LINEAR)
|
||||
.setPersisted(true)
|
||||
.build()
|
||||
|
|
|
@ -6,7 +6,7 @@ import android.app.job.JobService
|
|||
class WipeJobService : JobService() {
|
||||
override fun onStartJob(params: JobParameters?): Boolean {
|
||||
val prefs = Preferences(this)
|
||||
if (!prefs.isServiceEnabled || !prefs.isWipeOnInactivity) return false
|
||||
if (!prefs.isEnabled || !prefs.isWipeOnInactivity) return false
|
||||
try {
|
||||
DeviceAdminManager(this).wipeData()
|
||||
} catch (exc: SecurityException) {}
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/code"
|
||||
android:id="@+id/authenticationCode"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/code_off"
|
||||
|
@ -57,13 +57,13 @@
|
|||
android:textSize="16sp" />
|
||||
|
||||
<Space
|
||||
android:id="@+id/wipeESIMSpace"
|
||||
android:id="@+id/wipeSpace"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginVertical="2dp" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/wipeESIM"
|
||||
android:id="@+id/wipeEmbeddedSim"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
|
@ -78,27 +78,6 @@
|
|||
android:layout_marginVertical="8dp"
|
||||
android:background="?android:attr/listDivider" />
|
||||
|
||||
<com.google.android.material.slider.Slider
|
||||
android:id="@+id/maxFailedPasswordAttempts"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@string/max_failed_password_attempts_content_description"
|
||||
android:valueFrom="0"
|
||||
android:valueTo="10"
|
||||
android:stepSize="1.0" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/maxFailedPasswordAttemptsDescription"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/max_failed_password_attempts_description" />
|
||||
|
||||
<Space
|
||||
android:id="@+id/wipeOnInactivitySpace"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginVertical="8dp" />
|
||||
|
||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
||||
android:id="@+id/wipeOnInactivitySwitch"
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Wasted</string>
|
||||
<string name="description">Aktiviere Wasted, um dein Gerät im Notfall zu sperren. Du kannst hierfür PanicKit, Tile und Shortcut verwenden, oder eine Nachricht mit diesem Authentifizierungscode auf dein Gerät senden.</string>
|
||||
<string name="device_admin_label">Wasted</string>
|
||||
<string name="device_admin_description">Wasted erlauben das Gerät zu sperren und optional die Daten in einem Notfall zu löschen</string>
|
||||
<string name="service_unavailable_popup">Geräteadministrator nicht verfügbar</string>
|
||||
<string name="wipe_data_check_box">Daten löschen</string>
|
||||
<string name="wipe_esim_check_box">eSim löschen</string>
|
||||
<string name="panic_app_dialog_title">Bestätige Panik App</string>
|
||||
<string name="panic_app_dialog_message">Bist du sicher, dass du %1$s erlauben willst, destruktive Aktionen auslösen zu lassen\?</string>
|
||||
<string name="panic_app_unknown_app">eine unbekannte App</string>
|
||||
<string name="allow">Zulassen</string>
|
||||
<string name="tile_label">Flugmodus</string>
|
||||
<string name="shortcut_label">Panik</string>
|
||||
<string name="wipe_on_inactivity_switch">Bei Inaktivität löschen</string>
|
||||
<string name="wipe_on_inactivity_description">Den Speicher des Geräts löschen, wenn es für N Tage nicht entsperrt wurde.</string>
|
||||
<string name="wipe_on_inactivity_days">Tage</string>
|
||||
<string name="notification_channel_default_name">Standard</string>
|
||||
<string name="wipe_job_description">Den Speicher bei Inaktivität löschen</string>
|
||||
<string name="wipe_job_schedule_failed_popup">Fehler bei der Durchführung des Wipes</string>
|
||||
<string name="foreground_service_description">Receive events which require foreground service</string>
|
||||
<string name="foreground_service_notification_title">Guard</string>
|
||||
<string name="triggers_array_panic_kit">PanicKit</string>
|
||||
<string name="triggers_array_tile">Tile</string>
|
||||
<string name="triggers_array_shortcut">Shortcut</string>
|
||||
<string name="triggers_array_broadcast">Broadcast</string>
|
||||
<string name="triggers_array_notification">Benachrichtigung</string>
|
||||
<string name="notification_listener_service_label">Viper</string>
|
||||
<string name="notification_listener_service_description">Benachrichtigungen nach dem Authentifizierungscode absuchen</string>
|
||||
<string name="triggers">Auslöser</string>
|
||||
<string name="copied_popup">Kopiert</string>
|
||||
</resources>
|
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Wasted</string>
|
||||
<string name="description">Active Wasted para bloquear un dispositivo en caso de emergencia. Puede usar PanicKit, title, acceso directo o enviar un mensaje con este código de autenticación.</string>
|
||||
<string name="device_admin_label">Wasted</string>
|
||||
<string name="device_admin_description">Permita que Wasted bloquee un dispositivo y, opcionalmente, borre sus datos en caso de emergencia</string>
|
||||
<string name="service_unavailable_popup">Administrador de dispositivos no disponible</string>
|
||||
<string name="wipe_data_check_box">Borrar datos</string>
|
||||
<string name="wipe_esim_check_box">Borrar eSIM</string>
|
||||
<string name="panic_app_dialog_title">Confirmar aplicación de pánico</string>
|
||||
<string name="panic_app_dialog_message">¿Está seguro de que desea permitir que %1$s active acciones de pánico destructivas\?</string>
|
||||
<string name="panic_app_unknown_app">una aplicación desconocida</string>
|
||||
<string name="allow">Permitir</string>
|
||||
<string name="tile_label">Modo avión</string>
|
||||
<string name="shortcut_label">Pánico</string>
|
||||
<string name="wipe_on_inactivity_switch">Borrar por inactividad</string>
|
||||
<string name="wipe_on_inactivity_description">Limpia un dispositivo cuando no fue desbloqueado por N días.</string>
|
||||
<string name="wipe_on_inactivity_days">Días</string>
|
||||
<string name="notification_channel_default_name">Predeterminado</string>
|
||||
<string name="wipe_job_description">Borrar un dispositivo por inactividad</string>
|
||||
<string name="wipe_job_schedule_failed_popup">Error al programar un trabajo de borrado</string>
|
||||
<string name="foreground_service_description">Recibir eventos que requieren servicio de primer plano</string>
|
||||
<string name="foreground_service_notification_title">Guardia</string>
|
||||
<string name="triggers_array_panic_kit">PanicKit</string>
|
||||
<string name="triggers_array_tile">Título</string>
|
||||
<string name="triggers_array_shortcut">Acceso directo</string>
|
||||
<string name="triggers_array_broadcast">Transmisión</string>
|
||||
<string name="triggers_array_notification">Notificación</string>
|
||||
<string name="notification_listener_service_label">Viper</string>
|
||||
<string name="notification_listener_service_description">Escanear notificaciones para el código de autenticación</string>
|
||||
<string name="triggers">Activadores</string>
|
||||
<string name="copied_popup">Copiado</string>
|
||||
</resources>
|
|
@ -13,8 +13,6 @@
|
|||
<string name="allow">Consenti</string>
|
||||
<string name="tile_label">Modalità aereo</string>
|
||||
<string name="shortcut_label">Panico</string>
|
||||
<string name="max_failed_password_attempts_description">Numero massimo di tentativi di password falliti.</string>
|
||||
<string name="max_failed_password_attempts_content_description">Numero</string>
|
||||
<string name="wipe_on_inactivity_switch">Cancella in caso di inattività</string>
|
||||
<string name="wipe_on_inactivity_description">Cancella i dati quando il dispositivo non viene sbloccato per N giorni.</string>
|
||||
<string name="wipe_on_inactivity_days">Giorni</string>
|
||||
|
@ -23,9 +21,6 @@
|
|||
<string name="wipe_job_schedule_failed_popup">Impossibile pianificare un processo di cancellazione</string>
|
||||
<string name="foreground_service_description">Ricevi eventi che richiedono il servizio in primo piano</string>
|
||||
<string name="foreground_service_notification_title">Guardia</string>
|
||||
<string name="dialog_confirm_panic_title">Attivare il panico\?</string>
|
||||
<string name="dialog_confirm_panic_message">Questo bloccherà un dispositivo e, facoltativamente, cancellerà i suoi dati.</string>
|
||||
<string name="yes">Sì</string>
|
||||
<string name="triggers_array_panic_kit">PanicKit</string>
|
||||
<string name="triggers_array_tile">Toggle</string>
|
||||
<string name="triggers_array_shortcut">Scorciatoia</string>
|
||||
|
@ -34,4 +29,5 @@
|
|||
<string name="notification_listener_service_label">Viper</string>
|
||||
<string name="notification_listener_service_description">Scansiona le notifiche per il codice di autenticazione</string>
|
||||
<string name="triggers">Attivatori</string>
|
||||
<string name="copied_popup">Copiato</string>
|
||||
</resources>
|
||||
|
|
|
@ -13,8 +13,6 @@
|
|||
<string name="allow">Разрешить</string>
|
||||
<string name="tile_label">Режим полета</string>
|
||||
<string name="shortcut_label">Тревога</string>
|
||||
<string name="max_failed_password_attempts_description">Максимальное количество неудачных попыток ввода пароля.</string>
|
||||
<string name="max_failed_password_attempts_content_description">Количество</string>
|
||||
<string name="wipe_on_inactivity_switch">Стереть при неактивности</string>
|
||||
<string name="wipe_on_inactivity_description">Стереть данные когда устройство не разблокируется N дней.</string>
|
||||
<string name="wipe_on_inactivity_days">Дней</string>
|
||||
|
@ -23,9 +21,6 @@
|
|||
<string name="wipe_job_schedule_failed_popup">Не удалось запланировать сервис стирания данных</string>
|
||||
<string name="foreground_service_description">Получать события для которых требуется сервис на переднем плане</string>
|
||||
<string name="foreground_service_notification_title">Охрана</string>
|
||||
<string name="dialog_confirm_panic_title">Активировать тревогу\?</string>
|
||||
<string name="dialog_confirm_panic_message">Это заблокирует устройство и возможно сотрёт его данные.</string>
|
||||
<string name="yes">Да</string>
|
||||
<string name="triggers_array_panic_kit">Тревожная кнопка</string>
|
||||
<string name="triggers_array_tile">Плитка</string>
|
||||
<string name="triggers_array_shortcut">Ярлык</string>
|
||||
|
@ -34,4 +29,5 @@
|
|||
<string name="notification_listener_service_label">Вайпер</string>
|
||||
<string name="notification_listener_service_description">Сканирует уведомления на наличие кода аутентификации</string>
|
||||
<string name="triggers">Триггеры</string>
|
||||
<string name="copied_popup">Скопировано</string>
|
||||
</resources>
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Wasted</string>
|
||||
<string name="description">Turn on Wasted to lock a device on emergency. You can use PanicKit, tile, shortcut or send a message with this authentication code.</string>
|
||||
<string name="device_admin_label">Wasted</string>
|
||||
<string name="device_admin_description">Allow Wasted to lock a device and optionally wipe its data on emergency</string>
|
||||
<string name="service_unavailable_popup">Cihaz yöneticisi mevcut değil</string>
|
||||
<string name="wipe_data_check_box">Verileri sil</string>
|
||||
<string name="wipe_esim_check_box">eSIM\'i sil</string>
|
||||
<string name="panic_app_dialog_title">Panik uygulamasını onayla</string>
|
||||
<string name="panic_app_dialog_message">Yıkıcı panik eylemlerini tetiklemek için %1$s\'e izin vermek istediğinizden emin misiniz\?</string>
|
||||
<string name="panic_app_unknown_app">bilinmeyen bir uygulama</string>
|
||||
<string name="allow">İzin ver</string>
|
||||
<string name="tile_label">Uçak modu</string>
|
||||
<string name="shortcut_label">Panik</string>
|
||||
<string name="wipe_on_inactivity_switch">Kullanılmadığında sil</string>
|
||||
<string name="wipe_on_inactivity_description">N gün boyunca kilidi açılmamış bir cihazı silin.</string>
|
||||
<string name="wipe_on_inactivity_days">Gün</string>
|
||||
<string name="notification_channel_default_name">Varsayılan</string>
|
||||
<string name="wipe_job_description">Cihaz kullanılmadığında siler</string>
|
||||
<string name="wipe_job_schedule_failed_popup">Silme işi zamanlanamadı</string>
|
||||
<string name="foreground_service_description">Ön plan hizmeti gerektiren olayları al</string>
|
||||
<string name="foreground_service_notification_title">Guard</string>
|
||||
<string name="triggers_array_panic_kit">PanicKit</string>
|
||||
<string name="triggers_array_tile">Tile</string>
|
||||
<string name="triggers_array_shortcut">Kısayol</string>
|
||||
<string name="triggers_array_broadcast">Yayın</string>
|
||||
<string name="triggers_array_notification">Bildirim</string>
|
||||
<string name="notification_listener_service_label">Viper</string>
|
||||
<string name="notification_listener_service_description">Kimlik doğrulama kodu için bildirimleri tara</string>
|
||||
<string name="triggers">Tetikleyiciler</string>
|
||||
<string name="copied_popup">Kopyalandı</string>
|
||||
</resources>
|
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Wasted</string>
|
||||
<string name="description">Turn on Wasted to lock a device on emergency. You can use PanicKit, tile, shortcut or send a message with this authentication code.</string>
|
||||
<string name="device_admin_label">Wasted</string>
|
||||
<string name="device_admin_description">Allow Wasted to lock a device and optionally wipe its data on emergency</string>
|
||||
<string name="service_unavailable_popup">Адміністратор пристрою недоступний</string>
|
||||
<string name="wipe_data_check_box">Стерти дані</string>
|
||||
<string name="wipe_esim_check_box">Стерти дані eSIM</string>
|
||||
<string name="panic_app_dialog_title">Confirm panic app</string>
|
||||
<string name="panic_app_dialog_message">Are you sure that you want to allow %1$s to trigger destructive panic actions\?</string>
|
||||
<string name="panic_app_unknown_app">невідомий додаток</string>
|
||||
<string name="allow">Allow</string>
|
||||
<string name="tile_label">Режим польоту</string>
|
||||
<string name="shortcut_label">Тривога</string>
|
||||
<string name="wipe_on_inactivity_switch">Wipe on inactivity</string>
|
||||
<string name="wipe_on_inactivity_description">Видалення пристрою, коли його не було розблоковано протягом N днів.</string>
|
||||
<string name="wipe_on_inactivity_days">Днів</string>
|
||||
<string name="notification_channel_default_name">За замовчуванням</string>
|
||||
<string name="wipe_job_description">Wipe a device on inactivity</string>
|
||||
<string name="wipe_job_schedule_failed_popup">Failed to schedule a wipe job</string>
|
||||
<string name="foreground_service_description">Receive events which require foreground service</string>
|
||||
<string name="foreground_service_notification_title">Захисник</string>
|
||||
<string name="triggers_array_panic_kit">PanicKit</string>
|
||||
<string name="triggers_array_tile">Tile</string>
|
||||
<string name="triggers_array_shortcut">Гарячі клавіші</string>
|
||||
<string name="triggers_array_broadcast">Трансляція</string>
|
||||
<string name="triggers_array_notification">Сповіщення</string>
|
||||
<string name="notification_listener_service_label">Знищувач</string>
|
||||
<string name="notification_listener_service_description">Сканувати сповіщення для коду автентифікації</string>
|
||||
<string name="triggers">Тріггери</string>
|
||||
<string name="copied_popup">Скопійовано</string>
|
||||
</resources>
|
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">自毁</string>
|
||||
<string name="description">开启自毁,在紧急状况下锁定设备。您可以使用触发器、追踪器、快捷方式或者发送验证码信息。</string>
|
||||
<string name="device_admin_label">自毁</string>
|
||||
<string name="device_admin_description">允许自毁锁定设备以及可选择在紧急状况下擦除数据</string>
|
||||
<string name="service_unavailable_popup">设备管理员不可用</string>
|
||||
<string name="wipe_data_check_box">擦除数据</string>
|
||||
<string name="wipe_esim_check_box">擦除eSIM</string>
|
||||
<string name="panic_app_dialog_title">确认需要使用的紧急应用程序</string>
|
||||
<string name="panic_app_dialog_message">你是否确定允许%1$s触发紧急自毁行为?</string>
|
||||
<string name="panic_app_unknown_app">未知应用</string>
|
||||
<string name="allow">允许</string>
|
||||
<string name="tile_label">飞行模式</string>
|
||||
<string name="shortcut_label">紧急</string>
|
||||
<string name="wipe_on_inactivity_switch">在不使用时擦除</string>
|
||||
<string name="wipe_on_inactivity_description">当设备在N天内没被解锁时擦除</string>
|
||||
<string name="wipe_on_inactivity_days">天数</string>
|
||||
<string name="notification_channel_default_name">默认设置</string>
|
||||
<string name="wipe_job_description">擦除不使用的设备</string>
|
||||
<string name="wipe_job_schedule_failed_popup">无法制定擦除计划</string>
|
||||
<string name="foreground_service_description">接收需要前台服务的事件</string>
|
||||
<string name="foreground_service_notification_title">守卫</string>
|
||||
<string name="triggers_array_panic_kit">恐慌触发器</string>
|
||||
<string name="triggers_array_tile">追踪器</string>
|
||||
<string name="triggers_array_shortcut">快捷方式</string>
|
||||
<string name="triggers_array_broadcast">广播</string>
|
||||
<string name="triggers_array_notification">通知</string>
|
||||
<string name="notification_listener_service_label">哨兵</string>
|
||||
<string name="notification_listener_service_description">扫描通知中的验证码</string>
|
||||
<string name="triggers">触发器</string>
|
||||
<string name="copied_popup">复制成功</string>
|
||||
</resources>
|
|
@ -1,9 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Wasted</string>
|
||||
<string name="description">Turn on Wasted to lock a device on danger. You can use PanicKit, tile, shortcut or send a message with this authentication code.</string>
|
||||
<string name="description">Turn on Wasted to lock a device on emergency. You can use PanicKit, tile, shortcut or send a message with this authentication code.</string>
|
||||
<string name="device_admin_label">Wasted</string>
|
||||
<string name="device_admin_description">Allow Wasted to lock a device and optionally wipe its data on danger</string>
|
||||
<string name="device_admin_description">Allow Wasted to lock a device and optionally wipe its data on emergency</string>
|
||||
<string name="service_unavailable_popup">Device admin unavailable</string>
|
||||
<string name="wipe_data_check_box">Wipe data</string>
|
||||
<string name="wipe_esim_check_box">Wipe eSIM</string>
|
||||
|
@ -13,8 +13,6 @@
|
|||
<string name="allow">Allow</string>
|
||||
<string name="tile_label">Airplane mode</string>
|
||||
<string name="shortcut_label">Panic</string>
|
||||
<string name="max_failed_password_attempts_description">Maximum number of failed password attempts.</string>
|
||||
<string name="max_failed_password_attempts_content_description">Count</string>
|
||||
<string name="wipe_on_inactivity_switch">Wipe on inactivity</string>
|
||||
<string name="wipe_on_inactivity_description">Wipe a device when it was not unlocked for N days.</string>
|
||||
<string name="wipe_on_inactivity_days">Days</string>
|
||||
|
@ -22,10 +20,7 @@
|
|||
<string name="wipe_job_description">Wipe a device on inactivity</string>
|
||||
<string name="wipe_job_schedule_failed_popup">Failed to schedule a wipe job</string>
|
||||
<string name="foreground_service_description">Receive events which require foreground service</string>
|
||||
<string name="foreground_service_notification_title">Sentry</string>
|
||||
<string name="dialog_confirm_panic_title">Activate panic\?</string>
|
||||
<string name="dialog_confirm_panic_message">This will lock a device and optionally wipe its data.</string>
|
||||
<string name="yes">Yes</string>
|
||||
<string name="foreground_service_notification_title">Guard</string>
|
||||
<string name="triggers_array_panic_kit">PanicKit</string>
|
||||
<string name="triggers_array_tile">Tile</string>
|
||||
<string name="triggers_array_shortcut">Shortcut</string>
|
||||
|
@ -34,4 +29,5 @@
|
|||
<string name="notification_listener_service_label">Viper</string>
|
||||
<string name="notification_listener_service_description">Scan notifications for the authentication code</string>
|
||||
<string name="triggers">Triggers</string>
|
||||
<string name="copied_popup">Copied</string>
|
||||
</resources>
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<device-admin>
|
||||
<support-transfer-ownership />
|
||||
<uses-policies>
|
||||
<force-lock />
|
||||
<watch-login />
|
||||
<wipe-data />
|
||||
</uses-policies>
|
||||
</device-admin>
|
|
@ -5,8 +5,8 @@ buildscript {
|
|||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:7.1.1'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10"
|
||||
classpath 'com.android.tools.build:gradle:7.2.1'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.0"
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
remove all preferences migrations, you have to reconfigure Wasted !!
|
||||
.CodeReceiver renamed to .TriggerReceiver !!
|
||||
remove max failed password attempts, migrate to Sentry
|
||||
add copy authentication code with a long click
|
||||
add Chinese Simplified, German, Spanish, Turkish, Ukrainian translations
|
||||
update Italian translation
|
||||
under the hood improvements
|
||||
|
||||
Thanks to:
|
||||
Alex Ortiz
|
||||
Alexander Sergevich
|
||||
Noah Fisker
|
||||
SuperM
|
||||
TolDYuThad
|
||||
Wong Anny
|
||||
Giovanni Donisi (@gdonisi + @giovannidonisi)
|
|
@ -1,11 +1,11 @@
|
|||
Lock a device and wipe its data on danger.
|
||||
Lock a device and wipe its data on emergency.
|
||||
|
||||
You can use PanicKit, tile, shortcut or send a message with authentication code. On trigger, using
|
||||
Device Administration API, it locks a device and optionally runs wipe.
|
||||
|
||||
Also you can:
|
||||
* limit the maximum number of failed password attempts
|
||||
* wipe a device when it was not unlocked for N days
|
||||
* wipe a device using a duress password (companion app: [Duress](https://github.com/x13a/Duress))
|
||||
|
||||
The app works in Work Profile too. Use Shelter to install risky apps and Wasted in it. Then you can
|
||||
wipe this profile data with one click without wiping the whole device.
|
||||
|
|
Before Width: | Height: | Size: 117 KiB After Width: | Height: | Size: 101 KiB |
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 104 KiB |
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 100 KiB |
Before Width: | Height: | Size: 114 KiB |
|
@ -1 +1 @@
|
|||
Lock a device and wipe its data on danger
|
||||
Lock a device and wipe its data on emergency
|
||||
|
|
|
@ -5,7 +5,6 @@ un codice di autenticazione. All'attivazione, utilizzando l'API di amministrazio
|
|||
del dispositivo, blocca un dispositivo e facoltativamente esegue la cancellazione.
|
||||
|
||||
Puoi anche:
|
||||
* limitare il numero massimo di tentativi di password falliti
|
||||
* cancellare i dati quando un dispositivo non è stato sbloccato per N giorni
|
||||
|
||||
L'app funziona anche nel Profilo di Lavoro. Usa Shelter per installarci dentro le app pericolose e Wasted.
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
заблокирует устройство и при необходимости запустит очистку данных.
|
||||
|
||||
Также вы можете:
|
||||
* ограничить максимальное количество неудачных попыток ввода пароля
|
||||
* стереть данные, если устройство не было разблокировано в течение определённого количества дней
|
||||
* стереть данные, используя пароль под принуждением (приложение: https://github.com/x13a/Duress)
|
||||
|
||||
Приложение работает и в рабочем профиле. Используйте Shelter для установки рискованных приложений и
|
||||
Wasted в него. Тогда вы можете стереть данные этого профиля одним кликом, без удаления данных всего
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#Fri Jun 24 03:06:49 MSK 2022
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
|
||||
distributionPath=wrapper/dists
|
||||
distributionSha256Sum=f581709a9c35e9cb92e16f585d2c4bc99b2b1a5f85d2badbd3dc6bff59e1e6dd
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
|