diff --git a/app/src/main/java/dev/patrickgold/florisboard/ime/core/FlorisBoard.kt b/app/src/main/java/dev/patrickgold/florisboard/ime/core/FlorisBoard.kt index 56e9df80..29a4ca8f 100644 --- a/app/src/main/java/dev/patrickgold/florisboard/ime/core/FlorisBoard.kt +++ b/app/src/main/java/dev/patrickgold/florisboard/ime/core/FlorisBoard.kt @@ -57,7 +57,7 @@ import dev.patrickgold.florisboard.ime.theme.Theme import dev.patrickgold.florisboard.ime.theme.ThemeManager import dev.patrickgold.florisboard.setup.SetupActivity import dev.patrickgold.florisboard.util.* -import kotlinx.coroutines.launch +import kotlinx.coroutines.* import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.Transient @@ -84,6 +84,7 @@ class FlorisBoard : InputMethodService(), LifecycleOwner, FlorisClipboardManager private val serviceLifecycleDispatcher: ServiceLifecycleDispatcher = ServiceLifecycleDispatcher(this) private val uiScope: LifecycleCoroutineScope get() = lifecycle.coroutineScope + private var devtoolsOverlaySyncJob: Job? = null /** * The theme context for the UI. Must only be used for inflating/creating Views for the keyboard UI, else the IME @@ -393,6 +394,17 @@ class FlorisBoard : InputMethodService(), LifecycleOwner, FlorisClipboardManager onSubtypeChanged(activeSubtype) setActiveInput(R.id.text_input) + if (prefs.devtools.enabled && prefs.devtools.showHeapMemoryStats) { + devtoolsOverlaySyncJob?.cancel() + devtoolsOverlaySyncJob = uiScope.launch(Dispatchers.Default) { + while (true) { + if (!isActive) break + withContext(Dispatchers.Main) { inputView?.invalidate() } + delay(1000) + } + } + } + eventListeners.toList().forEach { it?.onWindowShown() } } @@ -406,6 +418,9 @@ class FlorisBoard : InputMethodService(), LifecycleOwner, FlorisClipboardManager } isWindowShown = false + devtoolsOverlaySyncJob?.cancel() + devtoolsOverlaySyncJob = null + eventListeners.toList().forEach { it?.onWindowHidden() } } diff --git a/app/src/main/java/dev/patrickgold/florisboard/ime/core/InputView.kt b/app/src/main/java/dev/patrickgold/florisboard/ime/core/InputView.kt index c448e143..bf57b8ce 100644 --- a/app/src/main/java/dev/patrickgold/florisboard/ime/core/InputView.kt +++ b/app/src/main/java/dev/patrickgold/florisboard/ime/core/InputView.kt @@ -18,6 +18,11 @@ package dev.patrickgold.florisboard.ime.core import android.content.Context import android.content.res.Configuration +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.graphics.Typeface +import android.text.TextPaint import android.util.AttributeSet import android.util.DisplayMetrics import android.view.ViewGroup @@ -58,6 +63,13 @@ class InputView : LinearLayout { var oneHandedCtrlPanelEnd: ViewGroup? = null private set + private val overlayTextPaint: TextPaint = TextPaint().apply { + color = Color.GREEN + textAlign = Paint.Align.RIGHT + textSize = resources.getDimension(R.dimen.devtools_memory_overlay_textSize) + typeface = Typeface.MONOSPACE + } + constructor(context: Context) : this(context, null) constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0) constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super( @@ -161,4 +173,34 @@ class InputView : LinearLayout { resources.getDimension(R.dimen.inputView_baseHeight) ) } + + override fun dispatchDraw(canvas: Canvas?) { + super.dispatchDraw(canvas) + canvas ?: return + + if (prefs.devtools.enabled && prefs.devtools.showHeapMemoryStats) { + try { + // Note: the below code only gets the heap size in MB, the actual RAM usage (native or others) can be + // a lot higher + // Source: https://stackoverflow.com/a/19267315/6801193 + val runtime = Runtime.getRuntime() + val usedMemInMB = (runtime.totalMemory() - runtime.freeMemory()) / 1048576L + val maxHeapSizeInMB = runtime.maxMemory() / 1048576L + val availHeapSizeInMB = maxHeapSizeInMB - usedMemInMB + val output = listOf( + "heap mem:", + String.format("used=%4dMB", usedMemInMB), + String.format("max=%4dMB", maxHeapSizeInMB), + String.format("avail=%4dMB", availHeapSizeInMB), + ) + val x = measuredWidth.toFloat() + var y = overlayTextPaint.descent() - overlayTextPaint.ascent() + for (line in output) { + canvas.drawText(line, x, y, overlayTextPaint) + y += overlayTextPaint.descent() - overlayTextPaint.ascent() + } + } catch (_: Throwable) { + } + } + } } diff --git a/app/src/main/java/dev/patrickgold/florisboard/ime/core/PrefHelper.kt b/app/src/main/java/dev/patrickgold/florisboard/ime/core/PrefHelper.kt index 95d3fab7..2a17ce1f 100644 --- a/app/src/main/java/dev/patrickgold/florisboard/ime/core/PrefHelper.kt +++ b/app/src/main/java/dev/patrickgold/florisboard/ime/core/PrefHelper.kt @@ -48,7 +48,9 @@ class PrefHelper( private val cacheString: HashMap = hashMapOf() val advanced = Advanced(this) + val clipboard = Clipboard(this) val correction = Correction(this) + val devtools = Devtools(this) val gestures = Gestures(this) val glide = Glide(this) val internal = Internal(this) @@ -57,7 +59,6 @@ class PrefHelper( val smartbar = Smartbar(this) val suggestion = Suggestion(this) val theme = Theme(this) - val clipboard = Clipboard(this) /** * Checks the cache if an entry for [key] exists, else calls [getPrefInternal] to retrieve the @@ -222,6 +223,23 @@ class PrefHelper( set(v) = prefHelper.setPref(REMEMBER_CAPS_LOCK_STATE, v) } + /** + * Wrapper class for devtools preferences. + */ + class Devtools(private val prefHelper: PrefHelper) { + companion object { + const val ENABLED = "devtools__enabled" + const val SHOW_HEAP_MEMORY_STATS = "devtools__show_heap_memory_stats" + } + + var enabled: Boolean + get() = prefHelper.getPref(ENABLED, false) + set(v) = prefHelper.setPref(ENABLED, v) + var showHeapMemoryStats: Boolean + get() = prefHelper.getPref(SHOW_HEAP_MEMORY_STATS, false) + set(v) = prefHelper.setPref(SHOW_HEAP_MEMORY_STATS, v) + } + /** * Wrapper class for gestures preferences. */ diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index f5a7fc82..67c7a6a0 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -26,6 +26,7 @@ 12sp 21sp 22sp + 8sp 8dp 6dp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f37cb60b..3656cd48 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -310,6 +310,10 @@ Show app icon in launcher Force private mode Will disable any features which have to temporarily work with your input data + Enable developer tools + Tools specifically designed for debugging and troubleshooting + Show heap memory stats + Overlays the heap memory usage and max size in the top-right corner About diff --git a/app/src/main/res/xml/prefs_advanced.xml b/app/src/main/res/xml/prefs_advanced.xml index 5ad74da7..af3e1004 100644 --- a/app/src/main/res/xml/prefs_advanced.xml +++ b/app/src/main/res/xml/prefs_advanced.xml @@ -26,4 +26,21 @@ app:summary="@string/pref__advanced__force_private_mode__summary" app:useSimpleSummaryProvider="true"/> + + + +