mirror of https://github.com/x13a/Wasted.git
direct boot aware
This commit is contained in:
parent
16b79689c6
commit
31b343b7d8
|
@ -24,6 +24,7 @@ locks a device and optionally runs wipe.
|
|||
Also you can:
|
||||
* wipe a device when it was not unlocked for N time
|
||||
* wipe a device using a duress password (companion app: [Duress](https://github.com/x13a/Duress))
|
||||
* wipe a device on USB connection event (companion app: [USBKill](https://github.com/x13a/USBKill))
|
||||
|
||||
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
|
||||
|
|
|
@ -10,8 +10,8 @@ android {
|
|||
applicationId "me.lucky.wasted"
|
||||
minSdk 23
|
||||
targetSdk 32
|
||||
versionCode 27
|
||||
versionName "1.4.2"
|
||||
versionCode 28
|
||||
versionName "1.4.3"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
@ -42,11 +42,11 @@ dependencies {
|
|||
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'
|
||||
|
||||
implementation 'androidx.security:security-crypto:1.0.0'
|
||||
implementation 'androidx.preference:preference-ktx:1.2.0'
|
||||
implementation 'info.guardianproject.panic:panic:1.0'
|
||||
}
|
|
@ -41,6 +41,7 @@
|
|||
|
||||
<receiver
|
||||
android:name=".TriggerReceiver"
|
||||
android:directBootAware="true"
|
||||
android:enabled="false"
|
||||
android:exported="true"
|
||||
tools:ignore="ExportedReceiver">
|
||||
|
@ -51,6 +52,7 @@
|
|||
|
||||
<activity
|
||||
android:name=".PanicResponderActivity"
|
||||
android:directBootAware="true"
|
||||
android:noHistory="true"
|
||||
android:enabled="false"
|
||||
android:exported="true"
|
||||
|
@ -86,6 +88,7 @@
|
|||
|
||||
<service
|
||||
android:name=".TileService"
|
||||
android:directBootAware="true"
|
||||
android:icon="@drawable/ic_baseline_airplanemode_active_24"
|
||||
android:label="@string/tile_label"
|
||||
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"
|
||||
|
@ -101,20 +104,24 @@
|
|||
|
||||
<service
|
||||
android:name=".WipeJobService"
|
||||
android:directBootAware="true"
|
||||
android:exported="true"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE">
|
||||
</service>
|
||||
|
||||
<service
|
||||
android:name=".ForegroundService"
|
||||
android:directBootAware="true"
|
||||
android:exported="false">
|
||||
</service>
|
||||
|
||||
<receiver
|
||||
android:name=".RestartReceiver"
|
||||
android:directBootAware="true"
|
||||
android:enabled="false"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
|
||||
</intent-filter>
|
||||
|
@ -122,6 +129,7 @@
|
|||
|
||||
<service
|
||||
android:name=".NotificationListenerService"
|
||||
android:directBootAware="true"
|
||||
android:enabled="false"
|
||||
android:exported="true"
|
||||
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
|
||||
|
|
|
@ -10,7 +10,7 @@ import java.lang.Exception
|
|||
class DeviceAdminManager(private val ctx: Context) {
|
||||
private val dpm = ctx.getSystemService(DevicePolicyManager::class.java)
|
||||
private val deviceAdmin by lazy { ComponentName(ctx, DeviceAdminReceiver::class.java) }
|
||||
private val prefs by lazy { Preferences(ctx) }
|
||||
private val prefs by lazy { Preferences.new(ctx) }
|
||||
|
||||
fun remove() = dpm?.removeActiveAdmin(deviceAdmin)
|
||||
fun isActive() = dpm?.isAdminActive(deviceAdmin) ?: false
|
||||
|
|
|
@ -59,7 +59,7 @@ class ForegroundService : Service() {
|
|||
private var unlocked = false
|
||||
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
if (!Preferences(context ?: return).isWipeOnInactivity) return
|
||||
if (!Preferences.new(context ?: return).isWipeOnInactivity) return
|
||||
when (intent?.action) {
|
||||
Intent.ACTION_USER_PRESENT -> {
|
||||
if (context.getSystemService(KeyguardManager::class.java)
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
package me.lucky.wasted
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.ComponentName
|
||||
import android.content.Intent
|
||||
import android.content.*
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
|
@ -32,6 +29,7 @@ open class MainActivity : AppCompatActivity() {
|
|||
|
||||
private lateinit var binding: ActivityMainBinding
|
||||
private lateinit var prefs: Preferences
|
||||
private lateinit var prefsdb: Preferences
|
||||
private lateinit var admin: DeviceAdminManager
|
||||
private val shortcut by lazy { ShortcutManager(this) }
|
||||
private val job by lazy { WipeJobManager(this) }
|
||||
|
@ -40,6 +38,10 @@ open class MainActivity : AppCompatActivity() {
|
|||
private var clipboardManager: ClipboardManager? = null
|
||||
private var clipboardClearTask: Timer? = null
|
||||
|
||||
private val prefsListener = SharedPreferences.OnSharedPreferenceChangeListener { _, key ->
|
||||
prefs.copyTo(prefsdb, key)
|
||||
}
|
||||
|
||||
private val registerForDeviceAdmin =
|
||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
when (it.resultCode) {
|
||||
|
@ -68,9 +70,15 @@ open class MainActivity : AppCompatActivity() {
|
|||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
prefs.registerListener(prefsListener)
|
||||
update()
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
prefs.unregisterListener(prefsListener)
|
||||
}
|
||||
|
||||
private fun update() {
|
||||
if (prefs.isEnabled && !admin.isActive())
|
||||
Snackbar.make(
|
||||
|
@ -82,6 +90,8 @@ open class MainActivity : AppCompatActivity() {
|
|||
|
||||
private fun init() {
|
||||
prefs = Preferences(this)
|
||||
prefsdb = Preferences(this, encrypted = false)
|
||||
prefs.copyTo(prefsdb)
|
||||
admin = DeviceAdminManager(this)
|
||||
clipboardManager = getSystemService(ClipboardManager::class.java)
|
||||
NotificationManager(this).createNotificationChannels()
|
||||
|
|
|
@ -15,7 +15,7 @@ class NotificationListenerService : NotificationListenerService() {
|
|||
}
|
||||
|
||||
private fun init() {
|
||||
prefs = Preferences(this)
|
||||
prefs = Preferences.new(this)
|
||||
admin = DeviceAdminManager(this)
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import info.guardianproject.panic.Panic
|
|||
import info.guardianproject.panic.PanicResponder
|
||||
|
||||
class PanicResponderActivity : AppCompatActivity() {
|
||||
private val prefs by lazy { Preferences(this) }
|
||||
private val prefs by lazy { Preferences.new(this) }
|
||||
private val admin by lazy { DeviceAdminManager(this) }
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
package me.lucky.wasted
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Build
|
||||
import android.os.UserManager
|
||||
import androidx.core.content.edit
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.security.crypto.EncryptedSharedPreferences
|
||||
import androidx.security.crypto.MasterKeys
|
||||
|
||||
class Preferences(ctx: Context) {
|
||||
class Preferences(ctx: Context, encrypted: Boolean = true) {
|
||||
companion object {
|
||||
const val DEFAULT_WIPE_ON_INACTIVITY_COUNT = 7 * 24 * 60
|
||||
private const val DEFAULT_WIPE_ON_INACTIVITY_COUNT = 7 * 24 * 60
|
||||
|
||||
private const val ENABLED = "enabled"
|
||||
private const val AUTHENTICATION_CODE = "authentication_code"
|
||||
|
@ -19,16 +23,28 @@ class Preferences(ctx: Context) {
|
|||
private const val WIPE_ON_INACTIVITY_COUNT = "wipe_on_inactivity_count"
|
||||
|
||||
private const val FILE_NAME = "sec_shared_prefs"
|
||||
|
||||
fun new(ctx: Context) = Preferences(
|
||||
ctx,
|
||||
encrypted = Build.VERSION.SDK_INT < Build.VERSION_CODES.N ||
|
||||
ctx.getSystemService(UserManager::class.java).isUserUnlocked,
|
||||
)
|
||||
}
|
||||
|
||||
private val mk = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
|
||||
private val prefs = EncryptedSharedPreferences.create(
|
||||
private val prefs: SharedPreferences = if (encrypted) {
|
||||
val mk = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
|
||||
EncryptedSharedPreferences.create(
|
||||
FILE_NAME,
|
||||
mk,
|
||||
ctx,
|
||||
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
||||
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM,
|
||||
)
|
||||
} else {
|
||||
val context = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
ctx.createDeviceProtectedStorageContext() else ctx
|
||||
PreferenceManager.getDefaultSharedPreferences(context)
|
||||
}
|
||||
|
||||
var isEnabled: Boolean
|
||||
get() = prefs.getBoolean(ENABLED, false)
|
||||
|
@ -57,6 +73,25 @@ class Preferences(ctx: Context) {
|
|||
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) }
|
||||
|
||||
fun registerListener(listener: SharedPreferences.OnSharedPreferenceChangeListener) =
|
||||
prefs.registerOnSharedPreferenceChangeListener(listener)
|
||||
|
||||
fun unregisterListener(listener: SharedPreferences.OnSharedPreferenceChangeListener) =
|
||||
prefs.unregisterOnSharedPreferenceChangeListener(listener)
|
||||
|
||||
fun copyTo(dst: Preferences, key: String? = null) = dst.prefs.edit {
|
||||
for (entry in prefs.all.entries) {
|
||||
val k = entry.key
|
||||
if (key != null && k != key) continue
|
||||
val v = entry.value ?: continue
|
||||
when (v) {
|
||||
is Boolean -> putBoolean(k, v)
|
||||
is Int -> putInt(k, v)
|
||||
is String -> putString(k, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class Trigger(val value: Int) {
|
||||
|
|
|
@ -7,9 +7,10 @@ import androidx.core.content.ContextCompat
|
|||
|
||||
class RestartReceiver : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
if (intent?.action != Intent.ACTION_BOOT_COMPLETED &&
|
||||
if (intent?.action != Intent.ACTION_LOCKED_BOOT_COMPLETED &&
|
||||
intent?.action != Intent.ACTION_BOOT_COMPLETED &&
|
||||
intent?.action != Intent.ACTION_MY_PACKAGE_REPLACED) return
|
||||
val prefs = Preferences(context ?: return)
|
||||
val prefs = Preferences.new(context ?: return)
|
||||
if (!prefs.isEnabled || !prefs.isWipeOnInactivity) return
|
||||
ContextCompat.startForegroundService(
|
||||
context.applicationContext,
|
||||
|
|
|
@ -5,7 +5,6 @@ import android.service.quicksettings.Tile
|
|||
import android.service.quicksettings.TileService
|
||||
import androidx.annotation.RequiresApi
|
||||
import java.util.*
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import kotlin.concurrent.timerTask
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.N)
|
||||
|
@ -16,7 +15,7 @@ class TileService : TileService() {
|
|||
|
||||
private lateinit var prefs: Preferences
|
||||
private lateinit var admin: DeviceAdminManager
|
||||
private val counter = AtomicInteger()
|
||||
private var counter = 0
|
||||
private var timer: Timer? = null
|
||||
|
||||
override fun onCreate() {
|
||||
|
@ -25,7 +24,7 @@ class TileService : TileService() {
|
|||
}
|
||||
|
||||
private fun init() {
|
||||
prefs = Preferences(this)
|
||||
prefs = Preferences.new(this)
|
||||
admin = DeviceAdminManager(this)
|
||||
}
|
||||
|
||||
|
@ -46,7 +45,9 @@ class TileService : TileService() {
|
|||
} catch (exc: SecurityException) {}
|
||||
return
|
||||
}
|
||||
when (counter.getAndIncrement()) {
|
||||
val v = counter
|
||||
counter++
|
||||
when (v) {
|
||||
0 -> {
|
||||
update(Tile.STATE_ACTIVE)
|
||||
timer?.cancel()
|
||||
|
@ -61,7 +62,7 @@ class TileService : TileService() {
|
|||
else -> {
|
||||
timer?.cancel()
|
||||
update(Tile.STATE_INACTIVE)
|
||||
counter.set(0)
|
||||
counter = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ class TriggerReceiver : BroadcastReceiver() {
|
|||
|
||||
fun panic(context: Context, intent: Intent?) {
|
||||
if (intent?.action != ACTION) return
|
||||
val prefs = Preferences(context)
|
||||
val prefs = Preferences.new(context)
|
||||
if (!prefs.isEnabled) return
|
||||
val code = prefs.authenticationCode
|
||||
assert(code.isNotEmpty())
|
||||
|
@ -25,7 +25,8 @@ class TriggerReceiver : BroadcastReceiver() {
|
|||
}
|
||||
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
if (Preferences(context ?: return).triggers.and(Trigger.BROADCAST.value) == 0) return
|
||||
if (Preferences.new(context ?: return).triggers.and(Trigger.BROADCAST.value) == 0)
|
||||
return
|
||||
panic(context, intent)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ class WipeJobManager(private val ctx: Context) {
|
|||
companion object {
|
||||
private const val JOB_ID = 1000
|
||||
}
|
||||
private val prefs by lazy { Preferences(ctx) }
|
||||
private val prefs by lazy { Preferences.new(ctx) }
|
||||
private val scheduler = ctx.getSystemService(JobScheduler::class.java)
|
||||
|
||||
fun schedule(): Int {
|
||||
|
|
|
@ -5,7 +5,7 @@ import android.app.job.JobService
|
|||
|
||||
class WipeJobService : JobService() {
|
||||
override fun onStartJob(params: JobParameters?): Boolean {
|
||||
val prefs = Preferences(this)
|
||||
val prefs = Preferences.new(this)
|
||||
if (!prefs.isEnabled || !prefs.isWipeOnInactivity) return false
|
||||
try {
|
||||
DeviceAdminManager(this).wipeData()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<vector android:height="24dp" android:tint="#000000"
|
||||
<vector android:height="24dp" android:tint="?attr/colorControlNormal"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M19.14,12.94c0.04,-0.3 0.06,-0.61 0.06,-0.94c0,-0.32 -0.02,-0.64 -0.07,-0.94l2.03,-1.58c0.18,-0.14 0.23,-0.41 0.12,-0.61l-1.92,-3.32c-0.12,-0.22 -0.37,-0.29 -0.59,-0.22l-2.39,0.96c-0.5,-0.38 -1.03,-0.7 -1.62,-0.94L14.4,2.81c-0.04,-0.24 -0.24,-0.41 -0.48,-0.41h-3.84c-0.24,0 -0.43,0.17 -0.47,0.41L9.25,5.35C8.66,5.59 8.12,5.92 7.63,6.29L5.24,5.33c-0.22,-0.08 -0.47,0 -0.59,0.22L2.74,8.87C2.62,9.08 2.66,9.34 2.86,9.48l2.03,1.58C4.84,11.36 4.8,11.69 4.8,12s0.02,0.64 0.07,0.94l-2.03,1.58c-0.18,0.14 -0.23,0.41 -0.12,0.61l1.92,3.32c0.12,0.22 0.37,0.29 0.59,0.22l2.39,-0.96c0.5,0.38 1.03,0.7 1.62,0.94l0.36,2.54c0.05,0.24 0.24,0.41 0.48,0.41h3.84c0.24,0 0.44,-0.17 0.47,-0.41l0.36,-2.54c0.59,-0.24 1.13,-0.56 1.62,-0.94l2.39,0.96c0.22,0.08 0.47,0 0.59,-0.22l1.92,-3.32c0.12,-0.22 0.07,-0.47 -0.12,-0.61L19.14,12.94zM12,15.6c-1.98,0 -3.6,-1.62 -3.6,-3.6s1.62,-3.6 3.6,-3.6s3.6,1.62 3.6,3.6S13.98,15.6 12,15.6z"/>
|
||||
|
|
|
@ -11,7 +11,10 @@
|
|||
<string name="tile_label">Modalità aereo</string>
|
||||
<string name="shortcut_label">Panico</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_description">Cancella i dati quando il dispositivo non viene sbloccato per N tempo.</string>
|
||||
<string name="wipe_on_inactivity_time_hint">tempo</string>
|
||||
<string name="wipe_on_inactivity_time_error">7d / 48h / 120m</string>
|
||||
<string name="wipe_on_inactivity_time_helper_text">[d]giorni [h]ore [m]minuti</string>
|
||||
<string name="notification_channel_default_name">Predefinito</string>
|
||||
<string name="foreground_service_notification_title">Guardia</string>
|
||||
<string name="triggers_array_panic_kit">PanicKit</string>
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
direct boot aware
|
||||
update Italian translation, thanks to Giovanni Donisi (@gdonisi + @giovannidonisi)
|
|
@ -6,6 +6,7 @@ Device Administration API, it locks a device and optionally runs wipe.
|
|||
Also you can:
|
||||
* wipe a device when it was not unlocked for N time
|
||||
* wipe a device using a duress password (companion app: [Duress](https://github.com/x13a/Duress))
|
||||
* wipe a device on USB connection event (companion app: [USBKill](https://github.com/x13a/USBKill))
|
||||
|
||||
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.
|
||||
|
|
Loading…
Reference in New Issue