recast option

This commit is contained in:
lucky 2022-08-30 02:43:07 +03:00
parent 6fc8fa382b
commit 957bec3a33
20 changed files with 295 additions and 101 deletions

View File

@ -19,7 +19,8 @@ Lock a device and wipe its data on emergency.
You can use [PanicKit](https://guardianproject.info/code/panickit/), tile, shortcut or send a You can use [PanicKit](https://guardianproject.info/code/panickit/), tile, shortcut or send a
message with a secret code. On trigger, using message with a secret code. On trigger, using
[Device Administration API](https://developer.android.com/guide/topics/admin/device-admin), it [Device Administration API](https://developer.android.com/guide/topics/admin/device-admin), it
locks a device and optionally runs wipe. locks a device and optionally runs wipe (factory reset). Also it can send a broadcast message
instead of the wipe.
Also you can: Also you can:
* fire when a device was not unlocked for X time * fire when a device was not unlocked for X time

View File

@ -10,8 +10,8 @@ android {
applicationId "me.lucky.wasted" applicationId "me.lucky.wasted"
minSdk 23 minSdk 23
targetSdk 32 targetSdk 32
versionCode 38 versionCode 39
versionName "1.5.9" versionName "1.5.10"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
} }

View File

@ -144,7 +144,7 @@ open class MainActivity : AppCompatActivity() {
R.id.nav_trigger_notification -> NotificationFragment() R.id.nav_trigger_notification -> NotificationFragment()
R.id.nav_trigger_lock -> LockFragment() R.id.nav_trigger_lock -> LockFragment()
R.id.nav_trigger_application -> ApplicationFragment() R.id.nav_trigger_application -> ApplicationFragment()
R.id.top_settings -> SettingsFragment() R.id.nav_recast -> RecastFragment()
else -> MainFragment() else -> MainFragment()
} }

View File

