Add support for Direct Boot by loading default preferences for first unlock

This commit is contained in:
Aleksandras Kostarevas 2024-07-13 07:24:48 +03:00
parent 7d492897cc
commit b6206e3059
17 changed files with 279 additions and 83 deletions

View File

@ -32,8 +32,6 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:allowBackup="true"
android:defaultToDeviceProtectedStorage="true"
android:directBootAware="true"
android:largeHeap="true"
android:name=".CrashLoggingApplication">
@ -41,6 +39,7 @@
<service android:name=".LatinIME"
android:label="@string/english_ime_name"
android:permission="android.permission.BIND_INPUT_METHOD"
android:directBootAware="true"
android:exported="true">
<intent-filter>
<action android:name="android.view.InputMethod"/>

View File

@ -159,9 +159,7 @@ public final class KeyboardTheme implements Comparable<KeyboardTheme> {
}
public static KeyboardTheme getKeyboardTheme(final Context context) {
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
final KeyboardTheme[] availableThemeArray = getAvailableThemeArray(context);
return getKeyboardTheme(prefs, BuildCompatUtils.EFFECTIVE_SDK_INT, availableThemeArray);
return KEYBOARD_THEMES[0];
}
/* package private for testing */

View File

@ -200,9 +200,7 @@ public final class MainKeyboardView extends KeyboardView implements DrawingProxy
PointerTracker.init(mainKeyboardViewAttr, mTimerHandler, this /* DrawingProxy */);
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
final boolean forceNonDistinctMultitouch = prefs.getBoolean(
DebugSettings.PREF_FORCE_NON_DISTINCT_MULTITOUCH, false);
final boolean forceNonDistinctMultitouch = false;
final boolean hasDistinctMultitouch = context.getPackageManager()
.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT)
&& !forceNonDistinctMultitouch;

View File

@ -1,5 +1,9 @@
package org.futo.inputmethod.latin
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.res.Configuration
import android.graphics.Color
import android.inputmethodservice.InputMethodService
@ -19,7 +23,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.ColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.key
import androidx.compose.ui.Modifier
@ -50,6 +53,7 @@ import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import org.futo.inputmethod.latin.SuggestedWords.SuggestedWordInfo
import org.futo.inputmethod.latin.common.Constants
import org.futo.inputmethod.latin.settings.Settings
import org.futo.inputmethod.latin.uix.BasicThemeProvider
import org.futo.inputmethod.latin.uix.DynamicThemeProvider
import org.futo.inputmethod.latin.uix.DynamicThemeProviderOwner
@ -67,6 +71,7 @@ import org.futo.inputmethod.latin.uix.differsFrom
import org.futo.inputmethod.latin.uix.getSetting
import org.futo.inputmethod.latin.uix.getSettingBlocking
import org.futo.inputmethod.latin.uix.getSettingFlow
import org.futo.inputmethod.latin.uix.isDirectBootUnlocked
import org.futo.inputmethod.latin.uix.setSetting
import org.futo.inputmethod.latin.uix.theme.DarkColorScheme
import org.futo.inputmethod.latin.uix.theme.ThemeOption
@ -76,10 +81,18 @@ import org.futo.inputmethod.latin.uix.theme.presets.VoiceInputTheme
import org.futo.inputmethod.latin.xlm.LanguageModelFacilitator
import org.futo.inputmethod.updates.scheduleUpdateCheckingJob
private class UnlockedBroadcastReceiver(val onDeviceUnlocked: () -> Unit) : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
println("Unlocked Broadcast Receiver: ${intent?.action}")
if (intent?.action == Intent.ACTION_USER_UNLOCKED) {
onDeviceUnlocked()
}
}
}
class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, SavedStateRegistryOwner,
LatinIMELegacy.SuggestionStripController, DynamicThemeProviderOwner {
private lateinit var mLifecycleRegistry: LifecycleRegistry
private lateinit var mViewModelStore: ViewModelStore
private lateinit var mSavedStateRegistryController: SavedStateRegistryController
@ -222,9 +235,14 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
jobs.clear()
}
private var unlockReceiver = UnlockedBroadcastReceiver { onDeviceUnlocked() }
override fun onCreate() {
super.onCreate()
val filter = IntentFilter(Intent.ACTION_USER_UNLOCKED)
registerReceiver(unlockReceiver, filter)
mLifecycleRegistry = LifecycleRegistry(this)
mLifecycleRegistry.currentState = Lifecycle.State.INITIALIZED
@ -264,7 +282,10 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
latinIMELegacy.onCreate()
languageModelFacilitator.launchProcessor()
languageModelFacilitator.loadHistoryLog()
if(isDirectBootUnlocked) {
languageModelFacilitator.loadHistoryLog()
}
scheduleUpdateCheckingJob(this)
launchJob { uixManager.showUpdateNoticeIfNeeded() }
@ -322,6 +343,8 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
}
override fun onDestroy() {
unregisterReceiver(unlockReceiver)
stopJobs()
mLifecycleRegistry.currentState = Lifecycle.State.DESTROYED
viewModelStore.clear()
@ -666,4 +689,22 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
get() = mSavedStateRegistryController.savedStateRegistry
override val viewModelStore: ViewModelStore
get() = mViewModelStore
private fun onDeviceUnlocked() {
Log.i("LatinIME", "DEVICE has UNLOCKED!!! Reloading settings...")
// Every place that called getDefaultSharedPreferences now needs to be refreshed or call it again
// Mainly Settings singleton needs to be refreshed
Settings.init(applicationContext)
Settings.getInstance().onSharedPreferenceChanged(null /* unused */, "")
latinIMELegacy.loadSettings()
recreateKeyboard()
Log.i("LatinIME", "DEVICE has UNLOCKED!!! Finished reloading: ${Settings.getInstance().current.dump()}")
languageModelFacilitator.loadHistoryLog()
// TODO: Spell checker service
}
}

