From 6f15a5b97cc7910be70125e86f434daa516a683b Mon Sep 17 00:00:00 2001 From: Emma Date: Wed, 17 Apr 2024 14:43:02 -0400 Subject: [PATCH] Make system UI match freetube themes --- .../freetube/FreeTubeJavaScriptInterface.kt | 34 +++++++++++++++++++ .../io/freetubeapp/freetube/MainActivity.kt | 12 +++++++ src/renderer/App.js | 18 +++++++--- src/renderer/helpers/android.js | 24 +++++++++++++ 4 files changed, 84 insertions(+), 4 deletions(-) diff --git a/android/app/src/main/java/io/freetubeapp/freetube/FreeTubeJavaScriptInterface.kt b/android/app/src/main/java/io/freetubeapp/freetube/FreeTubeJavaScriptInterface.kt index 65beaacabff50..31e5bb4d0fc68 100644 --- a/android/app/src/main/java/io/freetubeapp/freetube/FreeTubeJavaScriptInterface.kt +++ b/android/app/src/main/java/io/freetubeapp/freetube/FreeTubeJavaScriptInterface.kt @@ -8,6 +8,7 @@ import android.app.NotificationManager import android.app.PendingIntent import android.content.Intent import android.graphics.BitmapFactory +import android.graphics.Color import android.media.MediaMetadata import android.media.session.MediaSession import android.media.session.PlaybackState @@ -19,6 +20,7 @@ import android.webkit.JavascriptInterface import androidx.activity.result.ActivityResult import androidx.annotation.RequiresApi import androidx.core.app.NotificationManagerCompat +import androidx.core.view.WindowCompat import androidx.documentfile.provider.DocumentFile import java.io.File import java.io.FileInputStream @@ -600,6 +602,38 @@ class FreeTubeJavaScriptInterface { } } } + + /** + * + */ + @JavascriptInterface + fun themeSystemUi(topHex: String, bottomHex: String, darkMode: Boolean = true) { + context.runOnUiThread { + val windowInsetsController = + WindowCompat.getInsetsController(context.window, context.window.decorView) + windowInsetsController.isAppearanceLightNavigationBars = !darkMode + windowInsetsController.isAppearanceLightStatusBars = !darkMode + fun hexToColour(hex: String) : Int { + return Color.rgb( + Integer.valueOf(hex.substring(1, 3), 16), + Integer.valueOf(hex.substring(3, 5), 16), + Integer.valueOf(hex.substring(5, 7), 16) + ) + } + context.window.navigationBarColor = hexToColour(topHex) + context.window.statusBarColor = hexToColour(bottomHex) + } + } + + @JavascriptInterface + fun getSystemTheme(): String { + if (context.darkMode) { + return "dark" + } else { + return "light" + } + } + private fun addNamedCallbackToPromise(promise: String, name: String) { context.runOnUiThread { context.webView.loadUrl("javascript: window['${promise}'].callbacks = window['${promise}'].callbacks || {}; window['${promise}'].callbacks.notify = (key, message) => window['${promise}'].callbacks[key].forEach(callback => callback(message)); window['${promise}'].callbacks['${name}'] = window['${promise}'].callbacks['${name}'] || []") diff --git a/android/app/src/main/java/io/freetubeapp/freetube/MainActivity.kt b/android/app/src/main/java/io/freetubeapp/freetube/MainActivity.kt index 06fc40f4b1b99..26b87be6691a9 100644 --- a/android/app/src/main/java/io/freetubeapp/freetube/MainActivity.kt +++ b/android/app/src/main/java/io/freetubeapp/freetube/MainActivity.kt @@ -45,6 +45,7 @@ class MainActivity : AppCompatActivity(), OnRequestPermissionsResultCallback { lateinit var activityResultLauncher: ActivityResultLauncher lateinit var content: View var showSplashScreen: Boolean = true + var darkMode: Boolean = false /* * Gets the number of available cores @@ -73,6 +74,15 @@ class MainActivity : AppCompatActivity(), OnRequestPermissionsResultCallback { @Suppress("DEPRECATION") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + when (resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) { + Configuration.UI_MODE_NIGHT_NO -> { + darkMode = false + } + Configuration.UI_MODE_NIGHT_YES -> { + darkMode = true + } + } + content = findViewById(android.R.id.content) content.viewTreeObserver.addOnPreDrawListener( object : ViewTreeObserver.OnPreDrawListener { @@ -242,11 +252,13 @@ class MainActivity : AppCompatActivity(), OnRequestPermissionsResultCallback { super.onConfigurationChanged(newConfig) when (newConfig.uiMode and Configuration.UI_MODE_NIGHT_MASK) { Configuration.UI_MODE_NIGHT_NO -> { + darkMode = false webView.post { webView.loadUrl("javascript: window.dispatchEvent(new Event(\"enabled-light-mode\"))") } } Configuration.UI_MODE_NIGHT_YES -> { + darkMode = true webView.post { webView.loadUrl("javascript: window.dispatchEvent(new Event(\"enabled-dark-mode\"))") } diff --git a/src/renderer/App.js b/src/renderer/App.js index 30207b2329bc7..30f1bf0a8644c 100644 --- a/src/renderer/App.js +++ b/src/renderer/App.js @@ -18,6 +18,7 @@ import { openExternalLink, openInternalPath, showToast } from './helpers/utils' import { translateWindowTitle } from './helpers/strings' import 'core-js' import android from 'android' +import { updateSystemTheme, updateTheme } from './helpers/android' let ipcRenderer = null @@ -197,10 +198,10 @@ export default defineComponent({ // hides the splash screen android.hideSplashScreen() window.addEventListener('enabled-light-mode', () => { - document.body.dataset.systemTheme = 'light' + this.checkThemeSettings() }) window.addEventListener('enabled-dark-mode', () => { - document.body.dataset.systemTheme = 'dark' + this.checkThemeSettings() }) } @@ -230,8 +231,17 @@ export default defineComponent({ mainColor: this.mainColor || 'mainRed', secColor: this.secColor || 'secBlue' } - - this.updateTheme(theme) + if (process.env.IS_ANDROID) { + if (theme.baseTheme === 'system') { + // get a more precise theme with this + theme.baseTheme = android.getSystemTheme() + } + this.updateTheme(theme) + setTimeout(() => { + // 0 ms timeout to allow the css to update + updateSystemTheme(theme.baseTheme) + }) + } }, updateTheme: function (theme) { diff --git a/src/renderer/helpers/android.js b/src/renderer/helpers/android.js index c6f4240be11a8..6cbfa008ec3f8 100644 --- a/src/renderer/helpers/android.js +++ b/src/renderer/helpers/android.js @@ -304,3 +304,27 @@ export async function initalizeDatabasesInDirectory(directoryHandle) { } return filteredFiles } + +export function updateSystemTheme(theme) { + const dark = [ + 'dark', + 'black', + 'dracula', + 'catppuccinMocha', + 'nordic', + 'hotPink' + ] + const light = [ + 'light', + 'pastelPink' + ] + const bodyStyle = getComputedStyle(document.body) + const top = bodyStyle.getPropertyValue('--card-bg-color') + const bottom = bodyStyle.getPropertyValue('--side-nav-color') + if (dark.indexOf(theme) !== -1) { + android.themeSystemUi(bottom, top, true) + } + if (light.indexOf(theme) !== -1) { + android.themeSystemUi(bottom, top, false) + } +}