@ -19,6 +19,12 @@ class Preferences(ctx: Context, encrypted: Boolean = true) {
private const val WIPE_DATA = "wipe_data" private const val WIPE_DATA = "wipe_data"
private const val WIPE_EMBEDDED_SIM = "wipe_embedded_sim" private const val WIPE_EMBEDDED_SIM = "wipe_embedded_sim"
private const val RECAST_ENABLED = "recast_enabled"
private const val RECAST_ACTION = "recast_action"
private const val RECAST_RECEIVER = "recast_receiver"
private const val RECAST_EXTRA_KEY = "recast_extra_key"
private const val RECAST_EXTRA_VALUE = "recast_extra_value"
private const val TRIGGERS = "triggers" private const val TRIGGERS = "triggers"
private const val TRIGGER_LOCK_COUNT = "trigger_lock_count" private const val TRIGGER_LOCK_COUNT = "trigger_lock_count"
private const val TRIGGER_TILE_DELAY = "trigger_tile_delay" private const val TRIGGER_TILE_DELAY = "trigger_tile_delay"
@ -90,6 +96,26 @@ class Preferences(ctx: Context, encrypted: Boolean = true) {
get() = prefs.getInt(TRIGGER_APPLICATION_OPTIONS, 0) get() = prefs.getInt(TRIGGER_APPLICATION_OPTIONS, 0)
set(value) = prefs.edit { putInt(TRIGGER_APPLICATION_OPTIONS, value) } set(value) = prefs.edit { putInt(TRIGGER_APPLICATION_OPTIONS, value) }
var isRecastEnabled: Boolean
get() = prefs.getBoolean(RECAST_ENABLED, false)
set(value) = prefs.edit { putBoolean(RECAST_ENABLED, value) }
var recastAction: String
get() = prefs.getString(RECAST_ACTION, "") ?: ""
set(value) = prefs.edit { putString(RECAST_ACTION, value) }
var recastReceiver: String
get() = prefs.getString(RECAST_RECEIVER, "") ?: ""
set(value) = prefs.edit { putString(RECAST_RECEIVER, value) }
var recastExtraKey: String
get() = prefs.getString(RECAST_EXTRA_KEY, "") ?: ""
set(value) = prefs.edit { putString(RECAST_EXTRA_KEY, value) }
var recastExtraValue: String
get() = prefs.getString(RECAST_EXTRA_VALUE, "") ?: ""
set(value) = prefs.edit { putString(RECAST_EXTRA_VALUE, value) }
fun registerListener(listener: SharedPreferences.OnSharedPreferenceChangeListener) = fun registerListener(listener: SharedPreferences.OnSharedPreferenceChangeListener) =
prefs.registerOnSharedPreferenceChangeListener(listener) prefs.registerOnSharedPreferenceChangeListener(listener)

View File

@ -1,5 +1,6 @@
package me.lucky.wasted package me.lucky.wasted
import android.app.KeyguardManager
import android.content.ComponentName import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
@ -7,6 +8,7 @@ import android.content.pm.PackageManager
import android.os.Build import android.os.Build
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import me.lucky.wasted.admin.DeviceAdminManager
import me.lucky.wasted.trigger.notification.NotificationListenerService import me.lucky.wasted.trigger.notification.NotificationListenerService
import me.lucky.wasted.trigger.panic.PanicConnectionActivity import me.lucky.wasted.trigger.panic.PanicConnectionActivity
import me.lucky.wasted.trigger.panic.PanicResponderActivity import me.lucky.wasted.trigger.panic.PanicResponderActivity
@ -26,10 +28,10 @@ class Utils(private val ctx: Context) {
} }
} }
private val shortcut by lazy { ShortcutManager(ctx) } private val prefs by lazy { Preferences.new(ctx) }
fun setEnabled(enabled: Boolean) { fun setEnabled(enabled: Boolean) {
val triggers = Preferences(ctx).triggers val triggers = prefs.triggers
setPanicKitEnabled(enabled && triggers.and(Trigger.PANIC_KIT.value) != 0) setPanicKitEnabled(enabled && triggers.and(Trigger.PANIC_KIT.value) != 0)
setTileEnabled(enabled && triggers.and(Trigger.TILE.value) != 0) setTileEnabled(enabled && triggers.and(Trigger.TILE.value) != 0)
setShortcutEnabled(enabled && triggers.and(Trigger.SHORTCUT.value) != 0) setShortcutEnabled(enabled && triggers.and(Trigger.SHORTCUT.value) != 0)
@ -50,6 +52,7 @@ class Utils(private val ctx: Context) {
} }
fun setShortcutEnabled(enabled: Boolean) { fun setShortcutEnabled(enabled: Boolean) {
val shortcut = ShortcutManager(ctx)
if (!enabled) shortcut.remove() if (!enabled) shortcut.remove()
setComponentEnabled(ShortcutActivity::class.java, enabled) setComponentEnabled(ShortcutActivity::class.java, enabled)
if (enabled) shortcut.push() if (enabled) shortcut.push()
@ -63,7 +66,6 @@ class Utils(private val ctx: Context) {
fun updateApplicationEnabled() { fun updateApplicationEnabled() {
val prefix = "${ctx.packageName}.trigger.application" val prefix = "${ctx.packageName}.trigger.application"
val prefs = Preferences(ctx)
val options = prefs.triggerApplicationOptions val options = prefs.triggerApplicationOptions
val enabled = prefs.isEnabled && prefs.triggers.and(Trigger.APPLICATION.value) != 0 val enabled = prefs.isEnabled && prefs.triggers.and(Trigger.APPLICATION.value) != 0
setComponentEnabled( setComponentEnabled(
@ -85,7 +87,6 @@ class Utils(private val ctx: Context) {
} }
fun updateForegroundRequiredEnabled() { fun updateForegroundRequiredEnabled() {
val prefs = Preferences(ctx)
val enabled = prefs.isEnabled val enabled = prefs.isEnabled
val triggers = prefs.triggers val triggers = prefs.triggers
val isUSB = triggers.and(Trigger.USB.value) != 0 val isUSB = triggers.and(Trigger.USB.value) != 0
@ -114,4 +115,36 @@ class Utils(private val ctx: Context) {
PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP, PackageManager.DONT_KILL_APP,
) )
fun fire(trigger: Trigger, safe: Boolean = true) {
if (!prefs.isEnabled || prefs.triggers.and(trigger.value) == 0) return
val admin = DeviceAdminManager(ctx)
try {
admin.lockNow()
if (prefs.isWipeData && safe) admin.wipeData()
} catch (exc: SecurityException) {}
if (prefs.isRecastEnabled && safe) recast()
}
fun isDeviceLocked() = ctx.getSystemService(KeyguardManager::class.java).isDeviceLocked
private fun recast() {
val action = prefs.recastAction
if (action.isEmpty()) return
ctx.sendBroadcast(Intent(action).apply {
val cls = prefs.recastReceiver.split('/')
val packageName = cls.firstOrNull() ?: ""
if (packageName.isNotEmpty()) {
setPackage(packageName)
if (cls.size == 2)
setClassName(
packageName,
"$packageName.${cls[1].trimStart('.')}",
)
}
addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
val extraKey = prefs.recastExtraKey
if (extraKey.isNotEmpty()) putExtra(extraKey, prefs.recastExtraValue)
})
}
} }

View File

@ -0,0 +1,76 @@
package me.lucky.wasted.fragment
import android.content.Context
import android.content.SharedPreferences
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.widget.doAfterTextChanged
import androidx.fragment.app.Fragment
import me.lucky.wasted.Preferences
import me.lucky.wasted.databinding.FragmentRecastBinding
class RecastFragment : Fragment() {
private lateinit var binding: FragmentRecastBinding
private lateinit var ctx: Context
private lateinit var prefs: Preferences
private lateinit var prefsdb: Preferences
private val prefsListener = SharedPreferences.OnSharedPreferenceChangeListener { _, key ->
prefs.copyTo(prefsdb, key)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View {
binding = FragmentRecastBinding.inflate(inflater, container, false)
init()
setup()
return binding.root
}
override fun onStart() {
super.onStart()
prefs.registerListener(prefsListener)
}
override fun onStop() {
super.onStop()
prefs.unregisterListener(prefsListener)
}
private fun init() {
ctx = requireContext()
prefs = Preferences(ctx)
prefsdb = Preferences(ctx, encrypted = false)
binding.apply {
enabled.isChecked = prefs.isRecastEnabled
action.editText?.setText(prefs.recastAction)
receiver.editText?.setText(prefs.recastReceiver)
extraKey.editText?.setText(prefs.recastExtraKey)
extraValue.editText?.setText(prefs.recastExtraValue)
}
}
private fun setup() = binding.apply {
enabled.setOnCheckedChangeListener { _, isChecked ->
prefs.isRecastEnabled = isChecked
}
action.editText?.doAfterTextChanged {
prefs.recastAction = it?.toString()?.trim() ?: return@doAfterTextChanged
}
receiver.editText?.doAfterTextChanged {
prefs.recastReceiver = it?.toString()?.trim() ?: return@doAfterTextChanged
}
extraKey.editText?.doAfterTextChanged {
prefs.recastExtraKey = it?.toString()?.trim() ?: return@doAfterTextChanged
}
extraValue.editText?.doAfterTextChanged {
prefs.recastExtraValue = it?.toString()?.trim() ?: return@doAfterTextChanged
}
}
}

View File

@ -3,24 +3,13 @@ package me.lucky.wasted.trigger.application
import android.os.Bundle import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import me.lucky.wasted.Preferences
import me.lucky.wasted.Trigger import me.lucky.wasted.Trigger
import me.lucky.wasted.admin.DeviceAdminManager import me.lucky.wasted.Utils
class ApplicationActivity : AppCompatActivity() { class ApplicationActivity : AppCompatActivity() {
private val prefs by lazy { Preferences(this) }
private val admin by lazy { DeviceAdminManager(this) }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
if (!prefs.isEnabled || prefs.triggers.and(Trigger.APPLICATION.value) == 0) { Utils(this).fire(Trigger.APPLICATION)
finishAndRemoveTask()
return
}
try {
admin.lockNow()
if (prefs.isWipeData) admin.wipeData()
} catch (exc: SecurityException) {}
finishAndRemoveTask() finishAndRemoveTask()
} }
} }

View File

@ -6,30 +6,23 @@ import android.content.Intent
import me.lucky.wasted.Preferences import me.lucky.wasted.Preferences
import me.lucky.wasted.Trigger import me.lucky.wasted.Trigger
import me.lucky.wasted.admin.DeviceAdminManager import me.lucky.wasted.Utils
class BroadcastReceiver : BroadcastReceiver() { class BroadcastReceiver : BroadcastReceiver() {
companion object { companion object {
const val KEY = "code" const val KEY = "code"
const val ACTION = "me.lucky.wasted.action.TRIGGER" const val ACTION = "me.lucky.wasted.action.TRIGGER"
fun panic(context: Context, intent: Intent?) { fun panic(context: Context, intent: Intent?, trigger: Trigger) {
if (intent?.action != ACTION) return if (intent?.action != ACTION) return
val prefs = Preferences.new(context) val secret = Preferences.new(context).secret
if (!prefs.isEnabled) return
val secret = prefs.secret
assert(secret.isNotEmpty()) assert(secret.isNotEmpty())
if (intent.getStringExtra(KEY)?.trim() != secret) return if (intent.getStringExtra(KEY)?.trim() != secret) return
val admin = DeviceAdminManager(context) Utils(context).fire(trigger)
try {
admin.lockNow()
if (prefs.isWipeData) admin.wipeData()
} catch (exc: SecurityException) {}
} }
} }
override fun onReceive(context: Context?, intent: Intent?) { override fun onReceive(context: Context?, intent: Intent?) {
if (Preferences.new(context ?: return).triggers.and(Trigger.BROADCAST.value) != 0) panic(context ?: return, intent, Trigger.BROADCAST)
panic(context, intent)
} }
} }

View File

@ -3,19 +3,12 @@ package me.lucky.wasted.trigger.lock
import android.app.job.JobParameters import android.app.job.JobParameters
import android.app.job.JobService import android.app.job.JobService
import me.lucky.wasted.admin.DeviceAdminManager
import me.lucky.wasted.Preferences
import me.lucky.wasted.Trigger import me.lucky.wasted.Trigger
import me.lucky.wasted.Utils
class LockJobService : JobService() { class LockJobService : JobService() {
override fun onStartJob(params: JobParameters?): Boolean { override fun onStartJob(params: JobParameters?): Boolean {
val prefs = Preferences.new(this) Utils(this).fire(Trigger.LOCK)
if (!prefs.isEnabled || prefs.triggers.and(Trigger.LOCK.value) == 0) return false
val admin = DeviceAdminManager(this)
try {
admin.lockNow()
if (prefs.isWipeData) admin.wipeData()
} catch (exc: SecurityException) {}
return false return false
} }

View File

@ -5,13 +5,13 @@ import android.os.Build
import android.service.notification.NotificationListenerService import android.service.notification.NotificationListenerService
import android.service.notification.StatusBarNotification import android.service.notification.StatusBarNotification
import me.lucky.wasted.admin.DeviceAdminManager
import me.lucky.wasted.Preferences import me.lucky.wasted.Preferences
import me.lucky.wasted.Trigger import me.lucky.wasted.Trigger
import me.lucky.wasted.Utils
class NotificationListenerService : NotificationListenerService() { class NotificationListenerService : NotificationListenerService() {
private lateinit var prefs: Preferences private lateinit var prefs: Preferences
private lateinit var admin: DeviceAdminManager private lateinit var utils: Utils
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
@ -20,22 +20,17 @@ class NotificationListenerService : NotificationListenerService() {
private fun init() { private fun init() {
prefs = Preferences.new(this) prefs = Preferences.new(this)
admin = DeviceAdminManager(this) utils = Utils(this)
} }
override fun onNotificationPosted(sbn: StatusBarNotification?) { override fun onNotificationPosted(sbn: StatusBarNotification?) {
super.onNotificationPosted(sbn) super.onNotificationPosted(sbn)
if (sbn == null || if (sbn == null) return
!prefs.isEnabled ||
prefs.triggers.and(Trigger.NOTIFICATION.value) == 0) return
val secret = prefs.secret val secret = prefs.secret
assert(secret.isNotEmpty()) assert(secret.isNotEmpty())
if (sbn.notification.extras[Notification.EXTRA_TEXT]?.toString() != secret) return if (sbn.notification.extras[Notification.EXTRA_TEXT]?.toString()?.trim() != secret) return
cancelAllNotifications() cancelAllNotifications()
try { utils.fire(Trigger.NOTIFICATION)
admin.lockNow()
if (prefs.isWipeData) admin.wipeData()
} catch (exc: SecurityException) {}
} }
override fun onListenerConnected() { override fun onListenerConnected() {

View File

@ -5,28 +5,20 @@ import androidx.appcompat.app.AppCompatActivity
import info.guardianproject.panic.Panic import info.guardianproject.panic.Panic
import info.guardianproject.panic.PanicResponder import info.guardianproject.panic.PanicResponder
import me.lucky.wasted.admin.DeviceAdminManager
import me.lucky.wasted.Preferences
import me.lucky.wasted.Trigger import me.lucky.wasted.Trigger
import me.lucky.wasted.Utils
class PanicResponderActivity : AppCompatActivity() { class PanicResponderActivity : AppCompatActivity() {
private val prefs by lazy { Preferences.new(this) }
private val admin by lazy { DeviceAdminManager(this) }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
if (!Panic.isTriggerIntent(intent) || if (!Panic.isTriggerIntent(intent)) {
!prefs.isEnabled ||
prefs.triggers.and(Trigger.PANIC_KIT.value) == 0)
{
finishAndRemoveTask() finishAndRemoveTask()
return return
} }
try { Utils(this).fire(
admin.lockNow() Trigger.PANIC_KIT,
if (PanicResponder.receivedTriggerFromConnectedApp(this) && PanicResponder.receivedTriggerFromConnectedApp(this),
prefs.isWipeData) admin.wipeData() )
} catch (exc: SecurityException) {}
finishAndRemoveTask() finishAndRemoveTask()
} }
} }

View File

@ -13,7 +13,7 @@ import androidx.core.app.NotificationCompat
import me.lucky.wasted.Preferences import me.lucky.wasted.Preferences
import me.lucky.wasted.R import me.lucky.wasted.R
import me.lucky.wasted.Trigger import me.lucky.wasted.Trigger
import me.lucky.wasted.admin.DeviceAdminManager import me.lucky.wasted.Utils
import me.lucky.wasted.trigger.lock.LockJobManager import me.lucky.wasted.trigger.lock.LockJobManager
class ForegroundService : Service() { class ForegroundService : Service() {
@ -113,17 +113,11 @@ class ForegroundService : Service() {
override fun onReceive(context: Context?, intent: Intent?) { override fun onReceive(context: Context?, intent: Intent?) {
if (intent?.action != ACTION_USB_STATE) return if (intent?.action != ACTION_USB_STATE) return
val prefs = Preferences.new(context ?: return) val utils = Utils(context ?: return)
if (!prefs.isEnabled || if (!utils.isDeviceLocked()) return
prefs.triggers.and(Trigger.USB.value) == 0 ||
!context.getSystemService(KeyguardManager::class.java).isDeviceLocked) return
val extras = intent.extras ?: return val extras = intent.extras ?: return
if (!extras.getBoolean(KEY_1) && !extras.getBoolean(KEY_2)) return if (!extras.getBoolean(KEY_1) && !extras.getBoolean(KEY_2)) return
val admin = DeviceAdminManager(context) utils.fire(Trigger.USB)
try {
admin.lockNow()
if (prefs.isWipeData) admin.wipeData()
} catch (exc: SecurityException) {}
} }
} }
} }

View File

@ -3,15 +3,13 @@ package me.lucky.wasted.trigger.shortcut
import android.os.Bundle import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import me.lucky.wasted.Preferences
import me.lucky.wasted.Trigger import me.lucky.wasted.Trigger
import me.lucky.wasted.trigger.broadcast.BroadcastReceiver import me.lucky.wasted.trigger.broadcast.BroadcastReceiver
class ShortcutActivity : AppCompatActivity() { class ShortcutActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
if (Preferences(this).triggers.and(Trigger.SHORTCUT.value) != 0) BroadcastReceiver.panic(this, intent, Trigger.SHORTCUT)
BroadcastReceiver.panic(this, intent)
finishAndRemoveTask() finishAndRemoveTask()
} }
} }

View File

@ -10,11 +10,13 @@ import kotlin.concurrent.timerTask
import me.lucky.wasted.admin.DeviceAdminManager import me.lucky.wasted.admin.DeviceAdminManager
import me.lucky.wasted.Preferences import me.lucky.wasted.Preferences
import me.lucky.wasted.Trigger import me.lucky.wasted.Trigger
import me.lucky.wasted.Utils
@RequiresApi(Build.VERSION_CODES.N) @RequiresApi(Build.VERSION_CODES.N)
class TileService : TileService() { class TileService : TileService() {
private lateinit var prefs: Preferences private lateinit var prefs: Preferences
private lateinit var admin: DeviceAdminManager private lateinit var admin: DeviceAdminManager
private lateinit var utils: Utils
private var counter = 0 private var counter = 0
private var timer: Timer? = null private var timer: Timer? = null
@ -26,6 +28,7 @@ class TileService : TileService() {
private fun init() { private fun init() {
prefs = Preferences.new(this) prefs = Preferences.new(this)
admin = DeviceAdminManager(this) admin = DeviceAdminManager(this)
utils = Utils(this)
} }
override fun onStartListening() { override fun onStartListening() {
@ -38,11 +41,8 @@ class TileService : TileService() {
override fun onClick() { override fun onClick() {
super.onClick() super.onClick()
if (!prefs.isEnabled || prefs.triggers.and(Trigger.TILE.value) == 0) return
if (!prefs.isWipeData) { if (!prefs.isWipeData) {
try { utils.fire(Trigger.TILE, false)
admin.lockNow()
} catch (exc: SecurityException) {}
return return
} }
val v = counter val v = counter
@ -53,10 +53,7 @@ class TileService : TileService() {
timer?.cancel() timer?.cancel()
timer = Timer() timer = Timer()
timer?.schedule(timerTask { timer?.schedule(timerTask {
try { utils.fire(Trigger.TILE)
admin.lockNow()
admin.wipeData()
} catch (exc: SecurityException) {}
}, prefs.triggerTileDelay) }, prefs.triggerTileDelay)
} }
else -> { else -> {

View File

@ -1,27 +1,19 @@
package me.lucky.wasted.trigger.usb package me.lucky.wasted.trigger.usb
import android.app.KeyguardManager
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.hardware.usb.UsbManager import android.hardware.usb.UsbManager
import me.lucky.wasted.Preferences
import me.lucky.wasted.Trigger import me.lucky.wasted.Trigger
import me.lucky.wasted.admin.DeviceAdminManager import me.lucky.wasted.Utils
class UsbReceiver : BroadcastReceiver() { class UsbReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) { override fun onReceive(context: Context?, intent: Intent?) {
if (intent?.action != UsbManager.ACTION_USB_DEVICE_ATTACHED && if (intent?.action != UsbManager.ACTION_USB_DEVICE_ATTACHED &&
intent?.action != UsbManager.ACTION_USB_ACCESSORY_ATTACHED) return intent?.action != UsbManager.ACTION_USB_ACCESSORY_ATTACHED) return
val prefs = Preferences.new(context ?: return) val utils = Utils(context ?: return)
if (!prefs.isEnabled || if (!utils.isDeviceLocked()) return
prefs.triggers.and(Trigger.USB.value) == 0 || utils.fire(Trigger.USB)
!context.getSystemService(KeyguardManager::class.java).isDeviceLocked) return
val admin = DeviceAdminManager(context)
try {
admin.lockNow()
if (prefs.isWipeData) admin.wipeData()
} catch (exc: SecurityException) {}
} }
} }

View File

@ -0,0 +1,101 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".fragment.RecastFragment">
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/enabled"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?attr/textAppearanceBodyLarge"
android:text="@string/enable" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/recast_description"
android:textAppearance="?attr/textAppearanceBodySmall" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginVertical="10dp"
android:background="?android:attr/listDivider" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/action"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/action">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</com.google.android.material.textfield.TextInputLayout>
<Space
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="5dp" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/receiver"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/receiver">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</com.google.android.material.textfield.TextInputLayout>
<Space
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="5dp" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/extraKey"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/extra_key">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</com.google.android.material.textfield.TextInputLayout>
<Space
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="5dp" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/extraValue"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/extra_value">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
</ScrollView>
</FrameLayout>

View File

@ -29,4 +29,9 @@
</group> </group>
<item
android:id="@+id/nav_recast"
android:title="@string/recast"
android:checkable="true" />
</menu> </menu>

View File

@ -44,4 +44,11 @@
<string name="telegram">Telegram</string> <string name="telegram">Telegram</string>
<string name="threema">Threema</string> <string name="threema">Threema</string>
<string name="session">Session</string> <string name="session">Session</string>
<string name="recast">Recast</string>
<string name="recast_description">Instead of wipe data you may want to send a broadcast message.</string>
<string name="enable">Enable</string>
<string name="action">action*</string>
<string name="receiver">receiver</string>
<string name="extra_key">extra key</string>
<string name="extra_value">extra value</string>
</resources> </resources>

View File

@ -0,0 +1 @@
recast option

View File

@ -1,5 +1,6 @@
You can use PanicKit, tile, shortcut or send a message with a secret code. On trigger, using You can use PanicKit, tile, shortcut or send a message with a secret code. On trigger, using
Device Administration API, it locks a device and optionally runs wipe. Device Administration API, it locks a device and optionally runs wipe (factory reset). Also it can
send a broadcast message instead of the wipe.
Also you can: Also you can:
* fire when a device was not unlocked for X time * fire when a device was not unlocked for X time