View File

@ -38,7 +38,6 @@ import android.os.Build;
import android.os.Debug;
import android.os.IBinder;
import android.os.Message;
import android.preference.PreferenceManager;
import android.text.InputType;
import android.util.Log;
import android.util.PrintWriterPrinter;
@ -573,7 +572,6 @@ public class LatinIMELegacy implements KeyboardActionListener,
public void onCreate() {
Settings.init(mInputMethodService);
DebugFlags.init(PreferenceManager.getDefaultSharedPreferences(mInputMethodService));
RichInputMethodManager.init(mInputMethodService);
mRichImm = RichInputMethodManager.getInstance();
AudioAndHapticFeedbackManager.init(mInputMethodService);
@ -934,10 +932,6 @@ public class LatinIMELegacy implements KeyboardActionListener,
final boolean inputTypeChanged = !currentSettingsValues.isSameInputType(editorInfo);
final boolean isDifferentTextField = !restarting || inputTypeChanged;
StatsUtils.onStartInputView(editorInfo.inputType,
Settings.getInstance().getCurrent().mDisplayOrientation,
!isDifferentTextField);
// The EditorInfo might have a flag that affects fullscreen mode.
// Note: This call should be done by InputMethodService?
updateFullscreenMode();

View File

@ -34,6 +34,7 @@ import org.futo.inputmethod.annotations.UsedForTesting;
import org.futo.inputmethod.compat.InputMethodManagerCompatWrapper;
import org.futo.inputmethod.compat.InputMethodSubtypeCompatUtils;
import org.futo.inputmethod.latin.settings.Settings;
import org.futo.inputmethod.latin.uix.PreferenceUtils;
import org.futo.inputmethod.latin.utils.AdditionalSubtypeUtils;
import org.futo.inputmethod.latin.utils.LanguageOnSpacebarUtils;
import org.futo.inputmethod.latin.utils.SubtypeLocaleUtils;
@ -108,7 +109,7 @@ public class RichInputMethodManager {
}
public InputMethodSubtype[] getAdditionalSubtypes() {
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
final SharedPreferences prefs = PreferenceUtils.INSTANCE.getDefaultSharedPreferences(mContext);
final String prefAdditionalSubtypes = Settings.readPrefAdditionalSubtypes(
prefs, mContext.getResources());
return AdditionalSubtypeUtils.createAdditionalSubtypesArray(prefAdditionalSubtypes);

View File

@ -37,6 +37,7 @@ import okhttp3.internal.toImmutableList
import org.futo.inputmethod.latin.common.Constants
import org.futo.inputmethod.latin.uix.SettingsKey
import org.futo.inputmethod.latin.uix.getSettingBlocking
import org.futo.inputmethod.latin.uix.isDirectBootUnlocked
import org.futo.inputmethod.latin.uix.setSettingBlocking
import org.futo.inputmethod.latin.uix.settings.NavigationItem
import org.futo.inputmethod.latin.uix.settings.NavigationItemStyle
@ -93,6 +94,8 @@ object Subtypes {
}
fun addDefaultSubtypesIfNecessary(context: Context) {
if(!context.isDirectBootUnlocked) return
val currentSubtypes = context.getSettingBlocking(SubtypesSetting)
if(currentSubtypes.isNotEmpty()) {
removeExtensionsIfNecessary(context)
@ -116,7 +119,7 @@ object Subtypes {
addLanguage(context, Locale.forLanguageTag("zz"), "qwerty")
}
context.setSettingBlocking(ActiveSubtype.key, context.getSettingBlocking(SubtypesSetting).first())
context.setSettingBlocking(ActiveSubtype.key, context.getSettingBlocking(SubtypesSetting).firstOrNull() ?: return)
}
fun findClosestLocaleLayouts(locale: Locale): List<String> {
@ -224,6 +227,30 @@ object Subtypes {
}
}.mapValues { it.value.toImmutableList() }
}
fun getDirectBootInitialLayouts(context: Context): Set<String> {
val layouts = mutableSetOf("en_US:")
val locales = context.resources.configuration.locales
if(locales.size() == 0) return layouts
for(i in 0 until locales.size()) {
val locale = locales.get(i).stripExtensionsIfNeeded()
val layout = findClosestLocaleLayouts(locale).firstOrNull() ?: continue
val value = subtypeToString(
InputMethodSubtypeBuilder()
.setSubtypeLocale(locale.stripExtensionsIfNeeded().toString())
.setSubtypeExtraValue("KeyboardLayoutSet=$layout")
.build()
)
layouts.add(value)
}
return layouts
}
}

View File

@ -22,7 +22,6 @@ import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Build;
import android.preference.PreferenceManager;
import android.util.Log;
import org.futo.inputmethod.compat.BuildCompatUtils;
@ -30,6 +29,7 @@ import org.futo.inputmethod.latin.AudioAndHapticFeedbackManager;
import org.futo.inputmethod.latin.InputAttributes;
import org.futo.inputmethod.latin.R;
import org.futo.inputmethod.latin.common.StringUtils;
import org.futo.inputmethod.latin.uix.PreferenceUtils;
import org.futo.inputmethod.latin.utils.AdditionalSubtypeUtils;
import org.futo.inputmethod.latin.utils.ResourceUtils;
import org.futo.inputmethod.latin.utils.RunInLocale;
@ -145,7 +145,12 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
private void onCreate(final Context context) {
mContext = context;
mRes = context.getResources();
mPrefs = PreferenceManager.getDefaultSharedPreferences(context);
if(mPrefs != null) {
mPrefs.unregisterOnSharedPreferenceChangeListener(this);
}
mPrefs = PreferenceUtils.INSTANCE.getDefaultSharedPreferences(context);
mPrefs.registerOnSharedPreferenceChangeListener(this);
upgradeAutocorrectionSettings(mPrefs, mRes);
}

View File

@ -36,6 +36,7 @@ import org.futo.inputmethod.latin.RichInputMethodSubtype;
import org.futo.inputmethod.latin.SuggestedWords;
import org.futo.inputmethod.latin.common.ComposedData;
import org.futo.inputmethod.latin.settings.SettingsValuesForSuggestion;
import org.futo.inputmethod.latin.uix.PreferenceUtils;
import org.futo.inputmethod.latin.utils.AdditionalSubtypeUtils;
import org.futo.inputmethod.latin.utils.ScriptUtils;
import org.futo.inputmethod.latin.utils.SuggestionResults;
@ -95,7 +96,7 @@ public final class AndroidSpellCheckerService extends SpellCheckerService
super.onCreate();
mRecommendedThreshold = Float.parseFloat(
getString(R.string.spellchecker_recommended_threshold_value));
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
final SharedPreferences prefs = PreferenceUtils.INSTANCE.getDefaultSharedPreferences(this);
prefs.registerOnSharedPreferenceChangeListener(this);
onSharedPreferenceChanged(prefs, PREF_USE_CONTACTS_KEY);
}

