mirror of
https://codeberg.org/timelimit/timelimit-android.git
synced 2025-10-03 09:49:25 +02:00
New concept: annoying after a manipulation
This commit is contained in:
parent
c3b9efa37a
commit
393a9104ae
23 changed files with 298 additions and 531 deletions
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!--
|
<!--
|
||||||
TimeLimit Copyright <C> 2019 - 2021 Jonas Lochmann
|
TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
the Free Software Foundation version 3 of the License.
|
the Free Software Foundation version 3 of the License.
|
||||||
|
@ -74,15 +74,6 @@
|
||||||
android:taskAffinity=":lock"
|
android:taskAffinity=":lock"
|
||||||
tools:ignore="UnusedAttribute" />
|
tools:ignore="UnusedAttribute" />
|
||||||
|
|
||||||
<activity
|
|
||||||
tools:ignore="UnusedAttribute"
|
|
||||||
android:excludeFromRecents="true"
|
|
||||||
android:autoRemoveFromRecents="true"
|
|
||||||
android:taskAffinity=":manipulationwarning"
|
|
||||||
android:showOnLockScreen="true"
|
|
||||||
android:exported="false"
|
|
||||||
android:name=".ui.manipulation.UnlockAfterManipulationActivity" />
|
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
tools:ignore="UnusedAttribute"
|
tools:ignore="UnusedAttribute"
|
||||||
android:excludeFromRecents="true"
|
android:excludeFromRecents="true"
|
||||||
|
|
|
@ -214,9 +214,6 @@ abstract class ConfigDao {
|
||||||
updateValueSync(ConfigurationItemType.ShownHints, null)
|
updateValueSync(ConfigurationItemType.ShownHints, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun wasDeviceLockedSync() = getValueOfKeySync(ConfigurationItemType.WasDeviceLocked) == "true"
|
|
||||||
fun setWasDeviceLockedSync(value: Boolean) = updateValueSync(ConfigurationItemType.WasDeviceLocked, if (value) "true" else "false")
|
|
||||||
|
|
||||||
fun getLastAppVersionWhichSynced() = getValueOfKeySync(ConfigurationItemType.LastAppVersionWhichSynced)
|
fun getLastAppVersionWhichSynced() = getValueOfKeySync(ConfigurationItemType.LastAppVersionWhichSynced)
|
||||||
fun setLastAppVersionWhichSynced(version: String) = updateValueSync(ConfigurationItemType.LastAppVersionWhichSynced, version)
|
fun setLastAppVersionWhichSynced(version: String) = updateValueSync(ConfigurationItemType.LastAppVersionWhichSynced, version)
|
||||||
|
|
||||||
|
|
|
@ -84,7 +84,7 @@ enum class ConfigurationItemType {
|
||||||
DeviceAuthToken,
|
DeviceAuthToken,
|
||||||
FullVersionUntil,
|
FullVersionUntil,
|
||||||
ShownHints,
|
ShownHints,
|
||||||
WasDeviceLocked,
|
ObsoleteWasDeviceLocked,
|
||||||
LastAppVersionWhichSynced,
|
LastAppVersionWhichSynced,
|
||||||
LastScreenOnTime,
|
LastScreenOnTime,
|
||||||
ServerMessage,
|
ServerMessage,
|
||||||
|
@ -135,7 +135,7 @@ object ConfigurationItemTypeUtil {
|
||||||
ConfigurationItemType.DeviceAuthToken,
|
ConfigurationItemType.DeviceAuthToken,
|
||||||
ConfigurationItemType.FullVersionUntil,
|
ConfigurationItemType.FullVersionUntil,
|
||||||
ConfigurationItemType.ShownHints,
|
ConfigurationItemType.ShownHints,
|
||||||
ConfigurationItemType.WasDeviceLocked,
|
ConfigurationItemType.ObsoleteWasDeviceLocked,
|
||||||
ConfigurationItemType.LastAppVersionWhichSynced,
|
ConfigurationItemType.LastAppVersionWhichSynced,
|
||||||
ConfigurationItemType.LastScreenOnTime,
|
ConfigurationItemType.LastScreenOnTime,
|
||||||
ConfigurationItemType.ServerMessage,
|
ConfigurationItemType.ServerMessage,
|
||||||
|
@ -161,7 +161,7 @@ object ConfigurationItemTypeUtil {
|
||||||
ConfigurationItemType.DeviceAuthToken -> DEVICE_AUTH_TOKEN
|
ConfigurationItemType.DeviceAuthToken -> DEVICE_AUTH_TOKEN
|
||||||
ConfigurationItemType.FullVersionUntil -> FULL_VERSION_UNTIL
|
ConfigurationItemType.FullVersionUntil -> FULL_VERSION_UNTIL
|
||||||
ConfigurationItemType.ShownHints -> SHOWN_HINTS
|
ConfigurationItemType.ShownHints -> SHOWN_HINTS
|
||||||
ConfigurationItemType.WasDeviceLocked -> WAS_DEVICE_LOCKED
|
ConfigurationItemType.ObsoleteWasDeviceLocked -> WAS_DEVICE_LOCKED
|
||||||
ConfigurationItemType.LastAppVersionWhichSynced -> LAST_APP_VERSION_WHICH_SYNCED
|
ConfigurationItemType.LastAppVersionWhichSynced -> LAST_APP_VERSION_WHICH_SYNCED
|
||||||
ConfigurationItemType.LastScreenOnTime -> LAST_SCREEN_ON_TIME
|
ConfigurationItemType.LastScreenOnTime -> LAST_SCREEN_ON_TIME
|
||||||
ConfigurationItemType.ServerMessage -> SERVER_MESSAGE
|
ConfigurationItemType.ServerMessage -> SERVER_MESSAGE
|
||||||
|
@ -187,7 +187,7 @@ object ConfigurationItemTypeUtil {
|
||||||
DEVICE_AUTH_TOKEN -> ConfigurationItemType.DeviceAuthToken
|
DEVICE_AUTH_TOKEN -> ConfigurationItemType.DeviceAuthToken
|
||||||
FULL_VERSION_UNTIL -> ConfigurationItemType.FullVersionUntil
|
FULL_VERSION_UNTIL -> ConfigurationItemType.FullVersionUntil
|
||||||
SHOWN_HINTS -> ConfigurationItemType.ShownHints
|
SHOWN_HINTS -> ConfigurationItemType.ShownHints
|
||||||
WAS_DEVICE_LOCKED -> ConfigurationItemType.WasDeviceLocked
|
WAS_DEVICE_LOCKED -> ConfigurationItemType.ObsoleteWasDeviceLocked
|
||||||
LAST_APP_VERSION_WHICH_SYNCED -> ConfigurationItemType.LastAppVersionWhichSynced
|
LAST_APP_VERSION_WHICH_SYNCED -> ConfigurationItemType.LastAppVersionWhichSynced
|
||||||
LAST_SCREEN_ON_TIME -> ConfigurationItemType.LastScreenOnTime
|
LAST_SCREEN_ON_TIME -> ConfigurationItemType.LastScreenOnTime
|
||||||
SERVER_MESSAGE -> ConfigurationItemType.ServerMessage
|
SERVER_MESSAGE -> ConfigurationItemType.ServerMessage
|
||||||
|
@ -227,10 +227,9 @@ object HintsToShow {
|
||||||
}
|
}
|
||||||
|
|
||||||
object ExperimentalFlags {
|
object ExperimentalFlags {
|
||||||
const val DISABLE_BLOCK_ON_MANIPULATION = 1L
|
private const val OBSOLETE_DISABLE_BLOCK_ON_MANIPULATION = 1L
|
||||||
const val SYSTEM_LEVEL_BLOCKING = 2L
|
const val SYSTEM_LEVEL_BLOCKING = 2L
|
||||||
const val MANIPULATION_ANNOY_USER_ONLY = 4L
|
private const val OBSOLETE_MANIPULATION_ANNOY_USER_ONLY = 4L
|
||||||
const val MANIPULATION_ANNOY_USER = MANIPULATION_ANNOY_USER_ONLY or DISABLE_BLOCK_ON_MANIPULATION // otherwise there would be a conflict between both features
|
|
||||||
const val IGNORE_SYSTEM_CONNECTION_STATUS = 8L
|
const val IGNORE_SYSTEM_CONNECTION_STATUS = 8L
|
||||||
const val CUSTOM_HOME_SCREEN = 16L
|
const val CUSTOM_HOME_SCREEN = 16L
|
||||||
const val CUSTOM_HOMESCREEN_DELAY = 32L
|
const val CUSTOM_HOMESCREEN_DELAY = 32L
|
||||||
|
|
|
@ -44,7 +44,7 @@ abstract class PlatformIntegration(
|
||||||
abstract fun showOverlayMessage(text: String)
|
abstract fun showOverlayMessage(text: String)
|
||||||
|
|
||||||
abstract fun showAppLockScreen(currentPackageName: String, currentActivityName: String?)
|
abstract fun showAppLockScreen(currentPackageName: String, currentActivityName: String?)
|
||||||
abstract fun showAnnoyScreen(annoyDuration: Long)
|
abstract fun showAnnoyScreen()
|
||||||
// true = success
|
// true = success
|
||||||
abstract suspend fun muteAudioIfPossible(packageName: String): Boolean
|
abstract suspend fun muteAudioIfPossible(packageName: String): Boolean
|
||||||
abstract fun setShowBlockingOverlay(show: Boolean, blockedElement: String? = null)
|
abstract fun setShowBlockingOverlay(show: Boolean, blockedElement: String? = null)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* TimeLimit Copyright <C> 2019 - 2020 Jonas Lochmann
|
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -18,7 +18,6 @@ package io.timelimit.android.integration.platform.android
|
||||||
import android.app.admin.DeviceAdminReceiver
|
import android.app.admin.DeviceAdminReceiver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.UserHandle
|
|
||||||
import io.timelimit.android.R
|
import io.timelimit.android.R
|
||||||
import io.timelimit.android.coroutines.runAsync
|
import io.timelimit.android.coroutines.runAsync
|
||||||
import io.timelimit.android.logic.DefaultAppLogic
|
import io.timelimit.android.logic.DefaultAppLogic
|
||||||
|
@ -49,16 +48,4 @@ class AdminReceiver: DeviceAdminReceiver() {
|
||||||
|
|
||||||
return context.getString(R.string.admin_disable_warning)
|
return context.getString(R.string.admin_disable_warning)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPasswordSucceeded(context: Context, intent: Intent) {
|
|
||||||
super.onPasswordSucceeded(context, intent)
|
|
||||||
|
|
||||||
DefaultAppLogic.with(context).manipulationLogic.reportManualUnlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPasswordSucceeded(context: Context, intent: Intent, user: UserHandle) {
|
|
||||||
super.onPasswordSucceeded(context, intent, user)
|
|
||||||
|
|
||||||
DefaultAppLogic.with(context).manipulationLogic.reportManualUnlock()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -261,8 +261,8 @@ class AndroidIntegration(context: Context): PlatformIntegration(maximumProtectio
|
||||||
LockActivity.start(context, currentPackageName, currentActivityName)
|
LockActivity.start(context, currentPackageName, currentActivityName)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showAnnoyScreen(annoyDuration: Long) {
|
override fun showAnnoyScreen() {
|
||||||
AnnoyActivity.start(context, annoyDuration)
|
AnnoyActivity.start(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun muteAudioIfPossible(packageName: String): Boolean {
|
override suspend fun muteAudioIfPossible(packageName: String): Boolean {
|
||||||
|
|
|
@ -91,7 +91,7 @@ class DummyIntegration(
|
||||||
launchLockScreenForPackage = currentPackageName
|
launchLockScreenForPackage = currentPackageName
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showAnnoyScreen(annoyDuration: Long) {
|
override fun showAnnoyScreen() {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
107
app/src/main/java/io/timelimit/android/logic/AnnoyLogic.kt
Normal file
107
app/src/main/java/io/timelimit/android/logic/AnnoyLogic.kt
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
/*
|
||||||
|
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation version 3 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package io.timelimit.android.logic
|
||||||
|
|
||||||
|
import android.os.Build
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.lifecycle.Observer
|
||||||
|
import io.timelimit.android.BuildConfig
|
||||||
|
import io.timelimit.android.async.Threads
|
||||||
|
import io.timelimit.android.integration.platform.ProtectionLevel
|
||||||
|
import io.timelimit.android.livedata.*
|
||||||
|
|
||||||
|
class AnnoyLogic (val appLogic: AppLogic) {
|
||||||
|
// config
|
||||||
|
companion object {
|
||||||
|
private const val ENABLE = !BuildConfig.storeCompilant
|
||||||
|
|
||||||
|
private const val TEMP_UNBLOCK_DURATION = 1000 * 45L
|
||||||
|
private const val TEMP_UNBLOCK_PARENT_DURATION = 1000 * 60 * 10L
|
||||||
|
private const val MAX_BLOCK_DURATION = 15
|
||||||
|
|
||||||
|
private fun manualUnblockDelay(counter: Int): Long {
|
||||||
|
return if (counter <= 0) 0
|
||||||
|
else (counter + 4).coerceAtMost(MAX_BLOCK_DURATION).toLong() * 1000 * 60
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// input: clock
|
||||||
|
private fun now() = appLogic.timeApi.getCurrentUptimeInMillis()
|
||||||
|
|
||||||
|
// input: is manipulated (bool)
|
||||||
|
private val isManipulated = appLogic.deviceEntryIfEnabled.map { it?.hasActiveManipulationWarning ?: false }
|
||||||
|
private val isDeviceOwner = appLogic.deviceEntryIfEnabled.map { it?.currentProtectionLevel == ProtectionLevel.DeviceOwner }
|
||||||
|
private val enableAnnoyNow = if (ENABLE) isManipulated.and(isDeviceOwner) else liveDataFromNonNullValue(false)
|
||||||
|
|
||||||
|
private val annoyTempDisabled = MutableLiveData<Boolean>().apply { value = false }
|
||||||
|
private val annoyTempDisabledSetFalse: Runnable = Runnable {
|
||||||
|
annoyTempDisabled.value = false
|
||||||
|
isManipulated.removeObserver(resetTempDisabledObserver)
|
||||||
|
}
|
||||||
|
private val resetTempDisabledObserver = Observer<Boolean> { isManipulated ->
|
||||||
|
if (!isManipulated) {
|
||||||
|
Threads.mainThreadHandler.removeCallbacks(annoyTempDisabledSetFalse)
|
||||||
|
Threads.mainThreadHandler.post(annoyTempDisabledSetFalse)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// output: should block right now
|
||||||
|
val shouldAnnoyRightNow = enableAnnoyNow.and(annoyTempDisabled.invert())
|
||||||
|
|
||||||
|
// state: block duration
|
||||||
|
private var nextManualUnblockTimestamp = now()
|
||||||
|
private var manualUnblockCounter = 0
|
||||||
|
|
||||||
|
// output: duration until next manual unblock
|
||||||
|
val nextManualUnblockCountdown = liveDataFromFunction {
|
||||||
|
(nextManualUnblockTimestamp - now()).coerceAtLeast(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// input: trigger temp unblock (event)
|
||||||
|
fun doManualTempUnlock() {
|
||||||
|
val now = now()
|
||||||
|
|
||||||
|
if (now < nextManualUnblockTimestamp) return
|
||||||
|
if (annoyTempDisabled.value == true) return
|
||||||
|
|
||||||
|
// eventually reset
|
||||||
|
if (nextManualUnblockTimestamp + manualUnblockDelay(manualUnblockCounter) < now) {
|
||||||
|
manualUnblockCounter = 1
|
||||||
|
} else {
|
||||||
|
manualUnblockCounter += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
nextManualUnblockTimestamp = now + manualUnblockDelay(manualUnblockCounter)
|
||||||
|
|
||||||
|
enableTempDisabled(TEMP_UNBLOCK_DURATION)
|
||||||
|
}
|
||||||
|
|
||||||
|
// input: trigger temp unblock by parents (event)
|
||||||
|
fun doParentTempUnlock() {
|
||||||
|
manualUnblockCounter = 0
|
||||||
|
nextManualUnblockTimestamp = now()
|
||||||
|
|
||||||
|
enableTempDisabled(TEMP_UNBLOCK_PARENT_DURATION)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun enableTempDisabled(duration: Long) {
|
||||||
|
annoyTempDisabled.value = true
|
||||||
|
|
||||||
|
Threads.mainThreadHandler.removeCallbacks(annoyTempDisabledSetFalse)
|
||||||
|
Threads.mainThreadHandler.postDelayed(annoyTempDisabledSetFalse, duration)
|
||||||
|
isManipulated.observeForever(resetTempDisabledObserver)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* TimeLimit Copyright <C> 2019 - 2021 Jonas Lochmann
|
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -104,8 +104,8 @@ class AppLogic(
|
||||||
WatchdogLogic(this)
|
WatchdogLogic(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
val manipulationLogic = ManipulationLogic(this)
|
|
||||||
val suspendAppsLogic = SuspendAppsLogic(this)
|
val suspendAppsLogic = SuspendAppsLogic(this)
|
||||||
|
val annoyLogic = AnnoyLogic(this)
|
||||||
|
|
||||||
fun shutdown() {
|
fun shutdown() {
|
||||||
enable.value = false
|
enable.value = false
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* TimeLimit Copyright <C> 2019 - 2021 Jonas Lochmann
|
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -54,6 +54,7 @@ import io.timelimit.android.work.PeriodicSyncInBackgroundWorker
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
|
import kotlinx.coroutines.withTimeoutOrNull
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class BackgroundTaskLogic(val appLogic: AppLogic) {
|
class BackgroundTaskLogic(val appLogic: AppLogic) {
|
||||||
|
@ -983,65 +984,15 @@ class BackgroundTaskLogic(val appLogic: AppLogic) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// first time: annoy for 20 seconds; free for 5 minutes
|
|
||||||
// second time: annoy for 30 seconds; free for 2 minutes
|
|
||||||
// third time: annoy for 1 minute; free for 1 minute
|
|
||||||
// then: annoy for 2 minutes; free for 1 minute
|
|
||||||
private suspend fun annoyUserOnManipulationLoop() {
|
private suspend fun annoyUserOnManipulationLoop() {
|
||||||
val isManipulated = appLogic.deviceEntryIfEnabled.map { it?.hasActiveManipulationWarning ?: false }
|
val shouldAnnoyNow = appLogic.annoyLogic.shouldAnnoyRightNow
|
||||||
val enableAnnoy = appLogic.database.config().isExperimentalFlagsSetAsync(ExperimentalFlags.MANIPULATION_ANNOY_USER)
|
|
||||||
val timeLimitNotActive = IsAppInForeground.isRunning.invert()
|
|
||||||
|
|
||||||
var counter = 0
|
|
||||||
var globalCounter = 0
|
|
||||||
|
|
||||||
val shouldAnnoyNow = isManipulated.and(enableAnnoy).and(timeLimitNotActive)
|
|
||||||
|
|
||||||
if (BuildConfig.DEBUG) {
|
|
||||||
Log.d(LOG_TAG, "delay before enabling annoying")
|
|
||||||
}
|
|
||||||
|
|
||||||
delay(1000 * 15)
|
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (BuildConfig.DEBUG) {
|
|
||||||
Log.d(LOG_TAG, "wait until should annoy")
|
|
||||||
}
|
|
||||||
|
|
||||||
shouldAnnoyNow.waitUntilValueMatches { it == true }
|
shouldAnnoyNow.waitUntilValueMatches { it == true }
|
||||||
|
appLogic.platformIntegration.showAnnoyScreen()
|
||||||
|
|
||||||
val annoyDurationInSeconds = when (counter) {
|
// bring into foreground all five seconds
|
||||||
0 -> 20
|
withTimeoutOrNull(5000L) { shouldAnnoyNow.waitUntilValueMatches { it == false } }
|
||||||
1 -> 30
|
|
||||||
2 -> 60
|
|
||||||
else -> 120
|
|
||||||
}
|
|
||||||
|
|
||||||
val freeDurationInSeconds = when (counter) {
|
|
||||||
0 -> 5 * 60
|
|
||||||
1 -> 2 * 60
|
|
||||||
else -> 60
|
|
||||||
}
|
|
||||||
|
|
||||||
if (BuildConfig.DEBUG) {
|
|
||||||
Log.d(LOG_TAG, "annoy for $annoyDurationInSeconds seconds; free for $freeDurationInSeconds seconds")
|
|
||||||
}
|
|
||||||
|
|
||||||
appLogic.platformIntegration.showAnnoyScreen(annoyDurationInSeconds.toLong())
|
|
||||||
|
|
||||||
counter++
|
|
||||||
globalCounter++
|
|
||||||
|
|
||||||
// reset counter if there was nothing for one hour
|
|
||||||
val globalCounterBackup = globalCounter
|
|
||||||
appLogic.timeApi.runDelayed(Runnable {
|
|
||||||
if (globalCounter == globalCounterBackup) {
|
|
||||||
counter = 0
|
|
||||||
}
|
|
||||||
}, 1000 * 60 * 60 /* 1 hour */)
|
|
||||||
|
|
||||||
// wait before annoying next time
|
|
||||||
delay((annoyDurationInSeconds + freeDurationInSeconds) * 1000L)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,99 +0,0 @@
|
||||||
/*
|
|
||||||
* TimeLimit Copyright <C> 2019 - 2020 Jonas Lochmann
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation version 3 of the License.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package io.timelimit.android.logic
|
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Build
|
|
||||||
import io.timelimit.android.async.Threads
|
|
||||||
import io.timelimit.android.coroutines.executeAndWait
|
|
||||||
import io.timelimit.android.coroutines.runAsync
|
|
||||||
import io.timelimit.android.data.model.ExperimentalFlags
|
|
||||||
import io.timelimit.android.ui.MainActivity
|
|
||||||
import io.timelimit.android.ui.manipulation.UnlockAfterManipulationActivity
|
|
||||||
|
|
||||||
class ManipulationLogic(val appLogic: AppLogic) {
|
|
||||||
companion object {
|
|
||||||
private const val LOG_TAG = "ManipulationLogic"
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
runAsync {
|
|
||||||
Threads.database.executeAndWait {
|
|
||||||
if (appLogic.database.config().wasDeviceLockedSync()) {
|
|
||||||
showManipulationScreen()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun lockDeviceSync() {
|
|
||||||
if (!appLogic.database.config().isExperimentalFlagsSetSync(ExperimentalFlags.DISABLE_BLOCK_ON_MANIPULATION)) {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
|
||||||
if (appLogic.platformIntegration.setLockTaskPackages(listOf(appLogic.context.packageName))) {
|
|
||||||
appLogic.database.config().setWasDeviceLockedSync(true)
|
|
||||||
|
|
||||||
showManipulationScreen()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (lockDeviceSync("12timelimit34")) {
|
|
||||||
appLogic.database.config().setWasDeviceLockedSync(true)
|
|
||||||
|
|
||||||
showManipulationScreen()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun lockDeviceSync(password: String) = appLogic.platformIntegration.trySetLockScreenPassword(password)
|
|
||||||
|
|
||||||
private fun showManipulationScreen() {
|
|
||||||
appLogic.context.startActivity(
|
|
||||||
Intent(appLogic.context, UnlockAfterManipulationActivity::class.java)
|
|
||||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun showManipulationUnlockedScreen() {
|
|
||||||
appLogic.context.startActivity(
|
|
||||||
Intent(appLogic.context, MainActivity::class.java)
|
|
||||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun unlockDeviceSync() {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
|
||||||
appLogic.database.config().setWasDeviceLockedSync(false)
|
|
||||||
} else {
|
|
||||||
if (lockDeviceSync("")) {
|
|
||||||
appLogic.database.config().setWasDeviceLockedSync(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun reportManualUnlock() {
|
|
||||||
Threads.database.execute {
|
|
||||||
appLogic.database.runInTransaction {
|
|
||||||
if (appLogic.database.config().getOwnDeviceIdSync() != null) {
|
|
||||||
if (appLogic.database.config().wasDeviceLockedSync()) {
|
|
||||||
appLogic.database.config().setWasDeviceLockedSync(false)
|
|
||||||
|
|
||||||
showManipulationUnlockedScreen()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* TimeLimit Copyright <C> 2019 - 2020 Jonas Lochmann
|
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -27,7 +27,6 @@ import io.timelimit.android.data.model.PendingSyncActionType
|
||||||
import io.timelimit.android.data.model.UserType
|
import io.timelimit.android.data.model.UserType
|
||||||
import io.timelimit.android.integration.platform.PlatformIntegration
|
import io.timelimit.android.integration.platform.PlatformIntegration
|
||||||
import io.timelimit.android.logic.AppLogic
|
import io.timelimit.android.logic.AppLogic
|
||||||
import io.timelimit.android.logic.ManipulationLogic
|
|
||||||
import io.timelimit.android.sync.SyncUtil
|
import io.timelimit.android.sync.SyncUtil
|
||||||
import io.timelimit.android.sync.actions.*
|
import io.timelimit.android.sync.actions.*
|
||||||
import io.timelimit.android.sync.actions.dispatch.LocalDatabaseAppLogicActionDispatcher
|
import io.timelimit.android.sync.actions.dispatch.LocalDatabaseAppLogicActionDispatcher
|
||||||
|
@ -48,7 +47,6 @@ object ApplyActionUtil {
|
||||||
action = action,
|
action = action,
|
||||||
database = appLogic.database,
|
database = appLogic.database,
|
||||||
syncUtil = appLogic.syncUtil,
|
syncUtil = appLogic.syncUtil,
|
||||||
manipulationLogic = appLogic.manipulationLogic,
|
|
||||||
ignoreIfDeviceIsNotConfigured = ignoreIfDeviceIsNotConfigured
|
ignoreIfDeviceIsNotConfigured = ignoreIfDeviceIsNotConfigured
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -57,7 +55,6 @@ object ApplyActionUtil {
|
||||||
action: AppLogicAction,
|
action: AppLogicAction,
|
||||||
database: Database,
|
database: Database,
|
||||||
syncUtil: SyncUtil,
|
syncUtil: SyncUtil,
|
||||||
manipulationLogic: ManipulationLogic,
|
|
||||||
ignoreIfDeviceIsNotConfigured: Boolean
|
ignoreIfDeviceIsNotConfigured: Boolean
|
||||||
) {
|
) {
|
||||||
// uncomment this if you need to know what's dispatching an action
|
// uncomment this if you need to know what's dispatching an action
|
||||||
|
@ -79,7 +76,7 @@ object ApplyActionUtil {
|
||||||
return@runInTransaction
|
return@runInTransaction
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalDatabaseAppLogicActionDispatcher.dispatchAppLogicActionSync(action, ownDeviceId!!, database, manipulationLogic)
|
LocalDatabaseAppLogicActionDispatcher.dispatchAppLogicActionSync(action, ownDeviceId!!, database)
|
||||||
|
|
||||||
if (isSyncEnabled(database)) {
|
if (isSyncEnabled(database)) {
|
||||||
if (action is AddUsedTimeActionVersion2) {
|
if (action is AddUsedTimeActionVersion2) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* TimeLimit Copyright <C> 2019 - 2021 Jonas Lochmann
|
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -24,13 +24,12 @@ import io.timelimit.android.integration.platform.NewPermissionStatusUtil
|
||||||
import io.timelimit.android.integration.platform.ProtectionLevelUtil
|
import io.timelimit.android.integration.platform.ProtectionLevelUtil
|
||||||
import io.timelimit.android.integration.platform.RuntimePermissionStatusUtil
|
import io.timelimit.android.integration.platform.RuntimePermissionStatusUtil
|
||||||
import io.timelimit.android.logic.BackgroundTaskLogic
|
import io.timelimit.android.logic.BackgroundTaskLogic
|
||||||
import io.timelimit.android.logic.ManipulationLogic
|
|
||||||
import io.timelimit.android.sync.actions.*
|
import io.timelimit.android.sync.actions.*
|
||||||
|
|
||||||
object LocalDatabaseAppLogicActionDispatcher {
|
object LocalDatabaseAppLogicActionDispatcher {
|
||||||
private const val LOG_TAG = "AppLogicAction"
|
private const val LOG_TAG = "AppLogicAction"
|
||||||
|
|
||||||
fun dispatchAppLogicActionSync(action: AppLogicAction, deviceId: String, database: Database, manipulationLogic: ManipulationLogic) {
|
fun dispatchAppLogicActionSync(action: AppLogicAction, deviceId: String, database: Database) {
|
||||||
DatabaseValidation.assertDeviceExists(database, deviceId)
|
DatabaseValidation.assertDeviceExists(database, deviceId)
|
||||||
|
|
||||||
database.runInTransaction {
|
database.runInTransaction {
|
||||||
|
@ -308,10 +307,6 @@ object LocalDatabaseAppLogicActionDispatcher {
|
||||||
|
|
||||||
database.device().updateDeviceEntry(device)
|
database.device().updateDeviceEntry(device)
|
||||||
|
|
||||||
if (device.hasActiveManipulationWarning) {
|
|
||||||
manipulationLogic.lockDeviceSync()
|
|
||||||
}
|
|
||||||
|
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
is TriedDisablingDeviceAdminAction -> {
|
is TriedDisablingDeviceAdminAction -> {
|
||||||
|
@ -323,8 +318,6 @@ object LocalDatabaseAppLogicActionDispatcher {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
manipulationLogic.lockDeviceSync()
|
|
||||||
|
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
is SignOutAtDeviceAction -> {
|
is SignOutAtDeviceAction -> {
|
||||||
|
|
|
@ -116,14 +116,6 @@ data class DiagnoseExperimentalFlagItem(
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
val items = listOf(
|
val items = listOf(
|
||||||
DiagnoseExperimentalFlagItem(
|
|
||||||
label = R.string.diagnose_exf_lom,
|
|
||||||
enableFlags = ExperimentalFlags.DISABLE_BLOCK_ON_MANIPULATION,
|
|
||||||
disableFlags = ExperimentalFlags.DISABLE_BLOCK_ON_MANIPULATION,
|
|
||||||
enable = { flags ->
|
|
||||||
(!BuildConfig.storeCompilant) and ((flags and ExperimentalFlags.MANIPULATION_ANNOY_USER_ONLY) == 0L)
|
|
||||||
}
|
|
||||||
),
|
|
||||||
DiagnoseExperimentalFlagItem(
|
DiagnoseExperimentalFlagItem(
|
||||||
label = R.string.diagnose_exf_slb,
|
label = R.string.diagnose_exf_slb,
|
||||||
enableFlags = ExperimentalFlags.SYSTEM_LEVEL_BLOCKING,
|
enableFlags = ExperimentalFlags.SYSTEM_LEVEL_BLOCKING,
|
||||||
|
@ -136,12 +128,6 @@ data class DiagnoseExperimentalFlagItem(
|
||||||
disableFlags = ExperimentalFlags.NETWORKTIME_AT_SYSTEMLEVEL,
|
disableFlags = ExperimentalFlags.NETWORKTIME_AT_SYSTEMLEVEL,
|
||||||
enable = { !BuildConfig.storeCompilant }
|
enable = { !BuildConfig.storeCompilant }
|
||||||
),
|
),
|
||||||
DiagnoseExperimentalFlagItem(
|
|
||||||
label = R.string.diagnose_exf_mau,
|
|
||||||
enableFlags = ExperimentalFlags.MANIPULATION_ANNOY_USER,
|
|
||||||
disableFlags = ExperimentalFlags.MANIPULATION_ANNOY_USER_ONLY,
|
|
||||||
enable = { !BuildConfig.storeCompilant }
|
|
||||||
),
|
|
||||||
DiagnoseExperimentalFlagItem(
|
DiagnoseExperimentalFlagItem(
|
||||||
label = R.string.diagnose_exf_isc,
|
label = R.string.diagnose_exf_isc,
|
||||||
enableFlags = ExperimentalFlags.IGNORE_SYSTEM_CONNECTION_STATUS,
|
enableFlags = ExperimentalFlags.IGNORE_SYSTEM_CONNECTION_STATUS,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* TimeLimit Copyright <C> 2019 - 2020 Jonas Lochmann
|
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -45,6 +45,12 @@ class NewLoginFragment: DialogFragment() {
|
||||||
companion object {
|
companion object {
|
||||||
const val SHOW_ON_LOCKSCREEN = "showOnLockscreen"
|
const val SHOW_ON_LOCKSCREEN = "showOnLockscreen"
|
||||||
|
|
||||||
|
fun newInstance(showOnLockscreen: Boolean) = NewLoginFragment().apply {
|
||||||
|
arguments = Bundle().apply {
|
||||||
|
putBoolean(SHOW_ON_LOCKSCREEN, showOnLockscreen)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private const val SELECTED_USER_ID = "selectedUserId"
|
private const val SELECTED_USER_ID = "selectedUserId"
|
||||||
private const val USER_LIST = 0
|
private const val USER_LIST = 0
|
||||||
private const val PARENT_AUTH = 1
|
private const val PARENT_AUTH = 1
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* TimeLimit Copyright <C> 2019 - 2021 Jonas Lochmann
|
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -19,38 +19,43 @@ import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import androidx.activity.viewModels
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.lifecycle.Observer
|
|
||||||
import androidx.lifecycle.ViewModelProviders
|
|
||||||
import io.timelimit.android.R
|
import io.timelimit.android.R
|
||||||
|
import io.timelimit.android.data.model.UserType
|
||||||
import io.timelimit.android.databinding.AnnoyActivityBinding
|
import io.timelimit.android.databinding.AnnoyActivityBinding
|
||||||
|
import io.timelimit.android.extensions.showSafe
|
||||||
import io.timelimit.android.livedata.map
|
import io.timelimit.android.livedata.map
|
||||||
import io.timelimit.android.logic.DefaultAppLogic
|
import io.timelimit.android.logic.DefaultAppLogic
|
||||||
|
import io.timelimit.android.ui.backdoor.BackdoorDialogFragment
|
||||||
|
import io.timelimit.android.ui.login.NewLoginFragment
|
||||||
|
import io.timelimit.android.ui.main.ActivityViewModel
|
||||||
|
import io.timelimit.android.ui.main.ActivityViewModelHolder
|
||||||
import io.timelimit.android.ui.manage.device.manage.ManipulationWarningTypeLabel
|
import io.timelimit.android.ui.manage.device.manage.ManipulationWarningTypeLabel
|
||||||
import io.timelimit.android.ui.manage.device.manage.ManipulationWarnings
|
import io.timelimit.android.ui.manage.device.manage.ManipulationWarnings
|
||||||
import io.timelimit.android.util.TimeTextUtil
|
import io.timelimit.android.util.TimeTextUtil
|
||||||
|
|
||||||
class AnnoyActivity : AppCompatActivity() {
|
class AnnoyActivity : AppCompatActivity(), ActivityViewModelHolder {
|
||||||
companion object {
|
companion object {
|
||||||
private const val EXTRA_DURATION = "duration"
|
fun start(context: Context) {
|
||||||
|
|
||||||
fun start(context: Context, duration: Long) {
|
|
||||||
context.startActivity(
|
context.startActivity(
|
||||||
Intent(context, AnnoyActivity::class.java)
|
Intent(context, AnnoyActivity::class.java)
|
||||||
.putExtra(EXTRA_DURATION, duration)
|
|
||||||
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
|
|
||||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION)
|
.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION)
|
||||||
|
.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val model: ActivityViewModel by viewModels()
|
||||||
|
override var ignoreStop: Boolean = false
|
||||||
|
override val showPasswordRecovery: Boolean = false
|
||||||
|
override fun getActivityViewModel() = model
|
||||||
|
override fun showAuthenticationScreen() { NewLoginFragment.newInstance(showOnLockscreen = true).showSafe(supportFragmentManager, "nlf") }
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
val duration = intent!!.getLongExtra(EXTRA_DURATION, 10)
|
|
||||||
val model = ViewModelProviders.of(this).get(AnnoyModel::class.java)
|
|
||||||
val logic = DefaultAppLogic.with(this)
|
val logic = DefaultAppLogic.with(this)
|
||||||
|
|
||||||
val binding = AnnoyActivityBinding.inflate(layoutInflater)
|
val binding = AnnoyActivityBinding.inflate(layoutInflater)
|
||||||
|
@ -62,37 +67,36 @@ class AnnoyActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
model.init(duration = duration)
|
logic.annoyLogic.shouldAnnoyRightNow.observe(this) { shouldRun ->
|
||||||
model.countdown.observe(this, Observer {
|
if (!shouldRun) shutdown()
|
||||||
if (it == 0L) {
|
|
||||||
shutdown()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.annoyTimer.setText(
|
logic.annoyLogic.nextManualUnblockCountdown.observe(this) { countdown ->
|
||||||
getString(R.string.annoy_timer, TimeTextUtil.seconds(it.toInt(), this@AnnoyActivity))
|
binding.canRequestUnlock = countdown == 0L
|
||||||
)
|
binding.countdownText = getString(R.string.annoy_timer, TimeTextUtil.seconds((countdown / 1000).toInt(), this@AnnoyActivity))
|
||||||
})
|
}
|
||||||
|
|
||||||
logic.deviceEntry.map {
|
logic.deviceEntry.map {
|
||||||
val reasonItems = (it?.let { ManipulationWarnings.getFromDevice(it) } ?: ManipulationWarnings.empty)
|
val reasonItems = (it?.let { ManipulationWarnings.getFromDevice(it) } ?: ManipulationWarnings.empty)
|
||||||
.current
|
.current
|
||||||
.map {
|
.map { getString(ManipulationWarningTypeLabel.getLabel(it)) }
|
||||||
getString(ManipulationWarningTypeLabel.getLabel(it))
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reasonItems.isEmpty()) {
|
if (reasonItems.isEmpty()) {
|
||||||
null
|
null
|
||||||
} else {
|
} else {
|
||||||
getString(R.string.annoy_reason, reasonItems.joinToString(separator = ", "))
|
getString(R.string.annoy_reason, reasonItems.joinToString(separator = ", "))
|
||||||
}
|
}
|
||||||
}.observe(this, Observer {
|
}.observe(this) { binding.reasonText = it }
|
||||||
if (it.isNullOrEmpty()) {
|
|
||||||
binding.annoyReason.visibility = View.GONE
|
binding.unlockTemporarilyButton.setOnClickListener { logic.annoyLogic.doManualTempUnlock() }
|
||||||
} else {
|
binding.parentUnlockButton.setOnClickListener { showAuthenticationScreen() }
|
||||||
binding.annoyReason.visibility = View.VISIBLE
|
binding.useBackdoorButton.setOnClickListener { BackdoorDialogFragment().show(supportFragmentManager) }
|
||||||
binding.annoyReason.setText(it)
|
|
||||||
|
model.authenticatedUser.observe(this) { user ->
|
||||||
|
if (user?.second?.type == UserType.Parent) {
|
||||||
|
logic.annoyLogic.doParentTempUnlock()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun shutdown() {
|
private fun shutdown() {
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
/*
|
|
||||||
* TimeLimit Copyright <C> 2019 Jonas Lochmann
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation version 3 of the License.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package io.timelimit.android.ui.manipulation
|
|
||||||
|
|
||||||
import androidx.lifecycle.MutableLiveData
|
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
import io.timelimit.android.coroutines.runAsync
|
|
||||||
import io.timelimit.android.livedata.castDown
|
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
|
|
||||||
class AnnoyModel: ViewModel() {
|
|
||||||
private val countdownInternal = MutableLiveData<Long>()
|
|
||||||
private var hadInit = false
|
|
||||||
|
|
||||||
val countdown = countdownInternal.castDown()
|
|
||||||
|
|
||||||
fun init(duration: Long) {
|
|
||||||
if (!hadInit) {
|
|
||||||
hadInit = true
|
|
||||||
|
|
||||||
countdownInternal.value = duration
|
|
||||||
|
|
||||||
runAsync {
|
|
||||||
var timer = duration
|
|
||||||
|
|
||||||
while (timer >= 0) {
|
|
||||||
delay(1000)
|
|
||||||
timer--
|
|
||||||
|
|
||||||
countdownInternal.value = timer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,107 +0,0 @@
|
||||||
/*
|
|
||||||
* TimeLimit Copyright <C> 2019 - 2021 Jonas Lochmann
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation version 3 of the License.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package io.timelimit.android.ui.manipulation
|
|
||||||
|
|
||||||
import android.os.Build
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.WindowManager
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import androidx.lifecycle.Observer
|
|
||||||
import androidx.lifecycle.ViewModelProviders
|
|
||||||
import io.timelimit.android.async.Threads
|
|
||||||
import io.timelimit.android.data.model.UserType
|
|
||||||
import io.timelimit.android.databinding.ActivityUnlockAfterManipulationBinding
|
|
||||||
import io.timelimit.android.extensions.showSafe
|
|
||||||
import io.timelimit.android.logic.DefaultAppLogic
|
|
||||||
import io.timelimit.android.ui.backdoor.BackdoorDialogFragment
|
|
||||||
import io.timelimit.android.ui.login.NewLoginFragment
|
|
||||||
import io.timelimit.android.ui.main.ActivityViewModel
|
|
||||||
import io.timelimit.android.ui.main.ActivityViewModelHolder
|
|
||||||
|
|
||||||
class UnlockAfterManipulationActivity : AppCompatActivity(), ActivityViewModelHolder {
|
|
||||||
private val model: ActivityViewModel by lazy {
|
|
||||||
ViewModelProviders.of(this).get(ActivityViewModel::class.java)
|
|
||||||
}
|
|
||||||
|
|
||||||
override var ignoreStop: Boolean = false
|
|
||||||
override val showPasswordRecovery: Boolean = false
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
|
|
||||||
val binding = ActivityUnlockAfterManipulationBinding.inflate(layoutInflater)
|
|
||||||
setContentView(binding.root)
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
|
||||||
startLockTask()
|
|
||||||
} else {
|
|
||||||
window.addFlags(
|
|
||||||
WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or
|
|
||||||
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or
|
|
||||||
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
model.authenticatedUser.observe(this, Observer {
|
|
||||||
user ->
|
|
||||||
|
|
||||||
if (user != null && user.second.type == UserType.Parent) {
|
|
||||||
onAuthSuccess()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
model.logic.deviceId.observe(this, Observer {
|
|
||||||
if (it.isNullOrEmpty()) {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
|
||||||
stopLockTask()
|
|
||||||
}
|
|
||||||
|
|
||||||
DefaultAppLogic.with(this).appSetupLogic.resetAppCompletely()
|
|
||||||
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
binding.authBtn.setOnClickListener { showAuthenticationScreen() }
|
|
||||||
binding.useBackdoor.setOnClickListener { BackdoorDialogFragment().show(supportFragmentManager) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun showAuthenticationScreen() {
|
|
||||||
NewLoginFragment().apply {
|
|
||||||
arguments = Bundle().apply {
|
|
||||||
putBoolean(NewLoginFragment.SHOW_ON_LOCKSCREEN, true)
|
|
||||||
}
|
|
||||||
}.showSafe(supportFragmentManager, "nlf")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getActivityViewModel() = model
|
|
||||||
|
|
||||||
private fun onAuthSuccess() {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
|
||||||
stopLockTask()
|
|
||||||
}
|
|
||||||
|
|
||||||
val appLogic = DefaultAppLogic.with(this@UnlockAfterManipulationActivity)
|
|
||||||
|
|
||||||
Threads.database.execute {
|
|
||||||
appLogic.manipulationLogic.unlockDeviceSync()
|
|
||||||
}
|
|
||||||
|
|
||||||
appLogic.manipulationLogic.showManipulationUnlockedScreen()
|
|
||||||
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,63 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--
|
|
||||||
TimeLimit Copyright <C> 2019 - 2020 Jonas Lochmann
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation version 3 of the License.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
-->
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:orientation="vertical"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
tools:context="io.timelimit.android.ui.manipulation.UnlockAfterManipulationActivity">
|
|
||||||
|
|
||||||
<RelativeLayout
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="0dp">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_centerVertical="true"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:padding="8dp"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:gravity="center_horizontal"
|
|
||||||
android:textAppearance="?android:textAppearanceMedium"
|
|
||||||
android:text="@string/manipulation_activity_intro"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:layout_gravity="center_horizontal"
|
|
||||||
android:id="@+id/auth_btn"
|
|
||||||
android:text="@string/add_user_authentication_required_btn"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
</RelativeLayout>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
style="?materialButtonOutlinedStyle"
|
|
||||||
android:layout_marginLeft="8dp"
|
|
||||||
android:layout_marginRight="8dp"
|
|
||||||
android:layout_gravity="center_horizontal"
|
|
||||||
android:id="@+id/use_backdoor"
|
|
||||||
android:text="@string/backdoor_start_btn"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
|
@ -1,6 +1,40 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<!--
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation version 3 of the License.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<data>
|
||||||
|
<variable
|
||||||
|
name="reasonText"
|
||||||
|
type="String" />
|
||||||
|
|
||||||
|
<variable
|
||||||
|
name="countdownText"
|
||||||
|
type="String" />
|
||||||
|
|
||||||
|
<variable
|
||||||
|
name="canRequestUnlock"
|
||||||
|
type="boolean" />
|
||||||
|
|
||||||
|
<import type="android.text.TextUtils" />
|
||||||
|
<import type="android.view.View" />
|
||||||
|
</data>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
@ -15,8 +49,9 @@
|
||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
|
android:layout_weight="1"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="0dp">
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_margin="8dp"
|
android:layout_margin="8dp"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
|
@ -29,20 +64,59 @@
|
||||||
android:textAppearance="?android:textAppearanceMedium"
|
android:textAppearance="?android:textAppearanceMedium"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:id="@+id/annoy_reason"
|
android:id="@+id/annoy_reason"
|
||||||
android:text="@string/annoy_reason"
|
tools:text="@string/annoy_reason"
|
||||||
|
android:text="@{reasonText}"
|
||||||
|
android:visibility="@{TextUtils.isEmpty(reasonText) ? View.GONE : View.VISIBLE}"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:enabled="@{canRequestUnlock}"
|
||||||
|
android:id="@+id/unlock_temporarily_button"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:text="@string/annoy_unlock_temp_button"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
style="?materialButtonOutlinedStyle"
|
||||||
|
android:id="@+id/parent_unlock_button"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:text="@string/annoy_unlock_parent_button"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_margin="4dp"
|
android:layout_margin="4dp"
|
||||||
android:textAppearance="?android:textAppearanceMedium"
|
android:textAppearance="?android:textAppearanceSmall"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:id="@+id/annoy_timer"
|
android:id="@+id/annoy_timer"
|
||||||
android:text="@string/annoy_timer"
|
tools:text="@string/annoy_timer"
|
||||||
|
android:text="@{countdownText}"
|
||||||
|
android:visibility="@{canRequestUnlock ? View.INVISIBLE : View.VISIBLE}"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
style="?materialButtonOutlinedStyle"
|
||||||
|
android:layout_marginLeft="8dp"
|
||||||
|
android:layout_marginRight="8dp"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:id="@+id/use_backdoor_button"
|
||||||
|
android:text="@string/backdoor_start_btn"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
</layout>
|
|
@ -109,11 +109,13 @@
|
||||||
<string name="add_user_authentication_required_btn">Anmelden</string>
|
<string name="add_user_authentication_required_btn">Anmelden</string>
|
||||||
|
|
||||||
<string name="annoy_info">
|
<string name="annoy_info">
|
||||||
TimeLimit wurde manipuliert. Deshalb wird das Gerät für einen Moment gesperrt.
|
TimeLimit wurde manipuliert. Deshalb wurde das Gerät gesperrt.
|
||||||
</string>
|
</string>
|
||||||
<string name="annoy_timer">
|
<string name="annoy_timer">
|
||||||
Das Gerät wird in %s entsperrt.
|
Das Gerät kann in %s entsperrt werden.
|
||||||
</string>
|
</string>
|
||||||
|
<string name="annoy_unlock_temp_button">kurz entsperren</string>
|
||||||
|
<string name="annoy_unlock_parent_button">länger entsperren</string>
|
||||||
<string name="annoy_reason">
|
<string name="annoy_reason">
|
||||||
Folgendes wurde manipuliert: %s
|
Folgendes wurde manipuliert: %s
|
||||||
</string>
|
</string>
|
||||||
|
@ -502,9 +504,7 @@
|
||||||
<string name="diagnose_fga_query_range_min">Minimal (Standard)</string>
|
<string name="diagnose_fga_query_range_min">Minimal (Standard)</string>
|
||||||
|
|
||||||
<string name="diagnose_exf_title">Experimentelle Parameter</string>
|
<string name="diagnose_exf_title">Experimentelle Parameter</string>
|
||||||
<string name="diagnose_exf_lom">Sperrung nach Manipulationen deaktivieren</string>
|
|
||||||
<string name="diagnose_exf_slb">Apps auf Systemebene sperren</string>
|
<string name="diagnose_exf_slb">Apps auf Systemebene sperren</string>
|
||||||
<string name="diagnose_exf_mau">Nutzer nach Manipulation nerven</string>
|
|
||||||
<string name="diagnose_exf_isc">Systemverbindungsstatus ignorieren</string>
|
<string name="diagnose_exf_isc">Systemverbindungsstatus ignorieren</string>
|
||||||
<string name="diagnose_exf_chs">TimeLimit als Startbildschirm</string>
|
<string name="diagnose_exf_chs">TimeLimit als Startbildschirm</string>
|
||||||
<string name="diagnose_exf_chd">Startbildschirmweiterleitung verzögern</string>
|
<string name="diagnose_exf_chd">Startbildschirmweiterleitung verzögern</string>
|
||||||
|
@ -1080,8 +1080,6 @@
|
||||||
kann nicht mit einem weiteren Benutzer verknüpft werden
|
kann nicht mit einem weiteren Benutzer verknüpft werden
|
||||||
</string>
|
</string>
|
||||||
|
|
||||||
<string name="manipulation_activity_intro">Es gab eine Manipulation. Deswegen muss dieses Gerät durch ein Elternteil freigegeben werden.</string>
|
|
||||||
|
|
||||||
<string name="must_read_child_manipulation">
|
<string name="must_read_child_manipulation">
|
||||||
Da es manchmal nicht wahrgenommen wird:
|
Da es manchmal nicht wahrgenommen wird:
|
||||||
\n1. In den Standardeinstellungen kann TimeLimit ganz leicht,
|
\n1. In den Standardeinstellungen kann TimeLimit ganz leicht,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!--
|
<!--
|
||||||
TimeLimit Copyright <C> 2019 - 2021 Jonas Lochmann
|
TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
the Free Software Foundation version 3 of the License.
|
the Free Software Foundation version 3 of the License.
|
||||||
|
@ -428,9 +428,7 @@ por exemplo, quando você quer adicionar o uso de tela
|
||||||
<string name="diagnose_fga_query_range_min">Mínimo (Padrão)</string>
|
<string name="diagnose_fga_query_range_min">Mínimo (Padrão)</string>
|
||||||
|
|
||||||
<string name="diagnose_exf_title">Marcado res experimentais</string>
|
<string name="diagnose_exf_title">Marcado res experimentais</string>
|
||||||
<string name="diagnose_exf_lom">Desativar o travamento após manipulações</string>
|
|
||||||
<string name="diagnose_exf_slb">Bloqueio de Aplicativos no nível de sistema</string>
|
<string name="diagnose_exf_slb">Bloqueio de Aplicativos no nível de sistema</string>
|
||||||
<string name="diagnose_exf_mau">Irrita o usuário após a manipulação</string>
|
|
||||||
<string name="diagnose_exf_isc">Ignorar o status da conexão do sistema</string>
|
<string name="diagnose_exf_isc">Ignorar o status da conexão do sistema</string>
|
||||||
<string name="diagnose_exf_chs">TimeLimit como lançador</string>
|
<string name="diagnose_exf_chs">TimeLimit como lançador</string>
|
||||||
<string name="diagnose_exf_chd">Atraso no redirecionamento do lançador</string>
|
<string name="diagnose_exf_chd">Atraso no redirecionamento do lançador</string>
|
||||||
|
@ -949,8 +947,6 @@ Esta %s só é permitida em algumas redes, mas nenhuma conexão com tal rede foi
|
||||||
não pode ser vinculada a um usuário adicional
|
não pode ser vinculada a um usuário adicional
|
||||||
</string>
|
</string>
|
||||||
|
|
||||||
<string name="manipulation_activity_intro">Houve uma manipulação. Devido a isso, este dispositivo deve ser desbloqueado por os Pais.</string>
|
|
||||||
|
|
||||||
<string name="must_read_child_manipulation">
|
<string name="must_read_child_manipulation">
|
||||||
Porque alguns usuários não se dão conta disso:
|
Porque alguns usuários não se dão conta disso:
|
||||||
\n1st: Nas configurações padrão, TimeLimit pode ser facilmente desinstalado, mesmo por crianças.
|
\n1st: Nas configurações padrão, TimeLimit pode ser facilmente desinstalado, mesmo por crianças.
|
||||||
|
|
|
@ -153,11 +153,13 @@
|
||||||
<string name="add_user_authentication_required_btn">Authenticate</string>
|
<string name="add_user_authentication_required_btn">Authenticate</string>
|
||||||
|
|
||||||
<string name="annoy_info">
|
<string name="annoy_info">
|
||||||
There was a manipulation of TimeLimit. Due to that, this device is locked for a moment.
|
There was a manipulation of TimeLimit. Due to that, this device is locked now.
|
||||||
</string>
|
</string>
|
||||||
<string name="annoy_timer">
|
<string name="annoy_timer">
|
||||||
The device will be unlocked in %s.
|
The device can be unlocked in %s.
|
||||||
</string>
|
</string>
|
||||||
|
<string name="annoy_unlock_temp_button">Unlock for a moment</string>
|
||||||
|
<string name="annoy_unlock_parent_button">Unlock a bit longer</string>
|
||||||
<string name="annoy_reason">
|
<string name="annoy_reason">
|
||||||
This was manipulated: %s
|
This was manipulated: %s
|
||||||
</string>
|
</string>
|
||||||
|
@ -555,9 +557,7 @@
|
||||||
<string name="diagnose_fga_query_range_min">Minimum (Default)</string>
|
<string name="diagnose_fga_query_range_min">Minimum (Default)</string>
|
||||||
|
|
||||||
<string name="diagnose_exf_title">Experimental flags</string>
|
<string name="diagnose_exf_title">Experimental flags</string>
|
||||||
<string name="diagnose_exf_lom">Disable locking after manipulations</string>
|
|
||||||
<string name="diagnose_exf_slb">Block Apps at system level</string>
|
<string name="diagnose_exf_slb">Block Apps at system level</string>
|
||||||
<string name="diagnose_exf_mau">Annoy user after manipulation</string>
|
|
||||||
<string name="diagnose_exf_isc">Ignore system connection status</string>
|
<string name="diagnose_exf_isc">Ignore system connection status</string>
|
||||||
<string name="diagnose_exf_chs">TimeLimit as launcher</string>
|
<string name="diagnose_exf_chs">TimeLimit as launcher</string>
|
||||||
<string name="diagnose_exf_chd">Delay launcher redirection</string>
|
<string name="diagnose_exf_chd">Delay launcher redirection</string>
|
||||||
|
@ -1125,8 +1125,6 @@
|
||||||
can not be linked to an additional user
|
can not be linked to an additional user
|
||||||
</string>
|
</string>
|
||||||
|
|
||||||
<string name="manipulation_activity_intro">There was a manipulation. Due to that, this device must be unlocked by a parent.</string>
|
|
||||||
|
|
||||||
<string name="must_read_child_manipulation">
|
<string name="must_read_child_manipulation">
|
||||||
Because some users do not realize it:
|
Because some users do not realize it:
|
||||||
\n1st: In the default settings, TimeLimit can be removed easily, even by children.
|
\n1st: In the default settings, TimeLimit can be removed easily, even by children.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue