Skip to content

Commit

Permalink
[fix]: using flows in preferences
Browse files Browse the repository at this point in the history
  • Loading branch information
F0x1d committed Jun 29, 2024
1 parent b31b7fb commit 614ce55
Show file tree
Hide file tree
Showing 14 changed files with 116 additions and 144 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class AndroidFeatureConventionPlugin : Plugin<Project> {
ksp(library("androidx-room-compiler"))

implementation(library("insetter"))
implementation(library("flow-preferences"))
}
}
}
1 change: 1 addition & 0 deletions core/core-preferences/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ dependencies {
implementation(project(":core:core-database"))

implementation(libs.bundles.androidx)
implementation(libs.flow.preferences)
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ import com.f0x1d.logfox.database.entity.CrashType
import com.f0x1d.logfox.model.logline.LogLine
import com.f0x1d.logfox.model.preferences.ShowLogValues
import com.f0x1d.logfox.preferences.shared.base.BasePreferences
import com.fredporciuncula.flow.preferences.keyFlow
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
import java.util.Date
import javax.inject.Inject
import javax.inject.Singleton
Expand All @@ -21,29 +25,50 @@ class AppPreferences @Inject constructor(
const val DATE_FORMAT_DEFAULT = "dd.MM"
const val TIME_FORMAT_DEFAULT = "HH:mm:ss.SSS"

const val LOGS_EXPANDED_DEFAULT = false
const val LOGS_UPDATE_INTERVAL_DEFAULT = 300L
const val LOGS_TEXT_SIZE_DEFAULT = 14
const val LOGS_DISPLAY_LIMIT_DEFAULT = 10000

const val TERMINAL_INDEX_DEFAULT = 0
}

var dateFormat
get() = getNullable("pref_date_format", DATE_FORMAT_DEFAULT)
set(value) { put("pref_date_format", value) }
val dateFormatFlow get() = flowSharedPreferences.getString("pref_date_format", DATE_FORMAT_DEFAULT).asFlow()

var timeFormat
get() = getNullable("pref_time_format", TIME_FORMAT_DEFAULT)
set(value) { put("pref_time_format", value) }
val timeFormatFlow get() = flowSharedPreferences.getString("pref_time_format", TIME_FORMAT_DEFAULT).asFlow()

var logsUpdateInterval
get() = get("pref_logs_update_interval", LOGS_UPDATE_INTERVAL_DEFAULT)
set(value) { put("pref_logs_update_interval", value) }
val logsUpdateIntervalFlow get() = flowSharedPreferences.getLong(
key = "pref_logs_update_interval",
defaultValue = LOGS_UPDATE_INTERVAL_DEFAULT,
).asFlow()

var logsTextSize
get() = get("pref_logs_text_size", LOGS_TEXT_SIZE_DEFAULT)
set(value) { put("pref_logs_text_size", value) }
val logsTextSizeFlow get() = flowSharedPreferences.getInt("pref_logs_text_size", LOGS_TEXT_SIZE_DEFAULT).asFlow()

var logsDisplayLimit
get() = get("pref_logs_display_limit", LOGS_DISPLAY_LIMIT_DEFAULT)
set(value) { put("pref_logs_display_limit", value) }
val logsDisplayLimitFlow get() = flowSharedPreferences.getInt(
key = "pref_logs_display_limit",
defaultValue = LOGS_DISPLAY_LIMIT_DEFAULT,
).asFlow()

var logsExpanded
get() = get("pref_logs_expanded", false)
get() = get("pref_logs_expanded", LOGS_EXPANDED_DEFAULT)
set(value) { put("pref_logs_expanded", value) }
val logsExpandedFlow get() = flowSharedPreferences.getBoolean("pref_logs_expanded", LOGS_EXPANDED_DEFAULT).asFlow()

var resumeLoggingWithBottomTouch
get() = get("pref_resume_logs_with_touch", true)
set(value) { put("pref_resume_logs_with_touch", value) }
Expand Down Expand Up @@ -76,9 +101,18 @@ class AppPreferences @Inject constructor(
get() = get("pref_show_log_content", true)
set(value) { put("pref_show_log_content", value) }

val showLogValuesFlow get() = sharedPreferences.keyFlow.filter { key ->
key?.startsWith("pref_show_log") == true
}.onStart { emit("initial") }.map { showLogValues }

var selectedTerminalIndex
get() = get("pref_selected_terminal_index", 0)
get() = get("pref_selected_terminal_index", TERMINAL_INDEX_DEFAULT)
set(value) { put("pref_selected_terminal_index", value) }
val selectedTerminalIndexFlow get() = flowSharedPreferences.getInt(
key = "pref_selected_terminal_index",
defaultValue = TERMINAL_INDEX_DEFAULT,
).asFlow()

var fallbackToDefaultTerminal
get() = get("pref_fallback_to_default_terminal", true)
set(value) { put("pref_fallback_to_default_terminal", value) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,17 @@ package com.f0x1d.logfox.preferences.shared.base
import android.content.Context
import android.content.SharedPreferences
import androidx.core.content.edit
import com.fredporciuncula.flow.preferences.FlowSharedPreferences

abstract class BasePreferences(context: Context) {

val sharedPreferences by lazy { providePreferences(context) }
protected val sharedPreferences by lazy { providePreferences(context) }
protected val flowSharedPreferences by lazy { FlowSharedPreferences(sharedPreferences) }

abstract fun providePreferences(context: Context): SharedPreferences

inline fun <reified T> asLiveData(key: String, defValue: T?) = object : SharedPreferenceLiveData<T?>(sharedPreferences, key, defValue) {
override fun fromPreferences(key: String, defValue: T?) = try {
get(key, defValue)
} catch (e: RuntimeException) {
getNullable(key, defValue)
}
}

@Suppress("UNCHECKED_CAST")
inline fun <reified T> put(key: String, value: T?) = sharedPreferences.edit {
protected inline fun <reified T> put(key: String, value: T?) = sharedPreferences.edit {
when (value) {
null -> putString(key, null)

Expand All @@ -32,7 +26,7 @@ abstract class BasePreferences(context: Context) {
}
}

inline fun <reified T> get(key: String, defaultValue: T): T = when (defaultValue) {
protected inline fun <reified T> get(key: String, defaultValue: T): T = when (defaultValue) {
is Boolean -> sharedPreferences.getBoolean(key, defaultValue) as T
is Int -> sharedPreferences.getInt(key, defaultValue) as T
is Long -> sharedPreferences.getLong(key, defaultValue) as T
Expand All @@ -42,17 +36,12 @@ abstract class BasePreferences(context: Context) {
}

@Suppress("UNCHECKED_CAST")
inline fun <reified T> getNullable(key: String, defaultValue: T?): T? = when (defaultValue) {
protected inline fun <reified T> getNullable(key: String, defaultValue: T?): T? = when (defaultValue) {
null -> sharedPreferences.getString(key, defaultValue) as T?

is String -> sharedPreferences.getString(key, defaultValue) as T?
is Set<*> -> sharedPreferences.getStringSet(key, defaultValue as Set<String>) as T?

else -> error("Type of $defaultValue is not supported")
}

fun registerListener(listener: SharedPreferences.OnSharedPreferenceChangeListener) =
sharedPreferences.registerOnSharedPreferenceChangeListener(listener)
fun unregisterListener(listener: SharedPreferences.OnSharedPreferenceChangeListener) =
sharedPreferences.unregisterOnSharedPreferenceChangeListener(listener)
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@ package com.f0x1d.logfox.ui.view

import android.view.LayoutInflater
import android.view.inputmethod.InputMethodManager
import androidx.lifecycle.LifecycleOwner
import androidx.preference.Preference
import com.f0x1d.logfox.context.inputMethodManager
import com.f0x1d.logfox.preferences.shared.appPreferences
import com.f0x1d.logfox.strings.Strings
import com.f0x1d.logfox.ui.databinding.DialogTextBinding
import com.google.android.material.dialog.MaterialAlertDialogBuilder
Expand Down Expand Up @@ -66,30 +64,3 @@ fun Preference.setupAsListPreference(
.show()
return@setOnPreferenceClickListener true
}

fun Preference.observeAndUpdateSummaryForList(
observer: LifecycleOwner,
defValue: Int,
items: Array<String>
) = observeAndUpdateSummary(observer, defValue) {
summary = items[it]
}

inline fun <reified T> Preference.observeAndUpdateSummary(
observer: LifecycleOwner,
defValue: T
) {
observeAndUpdateSummary(observer, defValue) {
summary = it.toString()
}
}

inline fun <reified T> Preference.observeAndUpdateSummary(
observer: LifecycleOwner,
defValue: T,
crossinline block: (T) -> Unit
) {
context.appPreferences.asLiveData(key, defValue).observe(observer) {
block(it ?: return@observe)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import android.content.Intent
import android.content.SharedPreferences
import android.os.Binder
import android.os.IBinder
import android.util.Log
import androidx.core.app.NotificationCompat
import androidx.core.app.ServiceCompat
import androidx.lifecycle.LifecycleService
import androidx.lifecycle.lifecycleScope
import com.f0x1d.logfox.arch.di.MainDispatcher
import com.f0x1d.logfox.context.LOGGING_STATUS_CHANNEL_ID
import com.f0x1d.logfox.context.activityManager
import com.f0x1d.logfox.context.toast
Expand All @@ -26,12 +26,14 @@ import com.f0x1d.logfox.terminals.DefaultTerminal
import com.f0x1d.logfox.terminals.base.Terminal
import com.f0x1d.logfox.ui.Icons
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Job
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
Expand Down Expand Up @@ -69,6 +71,10 @@ class LoggingService : LifecycleService(), SharedPreferences.OnSharedPreferenceC
@Inject
lateinit var terminals: Array<Terminal>

@MainDispatcher
@Inject
lateinit var mainDispatcher: CoroutineDispatcher

private val logs = LinkedList<LogLine>()
private val logsMutex = Mutex()
private var loggingJob: Job? = null
Expand All @@ -87,7 +93,6 @@ class LoggingService : LifecycleService(), SharedPreferences.OnSharedPreferenceC

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
super.onStartCommand(intent, flags, startId)
Log.e("f0x1d", "got intent ${intent?.action}")

when (intent?.action) {
ACTION_RESTART_LOGGING -> restartLogging()
Expand All @@ -102,11 +107,19 @@ class LoggingService : LifecycleService(), SharedPreferences.OnSharedPreferenceC
private fun startLogging() {
if (loggingJob?.isActive == true) return

var loggingTerminal = terminals[appPreferences.selectedTerminalIndex]
loggingInterval = appPreferences.logsUpdateInterval
logsDisplayLimit = appPreferences.logsDisplayLimit
lifecycleScope.launch {
appPreferences.logsUpdateIntervalFlow
.onEach { loggingInterval = it }
.launchIn(this)

appPreferences.logsDisplayLimitFlow
.onEach { logsDisplayLimit = it }
.launchIn(this)
}

appPreferences.registerListener(this)
var loggingTerminal = terminals[appPreferences.selectedTerminalIndex]
//loggingInterval = appPreferences.logsUpdateInterval
//logsDisplayLimit = appPreferences.logsDisplayLimit

loggingJob = lifecycleScope.launch {
try {
Expand All @@ -116,7 +129,7 @@ class LoggingService : LifecycleService(), SharedPreferences.OnSharedPreferenceC
startingId = idsCounter,
).catch { throwable ->
if (throwable is TerminalNotSupportedException) {
if (appPreferences.fallbackToDefaultTerminal) withContext(Dispatchers.Main) {
if (appPreferences.fallbackToDefaultTerminal) withContext(mainDispatcher) {
toast(Strings.terminal_unavailable_falling_back)

loggingTerminal.exit()
Expand All @@ -129,8 +142,6 @@ class LoggingService : LifecycleService(), SharedPreferences.OnSharedPreferenceC
}
}.collect { logLine ->
logsMutex.withLock {


logs.add(logLine)

while (logs.size > logsDisplayLimit)
Expand All @@ -149,7 +160,6 @@ class LoggingService : LifecycleService(), SharedPreferences.OnSharedPreferenceC
withContext(NonCancellable) {
recordingController.loggingStopped()
clearLogs().join()
appPreferences.unregisterListener(this@LoggingService)

loggingTerminal.exit()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.f0x1d.feature.logging.ui.fragment

import android.content.SharedPreferences
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
Expand Down Expand Up @@ -35,7 +34,7 @@ import dev.chrisbanes.insetter.applyInsetter
import kotlinx.coroutines.flow.update

@AndroidEntryPoint
class LogsFragment: BaseViewModelFragment<LogsViewModel, FragmentLogsBinding>(), SharedPreferences.OnSharedPreferenceChangeListener {
class LogsFragment: BaseViewModelFragment<LogsViewModel, FragmentLogsBinding>() {

override val viewModel by hiltNavGraphViewModels<LogsViewModel>(Directions.logsFragment)

Expand Down Expand Up @@ -77,8 +76,6 @@ class LogsFragment: BaseViewModelFragment<LogsViewModel, FragmentLogsBinding>(),
) = FragmentLogsBinding.inflate(inflater, container, false)

override fun FragmentLogsBinding.onViewCreated(view: View, savedInstanceState: Bundle?) {
viewModel.appPreferences.registerListener(this@LogsFragment)

requireContext().isHorizontalOrientation.also { horizontalOrientation ->
logsRecycler.applyInsetter {
type(navigationBars = true) {
Expand Down Expand Up @@ -216,6 +213,19 @@ class LogsFragment: BaseViewModelFragment<LogsViewModel, FragmentLogsBinding>(),
changingState = false
}

viewModel.apply {
appPreferences.logsTextSizeFlow.collectWithLifecycle {
adapter.textSize = it.toFloat()
}
appPreferences.logsExpandedFlow.collectWithLifecycle {
adapter.logsExpanded = it
}

appPreferences.showLogValuesFlow.collectWithLifecycle {
adapter.logsFormat = it
}
}

requireActivity().onBackPressedDispatcher.apply {
addCallback(viewLifecycleOwner, clearSelectionOnBackPressedCallback)
}
Expand Down Expand Up @@ -281,25 +291,4 @@ class LogsFragment: BaseViewModelFragment<LogsViewModel, FragmentLogsBinding>(),
logsRecycler.stopScroll()
logsRecycler.scrollToPosition(adapter.itemCount - 1)
}

override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
viewModel.appPreferences.apply {
when (key) {
"pref_logs_text_size" -> adapter.textSize = logsTextSize.toFloat()
"pref_logs_expanded" -> adapter.logsExpanded = logsExpanded

"pref_date_format", "pref_time_format" -> adapter.notifyItemRangeChanged(
0,
adapter.itemCount
)
}

if (key?.startsWith("pref_show_log") == true) adapter.logsFormat = showLogValues
}
}

override fun onDestroy() {
super.onDestroy()
viewModel.appPreferences.unregisterListener(this)
}
}
Loading

0 comments on commit 614ce55

Please sign in to comment.