View File

@ -1,27 +1,147 @@
package org.futo.inputmethod.latin.uix
import android.content.Context
import android.content.SharedPreferences
import android.os.UserManager
import android.preference.PreferenceManager
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.PreferenceDataStoreFactory
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.preferencesOf
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.core.stringSetPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import androidx.datastore.preferences.preferencesDataStoreFile
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.take
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import org.futo.inputmethod.latin.ActiveSubtype
import org.futo.inputmethod.latin.Subtypes
import org.futo.inputmethod.latin.SubtypesSetting
import org.futo.inputmethod.latin.uix.theme.presets.ClassicMaterialDark
import org.futo.inputmethod.latin.uix.theme.presets.DynamicSystemTheme
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
// Used before first unlock (direct boot)
private object DefaultDataStore : DataStore<Preferences> {
private var activePreferences = preferencesOf(
ActiveSubtype.key to "en_US:",
SubtypesSetting.key to setOf("en_US:"),
THEME_KEY.key to ClassicMaterialDark.key,
KeyHintsSetting.key to true
)
var subtypesInitialized = false
suspend fun updateSubtypes(subtypes: Set<String>) {
val newPreferences = activePreferences.toMutablePreferences()
newPreferences[SubtypesSetting.key] = subtypes
activePreferences = newPreferences
sharedData.emit(activePreferences)
}
val sharedData = MutableSharedFlow<Preferences>(1)
override val data: Flow<Preferences>
get() {
return unlockedDataStore?.data ?: sharedData
}
init {
sharedData.tryEmit(activePreferences)
}
override suspend fun updateData(transform: suspend (t: Preferences) -> Preferences): Preferences {
return unlockedDataStore?.updateData(transform) ?: run {
val newActiveSubtype = transform(activePreferences)[ActiveSubtype.key]
if(newActiveSubtype != null && newActiveSubtype != activePreferences[ActiveSubtype.key]) {
val newPreferences = activePreferences.toMutablePreferences()
newPreferences[ActiveSubtype.key] = newActiveSubtype
activePreferences = newPreferences
sharedData.emit(newPreferences)
}
return activePreferences
}
}
}
// Set and used after first unlock (direct boot)
private var unlockedDataStore: DataStore<Preferences>? = null
// Initializes unlockedDataStore, or uses DefaultDataStore if device is still locked (direct boot)
@OptIn(DelicateCoroutinesApi::class)
val Context.dataStore: DataStore<Preferences>
get() {
val userManager = getSystemService(Context.USER_SERVICE) as UserManager
if (userManager.isUserUnlocked) {
// The device has been unlocked
return unlockedDataStore ?: run {
val newDataStore = PreferenceDataStoreFactory.create(
corruptionHandler = null,
migrations = listOf(),
scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
) {
applicationContext.preferencesDataStoreFile("settings")
}
unlockedDataStore = newDataStore
// Send new values to the DefaultDataStore for any listeners
GlobalScope.launch {
newDataStore.data.collect { value ->
DefaultDataStore.sharedData.emit(value)
}
}
newDataStore
}
} else {
// The device is still locked, return default data store
if(!DefaultDataStore.subtypesInitialized) {
DefaultDataStore.subtypesInitialized = true
GlobalScope.launch {
DefaultDataStore.updateSubtypes(Subtypes.getDirectBootInitialLayouts(this@dataStore))
}
}
return DefaultDataStore
}
}
object PreferenceUtils {
fun getDefaultSharedPreferences(context: Context): SharedPreferences {
val userManager = context.getSystemService(Context.USER_SERVICE) as UserManager
return if (userManager.isUserUnlocked) {
PreferenceManager.getDefaultSharedPreferences(context)
} else {
PreferenceManager.getDefaultSharedPreferences(context.createDeviceProtectedStorageContext())
}
}
}
val Context.isDirectBootUnlocked: Boolean
get() {
val userManager = getSystemService(Context.USER_SERVICE) as UserManager
return userManager.isUserUnlocked
}
suspend fun <T> Context.getSetting(key: Preferences.Key<T>, default: T): T {
val valueFlow: Flow<T> =

View File

@ -60,6 +60,7 @@ import org.futo.inputmethod.latin.uix.PersistentActionState
import org.futo.inputmethod.latin.uix.PersistentStateInitialization
import org.futo.inputmethod.latin.uix.SettingsKey
import org.futo.inputmethod.latin.uix.getSettingBlocking
import org.futo.inputmethod.latin.uix.isDirectBootUnlocked
import org.futo.inputmethod.latin.uix.settings.ScrollableList
import org.futo.inputmethod.latin.uix.settings.pages.ParagraphText
import org.futo.inputmethod.latin.uix.settings.pages.PaymentSurface
@ -268,6 +269,8 @@ class ClipboardHistoryManager(val context: Context, val coroutineScope: Lifecycl
}
private fun saveClipboard() {
if(!context.isDirectBootUnlocked) return
coroutineScope.launch {
withContext(Dispatchers.IO) {
pruneOldItems()
@ -281,6 +284,8 @@ class ClipboardHistoryManager(val context: Context, val coroutineScope: Lifecycl
}
private suspend fun loadClipboard() {
if(!context.isDirectBootUnlocked) return
try {
val file = File(context.filesDir, "clipboard.json")
@ -374,6 +379,7 @@ val ClipboardHistoryAction = Action(
},
persistentStateInitialization = PersistentStateInitialization.OnKeyboardLoad,
windowImpl = { manager, persistent ->
val unlocked = manager.getContext().isDirectBootUnlocked
val clipboardHistoryManager = persistent as ClipboardHistoryManager
manager.getLifecycleScope().launch { clipboardHistoryManager.pruneOldItems() }
@ -443,7 +449,13 @@ val ClipboardHistoryAction = Action(
override fun WindowContents(keyboardShown: Boolean) {
val view = LocalView.current
val clipboardHistory = useDataStore(ClipboardHistoryEnabled, blocking = true)
if(!clipboardHistory.value) {
if(!unlocked) {
ScrollableList {
PaymentSurface(isPrimary = true, title = "Device Locked") {
ParagraphText("Please unlock your device to access clipboard history")
}
}
} else if(!clipboardHistory.value) {
ScrollableList {
PaymentSurface(isPrimary = true, title = "Clipboard History Inactive") {
ParagraphText("Clipboard history is not enabled. To save clipboard items, you can enable clipboard history. This will keep up to 25 items for 3 days unless pinned. Passwords and other items marked sensitive are excluded from history.")

View File

@ -1,7 +1,6 @@
package org.futo.inputmethod.latin.uix.settings
import android.content.SharedPreferences
import android.preference.PreferenceManager
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
@ -20,6 +19,7 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import org.futo.inputmethod.latin.uix.PreferenceUtils
import org.futo.inputmethod.latin.uix.SettingsKey
import org.futo.inputmethod.latin.uix.dataStore
import org.futo.inputmethod.latin.uix.getSetting
@ -94,7 +94,7 @@ fun <T> useDataStore(key: SettingsKey<T>, blocking: Boolean = false): DataStoreI
fun<T> useSharedPrefsGeneric(key: String, default: T, get: (SharedPreferences, String, T) -> T, put: (SharedPreferences, String, T) -> Unit): DataStoreItem<T> {
val coroutineScope = rememberCoroutineScope()
val context = LocalContext.current
val sharedPrefs = remember { PreferenceManager.getDefaultSharedPreferences(context) }
val sharedPrefs = remember { PreferenceUtils.getDefaultSharedPreferences(context) }
val value = remember { mutableStateOf(get(sharedPrefs, key, default)) }
@ -158,7 +158,7 @@ fun useSharedPrefsInt(key: String, default: Int): DataStoreItem<Int> {
@Composable
private fun<T> SyncDataStoreToPreferences(key: SettingsKey<T>, update: (newValue: T, editor: SharedPreferences.Editor) -> Unit) {
val context = LocalContext.current
val sharedPrefs = remember { PreferenceManager.getDefaultSharedPreferences(context) }
val sharedPrefs = remember { PreferenceUtils.getDefaultSharedPreferences(context) }
val value = useDataStoreValueBlocking(key)
LaunchedEffect(value) {

View File

@ -1,7 +1,6 @@
package org.futo.inputmethod.latin.uix.settings.pages
import android.content.Context
import android.preference.PreferenceManager
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.layout.Box
@ -59,6 +58,7 @@ import org.futo.inputmethod.latin.uix.AndroidTextInput
import org.futo.inputmethod.latin.uix.KeyHintsSetting
import org.futo.inputmethod.latin.uix.KeyboardBottomOffsetSetting
import org.futo.inputmethod.latin.uix.KeyboardHeightMultiplierSetting
import org.futo.inputmethod.latin.uix.PreferenceUtils
import org.futo.inputmethod.latin.uix.SHOW_EMOJI_SUGGESTIONS
import org.futo.inputmethod.latin.uix.SettingsKey
import org.futo.inputmethod.latin.uix.actions.AllActions
@ -437,7 +437,7 @@ fun TypingScreen(navController: NavHostController = rememberNavController()) {
val (vibration, _) = useDataStore(key = vibrationDurationSetting.key, default = vibrationDurationSetting.default)
LaunchedEffect(vibration) {
val sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context)
val sharedPrefs = PreferenceUtils.getDefaultSharedPreferences(context)
withContext(Dispatchers.Main) {
sharedPrefs.edit {
putInt(PREF_VIBRATION_DURATION_SETTINGS, vibration)

View File

@ -69,20 +69,10 @@ public final class ImportantNoticeUtils {
}
}
@UsedForTesting
static SharedPreferences getImportantNoticePreferences(final Context context) {
return context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
}
@UsedForTesting
static boolean hasContactsNoticeShown(final Context context) {
return getImportantNoticePreferences(context).getBoolean(
KEY_SUGGEST_CONTACTS_NOTICE, false);
}
public static boolean shouldShowImportantNotice(final Context context,
final SettingsValues settingsValues) {
// Check to see whether "Use Contacts" is enabled by the user.
/*
if (!settingsValues.mUseContactsDict) {
return false;
}
@ -107,8 +97,8 @@ public final class ImportantNoticeUtils {
if (hasContactsNoticeTimeoutPassed(context, System.currentTimeMillis())) {
updateContactsNoticeShown(context);
return false;
}
return true;
}*/
return false;
}
public static String getSuggestContactsNoticeTitle(final Context context) {
@ -118,23 +108,11 @@ public final class ImportantNoticeUtils {
@UsedForTesting
static boolean hasContactsNoticeTimeoutPassed(
final Context context, final long currentTimeInMillis) {
final SharedPreferences prefs = getImportantNoticePreferences(context);
if (!prefs.contains(KEY_TIMESTAMP_OF_CONTACTS_NOTICE)) {
prefs.edit()
.putLong(KEY_TIMESTAMP_OF_CONTACTS_NOTICE, currentTimeInMillis)
.apply();
}
final long firstDisplayTimeInMillis = prefs.getLong(
KEY_TIMESTAMP_OF_CONTACTS_NOTICE, currentTimeInMillis);
final long elapsedTime = currentTimeInMillis - firstDisplayTimeInMillis;
return elapsedTime >= TIMEOUT_OF_IMPORTANT_NOTICE;
return false;
}
public static void updateContactsNoticeShown(final Context context) {
getImportantNoticePreferences(context)
.edit()
.putBoolean(KEY_SUGGEST_CONTACTS_NOTICE, true)
.remove(KEY_TIMESTAMP_OF_CONTACTS_NOTICE)
.apply();
}
}

View File

@ -4,6 +4,7 @@ import android.content.Context
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import org.futo.inputmethod.latin.uix.isDirectBootUnlocked
import java.io.File
@Serializable
@ -23,6 +24,8 @@ data class HistoryLogForTraining(
)
fun saveHistoryLogBackup(context: Context, log: List<HistoryLogForTraining>) {
if(!context.isDirectBootUnlocked) return
val json = Json.encodeToString(log)
val file = File(context.cacheDir, "historyLog.json")
@ -30,6 +33,8 @@ fun saveHistoryLogBackup(context: Context, log: List<HistoryLogForTraining>) {
}
fun loadHistoryLogBackup(context: Context, to: MutableList<HistoryLogForTraining>) {
if(!context.isDirectBootUnlocked) return
try {
val file = File(context.cacheDir, "historyLog.json")
if(file.exists()) {

View File

@ -25,6 +25,7 @@ import kotlinx.coroutines.withContext
import org.futo.inputmethod.latin.R
import org.futo.inputmethod.latin.uix.USE_TRANSFORMER_FINETUNING
import org.futo.inputmethod.latin.uix.getSetting
import org.futo.inputmethod.latin.uix.isDirectBootUnlocked
import java.io.File
import java.util.concurrent.TimeUnit
@ -289,6 +290,7 @@ class TrainingWorker(val context: Context, val parameters: WorkerParameters) : C
private val WORKER_TAG: String = "TRAINING_WORKER"
public fun scheduleTrainingWorkerBackground(context: Context) {
if(!context.isDirectBootUnlocked) return
val workManager = WorkManager.getInstance(context)
workManager.cancelAllWorkByTag(WORKER_TAG)

View File

@ -2,6 +2,7 @@ package org.futo.inputmethod.latin
import android.app.Application
import android.content.Context
import android.os.UserManager
import androidx.datastore.preferences.core.Preferences
import org.acra.ACRA
import org.acra.config.dialog
@ -12,35 +13,40 @@ import org.acra.data.StringFormat
import org.acra.ktx.initAcra
class CrashLoggingApplication : Application() {
override fun attachBaseContext(base: Context?) {
super.attachBaseContext(base)
initAcra {
reportFormat = StringFormat.JSON
val userManager = getSystemService(Context.USER_SERVICE) as UserManager
if(userManager.isUserUnlocked) {
println("Initializing ACRA, as user is unlocked")
initAcra {
reportFormat = StringFormat.JSON
dialog {
text = getString(
//if(BuildConfig.ENABLE_ACRA) {
// R.string.crashed_text
//} else {
dialog {
text = getString(
//if(BuildConfig.ENABLE_ACRA) {
// R.string.crashed_text
//} else {
R.string.crashed_text_email
//}
)
title = getString(R.string.crashed_title)
positiveButtonText = getString(R.string.crash_report_accept)
negativeButtonText = getString(R.string.crash_report_reject)
resTheme = android.R.style.Theme_DeviceDefault_Dialog
}
//}
)
title = getString(R.string.crashed_title)
positiveButtonText = getString(R.string.crash_report_accept)
negativeButtonText = getString(R.string.crash_report_reject)
resTheme = android.R.style.Theme_DeviceDefault_Dialog
}
//if(BuildConfig.ENABLE_ACRA) {
// httpSender {
// uri = BuildConfig.ACRA_URL
// basicAuthLogin = BuildConfig.ACRA_USER
// basicAuthPassword = BuildConfig.ACRA_PASSWORD
// httpMethod = HttpSender.Method.POST
// }
//} else {
//if(BuildConfig.ENABLE_ACRA) {
// httpSender {
// uri = BuildConfig.ACRA_URL
// basicAuthLogin = BuildConfig.ACRA_USER
// basicAuthPassword = BuildConfig.ACRA_PASSWORD
// httpMethod = HttpSender.Method.POST
// }
//} else {
mailSender {
mailTo = "keyboard@futo.org"
reportAsFile = true
@ -49,14 +55,23 @@ class CrashLoggingApplication : Application() {
body =
"I experienced this crash. My version: ${BuildConfig.VERSION_NAME}.\n\n(Enter details here if necessary)"
}
//}
//}
}
acraInitialized = true
} else {
println("Skipping ACRA, as user is locked")
}
}
companion object {
var acraInitialized = false
fun logPreferences(preferences: Preferences) {
preferences.asMap().forEach {
ACRA.errorReporter.putCustomData(it.key.name, it.value.toString())
if(acraInitialized) {
preferences.asMap().forEach {
ACRA.errorReporter.putCustomData(it.key.name, it.value.toString())
}
}
}
}