Skip to content

Commit 957bec3

Browse files
author
lucky
committed
recast option
1 parent 6fc8fa3 commit 957bec3

File tree

20 files changed

+295
-101
lines changed

20 files changed

+295
-101
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ Lock a device and wipe its data on emergency.
1919
You can use [PanicKit](https://guardianproject.info/code/panickit/), tile, shortcut or send a
2020
message with a secret code. On trigger, using
2121
[Device Administration API](https://developer.android.com/guide/topics/admin/device-admin), it
22-
locks a device and optionally runs wipe.
22+
locks a device and optionally runs wipe (factory reset). Also it can send a broadcast message
23+
instead of the wipe.
2324

2425
Also you can:
2526
* fire when a device was not unlocked for X time

app/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ android {
1010
applicationId "me.lucky.wasted"
1111
minSdk 23
1212
targetSdk 32
13-
versionCode 38
14-
versionName "1.5.9"
13+
versionCode 39
14+
versionName "1.5.10"
1515

1616
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
1717
}

app/src/main/java/me/lucky/wasted/MainActivity.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ open class MainActivity : AppCompatActivity() {
144144
R.id.nav_trigger_notification -> NotificationFragment()
145145
R.id.nav_trigger_lock -> LockFragment()
146146
R.id.nav_trigger_application -> ApplicationFragment()
147-
R.id.top_settings -> SettingsFragment()
147+
R.id.nav_recast -> RecastFragment()
148148
else -> MainFragment()
149149
}
150150

app/src/main/java/me/lucky/wasted/Preferences.kt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ class Preferences(ctx: Context, encrypted: Boolean = true) {
1919
private const val WIPE_DATA = "wipe_data"
2020
private const val WIPE_EMBEDDED_SIM = "wipe_embedded_sim"
2121

22+
private const val RECAST_ENABLED = "recast_enabled"
23+
private const val RECAST_ACTION = "recast_action"
24+
private const val RECAST_RECEIVER = "recast_receiver"
25+
private const val RECAST_EXTRA_KEY = "recast_extra_key"
26+
private const val RECAST_EXTRA_VALUE = "recast_extra_value"
27+
2228
private const val TRIGGERS = "triggers"
2329
private const val TRIGGER_LOCK_COUNT = "trigger_lock_count"
2430
private const val TRIGGER_TILE_DELAY = "trigger_tile_delay"
@@ -90,6 +96,26 @@ class Preferences(ctx: Context, encrypted: Boolean = true) {
9096
get() = prefs.getInt(TRIGGER_APPLICATION_OPTIONS, 0)
9197
set(value) = prefs.edit { putInt(TRIGGER_APPLICATION_OPTIONS, value) }
9298

99+
var isRecastEnabled: Boolean
100+
get() = prefs.getBoolean(RECAST_ENABLED, false)
101+
set(value) = prefs.edit { putBoolean(RECAST_ENABLED, value) }
102+
103+
var recastAction: String
104+
get() = prefs.getString(RECAST_ACTION, "") ?: ""
105+
set(value) = prefs.edit { putString(RECAST_ACTION, value) }
106+
107+
var recastReceiver: String
108+
get() = prefs.getString(RECAST_RECEIVER, "") ?: ""
109+
set(value) = prefs.edit { putString(RECAST_RECEIVER, value) }
110+
111+
var recastExtraKey: String
112+
get() = prefs.getString(RECAST_EXTRA_KEY, "") ?: ""
113+
set(value) = prefs.edit { putString(RECAST_EXTRA_KEY, value) }
114+
115+
var recastExtraValue: String
116+
get() = prefs.getString(RECAST_EXTRA_VALUE, "") ?: ""
117+
set(value) = prefs.edit { putString(RECAST_EXTRA_VALUE, value) }
118+
93119
fun registerListener(listener: SharedPreferences.OnSharedPreferenceChangeListener) =
94120
prefs.registerOnSharedPreferenceChangeListener(listener)
95121

app/src/main/java/me/lucky/wasted/Utils.kt

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package me.lucky.wasted
22

3+
import android.app.KeyguardManager
34
import android.content.ComponentName
45
import android.content.Context
56
import android.content.Intent
67
import android.content.pm.PackageManager
78
import android.os.Build
89
import androidx.core.content.ContextCompat
910

11+
import me.lucky.wasted.admin.DeviceAdminManager
1012
import me.lucky.wasted.trigger.notification.NotificationListenerService
1113
import me.lucky.wasted.trigger.panic.PanicConnectionActivity
1214
import me.lucky.wasted.trigger.panic.PanicResponderActivity
@@ -26,10 +28,10 @@ class Utils(private val ctx: Context) {
2628
}
2729
}
2830

29-
private val shortcut by lazy { ShortcutManager(ctx) }
31+
private val prefs by lazy { Preferences.new(ctx) }
3032

3133
fun setEnabled(enabled: Boolean) {
32-
val triggers = Preferences(ctx).triggers
34+
val triggers = prefs.triggers
3335
setPanicKitEnabled(enabled && triggers.and(Trigger.PANIC_KIT.value) != 0)
3436
setTileEnabled(enabled && triggers.and(Trigger.TILE.value) != 0)
3537
setShortcutEnabled(enabled && triggers.and(Trigger.SHORTCUT.value) != 0)
@@ -50,6 +52,7 @@ class Utils(private val ctx: Context) {
5052
}
5153

5254
fun setShortcutEnabled(enabled: Boolean) {
55+
val shortcut = ShortcutManager(ctx)
5356
if (!enabled) shortcut.remove()
5457
setComponentEnabled(ShortcutActivity::class.java, enabled)
5558
if (enabled) shortcut.push()
@@ -63,7 +66,6 @@ class Utils(private val ctx: Context) {
6366

6467
fun updateApplicationEnabled() {
6568
val prefix = "${ctx.packageName}.trigger.application"
66-
val prefs = Preferences(ctx)
6769
val options = prefs.triggerApplicationOptions
6870
val enabled = prefs.isEnabled && prefs.triggers.and(Trigger.APPLICATION.value) != 0
6971
setComponentEnabled(
@@ -85,7 +87,6 @@ class Utils(private val ctx: Context) {
8587
}
8688

8789
fun updateForegroundRequiredEnabled() {
88-
val prefs = Preferences(ctx)
8990
val enabled = prefs.isEnabled
9091
val triggers = prefs.triggers
9192
val isUSB = triggers.and(Trigger.USB.value) != 0
@@ -114,4 +115,36 @@ class Utils(private val ctx: Context) {
114115
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
115116
PackageManager.DONT_KILL_APP,
116117
)
118+
119+
fun fire(trigger: Trigger, safe: Boolean = true) {
120+
if (!prefs.isEnabled || prefs.triggers.and(trigger.value) == 0) return
121+
val admin = DeviceAdminManager(ctx)
122+
try {
123+
admin.lockNow()
124+
if (prefs.isWipeData && safe) admin.wipeData()
125+
} catch (exc: SecurityException) {}
126+
if (prefs.isRecastEnabled && safe) recast()
127+
}
128+
129+
fun isDeviceLocked() = ctx.getSystemService(KeyguardManager::class.java).isDeviceLocked
130+
131+
private fun recast() {
132+
val action = prefs.recastAction
133+
if (action.isEmpty()) return
134+
ctx.sendBroadcast(Intent(action).apply {
135+
val cls = prefs.recastReceiver.split('/')
136+
val packageName = cls.firstOrNull() ?: ""
137+
if (packageName.isNotEmpty()) {
138+
setPackage(packageName)
139+
if (cls.size == 2)
140+
setClassName(
141+
packageName,
142+
"$packageName.${cls[1].trimStart('.')}",
143+
)
144+
}
145+
addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
146+
val extraKey = prefs.recastExtraKey
147+
if (extraKey.isNotEmpty()) putExtra(extraKey, prefs.recastExtraValue)
148+
})
149+
}
117150
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package me.lucky.wasted.fragment
2+
3+
import android.content.Context
4+
import android.content.SharedPreferences
5+
import android.os.Bundle
6+
import android.view.LayoutInflater
7+
import android.view.View
8+
import android.view.ViewGroup
9+
import androidx.core.widget.doAfterTextChanged
10+
import androidx.fragment.app.Fragment
11+
12+
import me.lucky.wasted.Preferences
13+
import me.lucky.wasted.databinding.FragmentRecastBinding
14+
15+
class RecastFragment : Fragment() {
16+
private lateinit var binding: FragmentRecastBinding
17+
private lateinit var ctx: Context
18+
private lateinit var prefs: Preferences
19+
private lateinit var prefsdb: Preferences
20+
21+
private val prefsListener = SharedPreferences.OnSharedPreferenceChangeListener { _, key ->
22+
prefs.copyTo(prefsdb, key)
23+
}
24+
25+
override fun onCreateView(
26+
inflater: LayoutInflater,
27+
container: ViewGroup?,
28+
savedInstanceState: Bundle?,
29+
): View {
30+
binding = FragmentRecastBinding.inflate(inflater, container, false)
31+
init()
32+
setup()
33+
return binding.root
34+
}
35+
36+
override fun onStart() {
37+
super.onStart()
38+
prefs.registerListener(prefsListener)
39+
}
40+
41+
override fun onStop() {
42+
super.onStop()
43+
prefs.unregisterListener(prefsListener)
44+
}
45+
46+
private fun init() {
47+
ctx = requireContext()
48+
prefs = Preferences(ctx)
49+
prefsdb = Preferences(ctx, encrypted = false)
50+
binding.apply {
51+
enabled.isChecked = prefs.isRecastEnabled
52+
action.editText?.setText(prefs.recastAction)
53+
receiver.editText?.setText(prefs.recastReceiver)
54+
extraKey.editText?.setText(prefs.recastExtraKey)
55+
extraValue.editText?.setText(prefs.recastExtraValue)
56+
}
57+
}
58+
59+
private fun setup() = binding.apply {
60+
enabled.setOnCheckedChangeListener { _, isChecked ->
61+
prefs.isRecastEnabled = isChecked
62+
}
63+
action.editText?.doAfterTextChanged {
64+
prefs.recastAction = it?.toString()?.trim() ?: return@doAfterTextChanged
65+
}
66+
receiver.editText?.doAfterTextChanged {
67+
prefs.recastReceiver = it?.toString()?.trim() ?: return@doAfterTextChanged
68+
}
69+
extraKey.editText?.doAfterTextChanged {
70+
prefs.recastExtraKey = it?.toString()?.trim() ?: return@doAfterTextChanged
71+
}
72+
extraValue.editText?.doAfterTextChanged {
73+
prefs.recastExtraValue = it?.toString()?.trim() ?: return@doAfterTextChanged
74+
}
75+
}
76+
}

app/src/main/java/me/lucky/wasted/trigger/application/ApplicationActivity.kt

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,13 @@ package me.lucky.wasted.trigger.application
33
import android.os.Bundle
44
import androidx.appcompat.app.AppCompatActivity
55

6-
import me.lucky.wasted.Preferences
76
import me.lucky.wasted.Trigger
8-
import me.lucky.wasted.admin.DeviceAdminManager
7+
import me.lucky.wasted.Utils
98

109
class ApplicationActivity : AppCompatActivity() {
11-
private val prefs by lazy { Preferences(this) }
12-
private val admin by lazy { DeviceAdminManager(this) }
13-
1410
override fun onCreate(savedInstanceState: Bundle?) {
1511
super.onCreate(savedInstanceState)
16-
if (!prefs.isEnabled || prefs.triggers.and(Trigger.APPLICATION.value) == 0) {
17-
finishAndRemoveTask()
18-
return
19-
}
20-
try {
21-
admin.lockNow()
22-
if (prefs.isWipeData) admin.wipeData()
23-
} catch (exc: SecurityException) {}
12+
Utils(this).fire(Trigger.APPLICATION)
2413
finishAndRemoveTask()
2514
}
2615
}

app/src/main/java/me/lucky/wasted/trigger/broadcast/BroadcastReceiver.kt

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,30 +6,23 @@ import android.content.Intent
66

77
import me.lucky.wasted.Preferences
88
import me.lucky.wasted.Trigger
9-
import me.lucky.wasted.admin.DeviceAdminManager
9+
import me.lucky.wasted.Utils
1010

1111
class BroadcastReceiver : BroadcastReceiver() {
1212
companion object {
1313
const val KEY = "code"
1414
const val ACTION = "me.lucky.wasted.action.TRIGGER"
1515

16-
fun panic(context: Context, intent: Intent?) {
16+
fun panic(context: Context, intent: Intent?, trigger: Trigger) {
1717
if (intent?.action != ACTION) return
18-
val prefs = Preferences.new(context)
19-
if (!prefs.isEnabled) return
20-
val secret = prefs.secret
18+
val secret = Preferences.new(context).secret
2119
assert(secret.isNotEmpty())
2220
if (intent.getStringExtra(KEY)?.trim() != secret) return
23-
val admin = DeviceAdminManager(context)
24-
try {
25-
admin.lockNow()
26-
if (prefs.isWipeData) admin.wipeData()
27-
} catch (exc: SecurityException) {}
21+
Utils(context).fire(trigger)
2822
}
2923
}
3024

3125
override fun onReceive(context: Context?, intent: Intent?) {
32-
if (Preferences.new(context ?: return).triggers.and(Trigger.BROADCAST.value) != 0)
33-
panic(context, intent)
26+
panic(context ?: return, intent, Trigger.BROADCAST)
3427
}
3528
}

app/src/main/java/me/lucky/wasted/trigger/lock/LockJobService.kt

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,12 @@ package me.lucky.wasted.trigger.lock
33
import android.app.job.JobParameters
44
import android.app.job.JobService
55

6-
import me.lucky.wasted.admin.DeviceAdminManager
7-
import me.lucky.wasted.Preferences
86
import me.lucky.wasted.Trigger
7+
import me.lucky.wasted.Utils
98

109
class LockJobService : JobService() {
1110
override fun onStartJob(params: JobParameters?): Boolean {
12-
val prefs = Preferences.new(this)
13-
if (!prefs.isEnabled || prefs.triggers.and(Trigger.LOCK.value) == 0) return false
14-
val admin = DeviceAdminManager(this)
15-
try {
16-
admin.lockNow()
17-
if (prefs.isWipeData) admin.wipeData()
18-
} catch (exc: SecurityException) {}
11+
Utils(this).fire(Trigger.LOCK)
1912
return false
2013
}
2114

app/src/main/java/me/lucky/wasted/trigger/notification/NotificationListenerService.kt

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@ import android.os.Build
55
import android.service.notification.NotificationListenerService
66
import android.service.notification.StatusBarNotification
77

8-
import me.lucky.wasted.admin.DeviceAdminManager
98
import me.lucky.wasted.Preferences
109
import me.lucky.wasted.Trigger
10+
import me.lucky.wasted.Utils
1111

1212
class NotificationListenerService : NotificationListenerService() {
1313
private lateinit var prefs: Preferences
14-
private lateinit var admin: DeviceAdminManager
14+
private lateinit var utils: Utils
1515

1616
override fun onCreate() {
1717
super.onCreate()
@@ -20,22 +20,17 @@ class NotificationListenerService : NotificationListenerService() {
2020

2121
private fun init() {
2222
prefs = Preferences.new(this)
23-
admin = DeviceAdminManager(this)
23+
utils = Utils(this)
2424
}
2525

2626
override fun onNotificationPosted(sbn: StatusBarNotification?) {
2727
super.onNotificationPosted(sbn)
28-
if (sbn == null ||
29-
!prefs.isEnabled ||
30-
prefs.triggers.and(Trigger.NOTIFICATION.value) == 0) return
28+
if (sbn == null) return
3129
val secret = prefs.secret
3230
assert(secret.isNotEmpty())
33-
if (sbn.notification.extras[Notification.EXTRA_TEXT]?.toString() != secret) return
31+
if (sbn.notification.extras[Notification.EXTRA_TEXT]?.toString()?.trim() != secret) return
3432
cancelAllNotifications()
35-
try {
36-
admin.lockNow()
37-
if (prefs.isWipeData) admin.wipeData()
38-
} catch (exc: SecurityException) {}
33+
utils.fire(Trigger.NOTIFICATION)
3934
}
4035

4136
override fun onListenerConnected() {

0 commit comments

Comments
 (0)