diff --git a/README.md b/README.md
index 0e940f9..0dbe456 100644
--- a/README.md
+++ b/README.md
@@ -10,13 +10,19 @@ git submodule update --init --recursive
```
Add in the app's `settings.gradle`:
-```groovy
-include(":app", ":bohio")
-project(":bohio").projectDir = file("bohio")
+```kotlin
+include(":app")
+include(":bohio")
+
+// Activate For Release
+//project(":bohio").projectDir = file("bohio")
+
+// Activate For Bohio Development (and locate your local source)
+project(":bohio").projectDir = file("../git-mod_bohio")
```
Add in the app's `app/build.gradle`:
-```groovy
+```kotlin
dependencies {
implementation(project(":bohio"))
}
@@ -33,9 +39,9 @@ Make sure the app's theme extends `Theme.Rama.Base` in `themes.xml`.
For F-Droid, add `submodules: true` to the relevant build entry in the app's metadata so `git submodule update --init --recursive` runs after checkout. Keep the `bohio` repo public and avoid rewriting history on any commit a published app version's submodule pointer references.
-## Maintanence
+## Maintenance
Update submodule
```bash
git submodule update --remote bohio
-```
\ No newline at end of file
+```
diff --git a/build.gradle b/build.gradle
index 0e8d660..f01e2c9 100644
--- a/build.gradle
+++ b/build.gradle
@@ -29,9 +29,5 @@ android {
}
dependencies {
- // 'api' so consuming apps get these transitively without redeclaring.
- // CsActivity extends AppCompatActivity and uses OnBackPressedCallback
- // (from androidx.activity, pulled in transitively by appcompat).
api 'androidx.appcompat:appcompat:1.6.1'
- api 'androidx.fragment:fragment-ktx:1.6.2'
}
diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml
deleted file mode 100644
index bed3e93..0000000
--- a/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
-
-
-
-
-
diff --git a/src/main/java/com/rama/bohio/activity/BohioActivity.kt b/src/main/java/com/rama/bohio/activity/BohioActivity.kt
new file mode 100644
index 0000000..deefd1b
--- /dev/null
+++ b/src/main/java/com/rama/bohio/activity/BohioActivity.kt
@@ -0,0 +1,192 @@
+package com.rama.bohio.activity
+
+import android.content.Context
+import android.content.SharedPreferences
+import android.content.pm.ActivityInfo
+import android.content.res.Configuration
+import android.os.Build
+import android.os.Bundle
+import android.view.View
+import android.view.WindowInsets
+import android.view.WindowManager
+import androidx.activity.ComponentActivity
+import com.rama.bohio.managers.FontManager
+import com.rama.bohio.managers.ThemeManager
+import com.rama.bohio.objects.PrefKeys
+import com.rama.bohio.util.Dimens.dpToPx
+import com.rama.bohio.util.LocaleHelper
+
+abstract class BohioActivity : ComponentActivity() {
+
+ private var lastKnownLanguage: String? = null
+ private var lastKnownTheme: String? = null
+ private var lastKnownUiScale: Float = -1f
+
+ // Context wrapping (locale + UI scale)
+
+ override fun attachBaseContext(newBase: Context) {
+ val localeContext = LocaleHelper.wrapContext(newBase)
+
+ val scale = rawPrefs(localeContext).getFloat(PrefKeys.APP_UI_SCALE, 1f)
+
+ val context = if (scale != 1f) {
+ val config = Configuration(localeContext.resources.configuration)
+ config.densityDpi =
+ (localeContext.resources.displayMetrics.densityDpi * scale).toInt()
+ localeContext.createConfigurationContext(config)
+ } else {
+ localeContext
+ }
+
+ super.attachBaseContext(context)
+ }
+
+ // Lifecycle
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ val p = rawPrefs(this)
+ lastKnownLanguage = p.getString(PrefKeys.APP_LANGUAGE, "")
+ lastKnownTheme = p.getString(PrefKeys.APP_THEME_NAME, "")
+ lastKnownUiScale = p.getFloat(PrefKeys.APP_UI_SCALE, 1f)
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ window.setDecorFitsSystemWindows(false)
+ } else {
+ @Suppress("DEPRECATION")
+ window.decorView.systemUiVisibility =
+ View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
+ View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
+ View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ }
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ window.attributes.layoutInDisplayCutoutMode =
+ WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
+ }
+ }
+
+ override fun onResume() {
+ super.onResume()
+
+ val p = rawPrefs(this)
+
+ val lang = p.getString(PrefKeys.APP_LANGUAGE, "") ?: ""
+ if (lang != lastKnownLanguage) {
+ lastKnownLanguage = lang
+ if (shouldRecreateOnSettingsChange()) {
+ recreate(); return
+ }
+ }
+
+ val theme = p.getString(PrefKeys.APP_THEME_NAME, "") ?: ""
+ if (theme != lastKnownTheme) {
+ lastKnownTheme = theme
+ if (shouldRecreateOnSettingsChange()) {
+ recreate(); return
+ }
+ }
+
+ val scale = p.getFloat(PrefKeys.APP_UI_SCALE, 1f)
+ if (scale != lastKnownUiScale) {
+ lastKnownUiScale = scale
+ if (shouldRecreateOnSettingsChange()) {
+ recreate(); return
+ }
+ }
+
+ val preventRotation = p.getBoolean(PrefKeys.SYSTEM_PREVENT_ROTATION, false)
+ applyRotationLock(preventRotation)
+
+ ThemeManager.applyTheme(this, contentRoot())
+ }
+
+ override fun onWindowFocusChanged(hasFocus: Boolean) {
+ super.onWindowFocusChanged(hasFocus)
+ if (hasFocus) applySystemBarVisibility()
+ }
+
+ // Public API
+
+ protected open fun shouldRecreateOnSettingsChange(): Boolean = true
+
+ fun applyCurrentTheme(root: View? = null) {
+ ThemeManager.applyTheme(this, root ?: contentRoot())
+ applyNavBarColor()
+ }
+
+ fun refreshFont() {
+ FontManager.applyFont(this, contentRoot())
+ }
+
+ fun applyRotationLock(lock: Boolean) {
+ requestedOrientation =
+ if (lock) ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
+ else ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
+ }
+
+ protected fun applyEdgeToEdgePadding(root: View) {
+ val paddingInline = dpToPx(this, 16f)
+ val paddingBlock = dpToPx(this, 8f)
+
+ root.setOnApplyWindowInsetsListener { view, insets ->
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ val sysBars = insets.getInsets(
+ WindowInsets.Type.systemBars() or WindowInsets.Type.displayCutout()
+ )
+ val ime = insets.getInsets(WindowInsets.Type.ime())
+ val bottomInset = if (insets.isVisible(WindowInsets.Type.ime())) ime.bottom
+ else sysBars.bottom
+ view.setPadding(
+ sysBars.left + paddingInline,
+ sysBars.top + paddingBlock,
+ sysBars.right + paddingInline,
+ bottomInset + paddingBlock,
+ )
+ } else {
+ @Suppress("DEPRECATION")
+ view.setPadding(
+ insets.systemWindowInsetLeft + paddingInline,
+ insets.systemWindowInsetTop + paddingBlock,
+ insets.systemWindowInsetRight + paddingInline,
+ insets.systemWindowInsetBottom + paddingBlock,
+ )
+ }
+ insets
+ }
+ }
+
+ // Private helpers
+
+ protected fun applyNavBarColor() {
+ val theme = rawPrefs(this).getString(PrefKeys.APP_THEME_NAME, "") ?: ""
+ val palette = ThemeManager.paletteFor(theme, this)
+ window.navigationBarColor = palette.bg_1
+ }
+
+ protected open fun isSystemBarVisible(): Boolean {
+ return rawPrefs(this)
+ .getBoolean(PrefKeys.SYSTEM_BAR_VISIBLE, true)
+ }
+
+ private fun applySystemBarVisibility() {
+ if (isSystemBarVisible()) {
+ @Suppress("DEPRECATION")
+ window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ } else {
+ @Suppress("DEPRECATION")
+ window.decorView.systemUiVisibility =
+ View.SYSTEM_UI_FLAG_FULLSCREEN or
+ View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
+ View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
+ View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
+ }
+ contentRoot().requestApplyInsets()
+ }
+
+ private fun contentRoot(): View = findViewById(android.R.id.content)
+
+ private fun rawPrefs(context: Context): SharedPreferences =
+ context.getSharedPreferences("settings", Context.MODE_PRIVATE)
+}
\ No newline at end of file
diff --git a/src/main/java/com/rama/bohio/dialogs/ColorPickerDialog.kt b/src/main/java/com/rama/bohio/dialogs/ColorPickerDialog.kt
new file mode 100644
index 0000000..db06e08
--- /dev/null
+++ b/src/main/java/com/rama/bohio/dialogs/ColorPickerDialog.kt
@@ -0,0 +1,92 @@
+package com.rama.bohio.dialogs
+
+import android.app.Activity
+import android.app.Dialog
+import android.graphics.Color
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup.LayoutParams.MATCH_PARENT
+import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
+import android.widget.Button
+import android.widget.EditText
+import android.widget.Toast
+import com.rama.bohio.R
+import com.rama.bohio.managers.ThemeManager
+import com.rama.bohio.widgets.HSVSquareView
+import com.rama.bohio.widgets.HueStripView
+
+object ColorPickerDialog {
+
+ fun show(
+ activity: Activity,
+ initialColor: Int,
+ onColorSelected: (Int) -> Unit
+ ) {
+ val dialog = Dialog(activity)
+ val view = LayoutInflater.from(activity).inflate(R.layout.wd_color_picker_dialog, null)
+
+ dialog.setContentView(view)
+ dialog.window?.setLayout(MATCH_PARENT, WRAP_CONTENT)
+
+ ThemeManager.applyTheme(activity, view)
+
+ val preview = view.findViewById(R.id.preview)
+ val hexInput = view.findViewById(R.id.hex_input)
+ val applyButton = view.findViewById