mirror of
https://gitlab.futo.org/keyboard/latinime.git
synced 2024-09-28 14:54:30 +01:00
Add support for Direct Boot by loading default preferences for first unlock
This commit is contained in:
parent
7d492897cc
commit
b6206e3059
@ -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"/>
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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> =
|
||||
|
@ -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.")
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
|
@ -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();
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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()) {
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user