Implement Actions, create a theme switcher action window

This commit is contained in:
Aleksandras Kostarevas 2023-08-22 15:36:40 +03:00
parent ad151d7f11
commit f91a626955
15 changed files with 605 additions and 163 deletions

View File

@ -0,0 +1,20 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="26dp"
android:height="26dp"
android:viewportWidth="26"
android:viewportHeight="26">
<path
android:pathData="M20,13L6,13"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#FFFFFF"
android:strokeLineCap="round"/>
<path
android:pathData="M13,20L6,13L13,6"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#FFFFFF"
android:strokeLineCap="round"/>
</vector>

20
java/res/drawable/eye.xml Normal file
View File

@ -0,0 +1,20 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="26dp"
android:height="26dp"
android:viewportWidth="26"
android:viewportHeight="26">
<path
android:pathData="M2,13C2,13 6,5 13,5C20,5 24,13 24,13C24,13 20,21 13,21C6,21 2,13 2,13ZZ"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#FFFFFF"
android:strokeLineCap="round"/>
<path
android:pathData="M10,13a3,3 0,1 0,6 0a3,3 0,1 0,-6 0z"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#FFFFFF"
android:strokeLineCap="round"/>
</vector>

View File

@ -98,13 +98,19 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
} }
} }
private boolean themeSwitchPending = false;
public void queueThemeSwitch() {
themeSwitchPending = true;
}
private boolean updateKeyboardThemeAndContextThemeWrapper(final Context context, private boolean updateKeyboardThemeAndContextThemeWrapper(final Context context,
final KeyboardTheme keyboardTheme) { final KeyboardTheme keyboardTheme) {
if (mThemeContext == null || !keyboardTheme.equals(mKeyboardTheme) if (themeSwitchPending || mThemeContext == null || !keyboardTheme.equals(mKeyboardTheme)
|| !mThemeContext.getResources().equals(context.getResources())) { || !mThemeContext.getResources().equals(context.getResources())) {
mKeyboardTheme = keyboardTheme; mKeyboardTheme = keyboardTheme;
mThemeContext = new ContextThemeWrapper(context, keyboardTheme.mStyleId); mThemeContext = new ContextThemeWrapper(context, keyboardTheme.mStyleId);
KeyboardLayoutSet.onKeyboardThemeChanged(); KeyboardLayoutSet.onKeyboardThemeChanged();
themeSwitchPending = false;
return true; return true;
} }
return false; return false;

View File

@ -35,8 +35,8 @@ import android.view.View;
import org.futo.inputmethod.keyboard.internal.KeyDrawParams; import org.futo.inputmethod.keyboard.internal.KeyDrawParams;
import org.futo.inputmethod.keyboard.internal.KeyVisualAttributes; import org.futo.inputmethod.keyboard.internal.KeyVisualAttributes;
import org.futo.inputmethod.latin.KeyboardDrawableProvider; import org.futo.inputmethod.latin.DynamicThemeProvider;
import org.futo.inputmethod.latin.KeyboardDrawableProviderOwner; import org.futo.inputmethod.latin.DynamicThemeProviderOwner;
import org.futo.inputmethod.latin.R; import org.futo.inputmethod.latin.R;
import org.futo.inputmethod.latin.common.Constants; import org.futo.inputmethod.latin.common.Constants;
import org.futo.inputmethod.latin.utils.TypefaceUtils; import org.futo.inputmethod.latin.utils.TypefaceUtils;
@ -96,7 +96,7 @@ public class KeyboardView extends View {
private final Drawable mKeyBackground; private final Drawable mKeyBackground;
private final Drawable mFunctionalKeyBackground; private final Drawable mFunctionalKeyBackground;
private final Drawable mSpacebarBackground; private final Drawable mSpacebarBackground;
protected final KeyboardDrawableProvider mDrawableProvider; protected final DynamicThemeProvider mDrawableProvider;
private final float mSpacebarIconWidthRatio; private final float mSpacebarIconWidthRatio;
private final Rect mKeyBackgroundPadding = new Rect(); private final Rect mKeyBackgroundPadding = new Rect();
private static final float KET_TEXT_SHADOW_RADIUS_DISABLED = -1.0f; private static final float KET_TEXT_SHADOW_RADIUS_DISABLED = -1.0f;
@ -138,9 +138,9 @@ public class KeyboardView extends View {
R.styleable.KeyboardView, defStyle, R.style.KeyboardView); R.styleable.KeyboardView, defStyle, R.style.KeyboardView);
assert(context instanceof ContextThemeWrapper); assert(context instanceof ContextThemeWrapper);
assert(((ContextThemeWrapper) context).getBaseContext() instanceof KeyboardDrawableProviderOwner); assert(((ContextThemeWrapper) context).getBaseContext() instanceof DynamicThemeProviderOwner);
mDrawableProvider = ((KeyboardDrawableProviderOwner) ((ContextThemeWrapper) context).getBaseContext()).getDrawableProvider(); mDrawableProvider = ((DynamicThemeProviderOwner) ((ContextThemeWrapper) context).getBaseContext()).getDrawableProvider();
boolean isMoreKeys = defStyle == R.attr.moreKeysKeyboardViewStyle || defStyle == R.attr.moreKeysKeyboardViewForActionStyle; boolean isMoreKeys = defStyle == R.attr.moreKeysKeyboardViewStyle || defStyle == R.attr.moreKeysKeyboardViewForActionStyle;

View File

@ -28,7 +28,6 @@ import android.graphics.Paint;
import android.graphics.Paint.Align; import android.graphics.Paint.Align;
import android.graphics.Typeface; import android.graphics.Typeface;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.text.TextUtils;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log; import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@ -51,7 +50,7 @@ import org.futo.inputmethod.keyboard.internal.MoreKeySpec;
import org.futo.inputmethod.keyboard.internal.NonDistinctMultitouchHelper; import org.futo.inputmethod.keyboard.internal.NonDistinctMultitouchHelper;
import org.futo.inputmethod.keyboard.internal.SlidingKeyInputDrawingPreview; import org.futo.inputmethod.keyboard.internal.SlidingKeyInputDrawingPreview;
import org.futo.inputmethod.keyboard.internal.TimerHandler; import org.futo.inputmethod.keyboard.internal.TimerHandler;
import org.futo.inputmethod.latin.KeyboardDrawableProvider; import org.futo.inputmethod.latin.DynamicThemeProvider;
import org.futo.inputmethod.latin.R; import org.futo.inputmethod.latin.R;
import org.futo.inputmethod.latin.RichInputMethodSubtype; import org.futo.inputmethod.latin.RichInputMethodSubtype;
import org.futo.inputmethod.latin.SuggestedWords; import org.futo.inputmethod.latin.SuggestedWords;
@ -61,7 +60,6 @@ import org.futo.inputmethod.latin.settings.DebugSettings;
import org.futo.inputmethod.latin.utils.LanguageOnSpacebarUtils; import org.futo.inputmethod.latin.utils.LanguageOnSpacebarUtils;
import org.futo.inputmethod.latin.utils.TypefaceUtils; import org.futo.inputmethod.latin.utils.TypefaceUtils;
import java.util.Locale;
import java.util.WeakHashMap; import java.util.WeakHashMap;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
@ -215,7 +213,7 @@ public final class MainKeyboardView extends KeyboardView implements DrawingProxy
mBackgroundDimAlphaPaint.setAlpha(backgroundDimAlpha); mBackgroundDimAlphaPaint.setAlpha(backgroundDimAlpha);
mLanguageOnSpacebarTextRatio = mainKeyboardViewAttr.getFraction( mLanguageOnSpacebarTextRatio = mainKeyboardViewAttr.getFraction(
R.styleable.MainKeyboardView_languageOnSpacebarTextRatio, 1, 1, 1.0f); R.styleable.MainKeyboardView_languageOnSpacebarTextRatio, 1, 1, 1.0f);
mLanguageOnSpacebarTextColor = KeyboardDrawableProvider.Companion.getColorOrDefault( mLanguageOnSpacebarTextColor = DynamicThemeProvider.Companion.getColorOrDefault(
R.styleable.MainKeyboardView_languageOnSpacebarTextColor, 0, R.styleable.MainKeyboardView_languageOnSpacebarTextColor, 0,
mainKeyboardViewAttr, mDrawableProvider mainKeyboardViewAttr, mDrawableProvider
); );
@ -461,6 +459,10 @@ public final class MainKeyboardView extends KeyboardView implements DrawingProxy
Log.w(TAG, "Cannot find android.R.id.content view to add DrawingPreviewPlacerView"); Log.w(TAG, "Cannot find android.R.id.content view to add DrawingPreviewPlacerView");
return; return;
} }
if(mDrawingPreviewPlacerView.getParent() != null) {
((ViewGroup)mDrawingPreviewPlacerView.getParent()).removeView(mDrawingPreviewPlacerView);
}
windowContentView.addView(mDrawingPreviewPlacerView); windowContentView.addView(mDrawingPreviewPlacerView);
} }

View File

@ -26,7 +26,7 @@ import android.view.View;
import android.view.animation.AccelerateInterpolator; import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator; import android.view.animation.DecelerateInterpolator;
import org.futo.inputmethod.latin.KeyboardDrawableProvider; import org.futo.inputmethod.latin.DynamicThemeProvider;
import org.futo.inputmethod.latin.R; import org.futo.inputmethod.latin.R;
public final class KeyPreviewDrawParams { public final class KeyPreviewDrawParams {
@ -71,7 +71,7 @@ public final class KeyPreviewDrawParams {
// preview background. // preview background.
private int mVisibleOffset; private int mVisibleOffset;
public KeyPreviewDrawParams(final TypedArray mainKeyboardViewAttr, final KeyboardDrawableProvider provider) { public KeyPreviewDrawParams(final TypedArray mainKeyboardViewAttr, final DynamicThemeProvider provider) {
mPreviewOffset = mainKeyboardViewAttr.getDimensionPixelOffset( mPreviewOffset = mainKeyboardViewAttr.getDimensionPixelOffset(
R.styleable.MainKeyboardView_keyPreviewOffset, 0); R.styleable.MainKeyboardView_keyPreviewOffset, 0);
mPreviewHeight = mainKeyboardViewAttr.getDimensionPixelSize( mPreviewHeight = mainKeyboardViewAttr.getDimensionPixelSize(

View File

@ -20,7 +20,7 @@ import android.content.res.TypedArray;
import android.graphics.Typeface; import android.graphics.Typeface;
import android.util.SparseIntArray; import android.util.SparseIntArray;
import org.futo.inputmethod.latin.KeyboardDrawableProvider; import org.futo.inputmethod.latin.DynamicThemeProvider;
import org.futo.inputmethod.latin.R; import org.futo.inputmethod.latin.R;
import org.futo.inputmethod.latin.utils.ResourceUtils; import org.futo.inputmethod.latin.utils.ResourceUtils;
@ -87,7 +87,7 @@ public final class KeyVisualAttributes {
} }
@Nullable @Nullable
public static KeyVisualAttributes newInstance(@Nonnull final TypedArray keyAttr, @Nullable final KeyboardDrawableProvider provider) { public static KeyVisualAttributes newInstance(@Nonnull final TypedArray keyAttr, @Nullable final DynamicThemeProvider provider) {
final int indexCount = keyAttr.getIndexCount(); final int indexCount = keyAttr.getIndexCount();
for (int i = 0; i < indexCount; i++) { for (int i = 0; i < indexCount; i++) {
final int attrId = keyAttr.getIndex(i); final int attrId = keyAttr.getIndex(i);
@ -99,7 +99,7 @@ public final class KeyVisualAttributes {
return null; return null;
} }
private KeyVisualAttributes(@Nonnull final TypedArray keyAttr, @Nullable final KeyboardDrawableProvider provider) { private KeyVisualAttributes(@Nonnull final TypedArray keyAttr, @Nullable final DynamicThemeProvider provider) {
if (keyAttr.hasValue(R.styleable.Keyboard_Key_keyTypeface)) { if (keyAttr.hasValue(R.styleable.Keyboard_Key_keyTypeface)) {
mTypeface = Typeface.defaultFromStyle( mTypeface = Typeface.defaultFromStyle(
keyAttr.getInt(R.styleable.Keyboard_Key_keyTypeface, Typeface.NORMAL)); keyAttr.getInt(R.styleable.Keyboard_Key_keyTypeface, Typeface.NORMAL));
@ -126,23 +126,23 @@ public final class KeyVisualAttributes {
mPreviewTextRatio = ResourceUtils.getFraction(keyAttr, mPreviewTextRatio = ResourceUtils.getFraction(keyAttr,
R.styleable.Keyboard_Key_keyPreviewTextRatio); R.styleable.Keyboard_Key_keyPreviewTextRatio);
mTextColor = KeyboardDrawableProvider.Companion.getColorOrDefault( mTextColor = DynamicThemeProvider.Companion.getColorOrDefault(
R.styleable.Keyboard_Key_keyTextColor, 0, keyAttr, provider); R.styleable.Keyboard_Key_keyTextColor, 0, keyAttr, provider);
mTextInactivatedColor = KeyboardDrawableProvider.Companion.getColorOrDefault( mTextInactivatedColor = DynamicThemeProvider.Companion.getColorOrDefault(
R.styleable.Keyboard_Key_keyTextInactivatedColor, 0, keyAttr, provider); R.styleable.Keyboard_Key_keyTextInactivatedColor, 0, keyAttr, provider);
mTextShadowColor = KeyboardDrawableProvider.Companion.getColorOrDefault( mTextShadowColor = DynamicThemeProvider.Companion.getColorOrDefault(
R.styleable.Keyboard_Key_keyTextShadowColor, 0, keyAttr, provider); R.styleable.Keyboard_Key_keyTextShadowColor, 0, keyAttr, provider);
mFunctionalTextColor = KeyboardDrawableProvider.Companion.getColorOrDefault( mFunctionalTextColor = DynamicThemeProvider.Companion.getColorOrDefault(
R.styleable.Keyboard_Key_functionalTextColor, 0, keyAttr, provider); R.styleable.Keyboard_Key_functionalTextColor, 0, keyAttr, provider);
mHintLetterColor = KeyboardDrawableProvider.Companion.getColorOrDefault( mHintLetterColor = DynamicThemeProvider.Companion.getColorOrDefault(
R.styleable.Keyboard_Key_keyHintLetterColor, 0, keyAttr, provider); R.styleable.Keyboard_Key_keyHintLetterColor, 0, keyAttr, provider);
mHintLabelColor = KeyboardDrawableProvider.Companion.getColorOrDefault( mHintLabelColor = DynamicThemeProvider.Companion.getColorOrDefault(
R.styleable.Keyboard_Key_keyHintLabelColor, 0, keyAttr, provider); R.styleable.Keyboard_Key_keyHintLabelColor, 0, keyAttr, provider);
mShiftedLetterHintInactivatedColor = KeyboardDrawableProvider.Companion.getColorOrDefault( mShiftedLetterHintInactivatedColor = DynamicThemeProvider.Companion.getColorOrDefault(
R.styleable.Keyboard_Key_keyShiftedLetterHintInactivatedColor, 0, keyAttr, provider); R.styleable.Keyboard_Key_keyShiftedLetterHintInactivatedColor, 0, keyAttr, provider);
mShiftedLetterHintActivatedColor = KeyboardDrawableProvider.Companion.getColorOrDefault( mShiftedLetterHintActivatedColor = DynamicThemeProvider.Companion.getColorOrDefault(
R.styleable.Keyboard_Key_keyShiftedLetterHintActivatedColor, 0, keyAttr, provider); R.styleable.Keyboard_Key_keyShiftedLetterHintActivatedColor, 0, keyAttr, provider);
mPreviewTextColor = KeyboardDrawableProvider.Companion.getColorOrDefault( mPreviewTextColor = DynamicThemeProvider.Companion.getColorOrDefault(
R.styleable.Keyboard_Key_keyPreviewTextColor, 0, keyAttr, provider); R.styleable.Keyboard_Key_keyPreviewTextColor, 0, keyAttr, provider);
mHintLabelVerticalAdjustment = ResourceUtils.getFraction(keyAttr, mHintLabelVerticalAdjustment = ResourceUtils.getFraction(keyAttr,

View File

@ -33,8 +33,8 @@ import org.futo.inputmethod.keyboard.Key;
import org.futo.inputmethod.keyboard.Keyboard; import org.futo.inputmethod.keyboard.Keyboard;
import org.futo.inputmethod.keyboard.KeyboardId; import org.futo.inputmethod.keyboard.KeyboardId;
import org.futo.inputmethod.keyboard.KeyboardTheme; import org.futo.inputmethod.keyboard.KeyboardTheme;
import org.futo.inputmethod.latin.KeyboardDrawableProvider; import org.futo.inputmethod.latin.DynamicThemeProvider;
import org.futo.inputmethod.latin.KeyboardDrawableProviderOwner; import org.futo.inputmethod.latin.DynamicThemeProviderOwner;
import org.futo.inputmethod.latin.R; import org.futo.inputmethod.latin.R;
import org.futo.inputmethod.latin.common.Constants; import org.futo.inputmethod.latin.common.Constants;
import org.futo.inputmethod.latin.common.StringUtils; import org.futo.inputmethod.latin.common.StringUtils;
@ -153,16 +153,16 @@ public class KeyboardBuilder<KP extends KeyboardParams> {
private boolean mTopEdge; private boolean mTopEdge;
private Key mRightEdgeKey = null; private Key mRightEdgeKey = null;
private KeyboardDrawableProvider mProvider = null; private DynamicThemeProvider mProvider = null;
public KeyboardBuilder(final Context context, @Nonnull final KP params) { public KeyboardBuilder(final Context context, @Nonnull final KP params) {
mContext = context; mContext = context;
if(mContext instanceof KeyboardDrawableProviderOwner) { if(mContext instanceof DynamicThemeProviderOwner) {
mProvider = ((KeyboardDrawableProviderOwner) mContext).getDrawableProvider(); mProvider = ((DynamicThemeProviderOwner) mContext).getDrawableProvider();
}else if(mContext instanceof ContextThemeWrapper) { }else if(mContext instanceof ContextThemeWrapper) {
Context baseContext = ((ContextThemeWrapper) mContext).getBaseContext(); Context baseContext = ((ContextThemeWrapper) mContext).getBaseContext();
if(baseContext instanceof KeyboardDrawableProviderOwner) { if(baseContext instanceof DynamicThemeProviderOwner) {
mProvider = ((KeyboardDrawableProviderOwner) baseContext).getDrawableProvider(); mProvider = ((DynamicThemeProviderOwner) baseContext).getDrawableProvider();
} }
} }

View File

@ -22,7 +22,7 @@ import android.graphics.drawable.Drawable;
import android.util.Log; import android.util.Log;
import android.util.SparseIntArray; import android.util.SparseIntArray;
import org.futo.inputmethod.latin.KeyboardDrawableProvider; import org.futo.inputmethod.latin.DynamicThemeProvider;
import org.futo.inputmethod.latin.R; import org.futo.inputmethod.latin.R;
import java.util.HashMap; import java.util.HashMap;
@ -108,12 +108,12 @@ public final class KeyboardIconsSet {
} }
} }
public void loadIcons(final TypedArray keyboardAttrs, @Nullable KeyboardDrawableProvider provider) { public void loadIcons(final TypedArray keyboardAttrs, @Nullable DynamicThemeProvider provider) {
final int size = ATTR_ID_TO_ICON_ID.size(); final int size = ATTR_ID_TO_ICON_ID.size();
for (int index = 0; index < size; index++) { for (int index = 0; index < size; index++) {
final int attrId = ATTR_ID_TO_ICON_ID.keyAt(index); final int attrId = ATTR_ID_TO_ICON_ID.keyAt(index);
try { try {
final Drawable icon = KeyboardDrawableProvider.Companion.getDrawableOrDefault(attrId, keyboardAttrs, provider); final Drawable icon = DynamicThemeProvider.Companion.getDrawableOrDefault(attrId, keyboardAttrs, provider);
setDefaultBounds(icon); setDefaultBounds(icon);
final Integer iconId = ATTR_ID_TO_ICON_ID.get(attrId); final Integer iconId = ATTR_ID_TO_ICON_ID.get(attrId);
mIcons[iconId] = icon; mIcons[iconId] = icon;

View File

@ -6,13 +6,11 @@ import android.content.res.TypedArray
import android.graphics.Color import android.graphics.Color
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.graphics.drawable.GradientDrawable import android.graphics.drawable.GradientDrawable
import android.graphics.drawable.InsetDrawable
import android.graphics.drawable.LayerDrawable import android.graphics.drawable.LayerDrawable
import android.graphics.drawable.ShapeDrawable import android.graphics.drawable.ShapeDrawable
import android.graphics.drawable.StateListDrawable import android.graphics.drawable.StateListDrawable
import android.graphics.drawable.shapes.RoundRectShape import android.graphics.drawable.shapes.RoundRectShape
import android.inputmethodservice.InputMethodService import android.inputmethodservice.InputMethodService
import android.util.AttributeSet
import android.util.TypedValue import android.util.TypedValue
import android.view.KeyEvent import android.view.KeyEvent
import android.view.View import android.view.View
@ -22,22 +20,34 @@ import android.view.inputmethod.InputMethodManager
import android.view.inputmethod.InputMethodSubtype import android.view.inputmethod.InputMethodSubtype
import androidx.annotation.ColorInt import androidx.annotation.ColorInt
import androidx.appcompat.content.res.AppCompatResources import androidx.appcompat.content.res.AppCompatResources
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.material3.Button
import androidx.compose.material3.ColorScheme
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.dynamicDarkColorScheme import androidx.compose.material3.Text
import androidx.compose.material3.dynamicLightColorScheme import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.key import androidx.compose.runtime.key
import androidx.compose.ui.Alignment
import androidx.compose.ui.Alignment.Companion.CenterVertically
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.graphics.toColor
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry import androidx.lifecycle.LifecycleRegistry
@ -53,12 +63,16 @@ import androidx.savedstate.SavedStateRegistryOwner
import androidx.savedstate.findViewTreeSavedStateRegistryOwner import androidx.savedstate.findViewTreeSavedStateRegistryOwner
import androidx.savedstate.setViewTreeSavedStateRegistryOwner import androidx.savedstate.setViewTreeSavedStateRegistryOwner
import com.google.android.material.color.DynamicColors import com.google.android.material.color.DynamicColors
import org.futo.inputmethod.latin.common.Constants
import org.futo.inputmethod.latin.uix.Action
import org.futo.inputmethod.latin.uix.ActionBar import org.futo.inputmethod.latin.uix.ActionBar
import org.futo.inputmethod.latin.uix.KeyboardManagerForAction
import org.futo.inputmethod.latin.uix.theme.DarkColorScheme import org.futo.inputmethod.latin.uix.theme.DarkColorScheme
import org.futo.inputmethod.latin.uix.theme.Typography
import org.futo.inputmethod.latin.uix.theme.UixThemeWrapper
import kotlin.math.roundToInt import kotlin.math.roundToInt
interface DynamicThemeProvider {
interface KeyboardDrawableProvider {
val primaryKeyboardColor: Int val primaryKeyboardColor: Int
val keyboardBackground: Drawable val keyboardBackground: Drawable
@ -77,11 +91,11 @@ interface KeyboardDrawableProvider {
companion object { companion object {
@ColorInt @ColorInt
fun getColorOrDefault(i: Int, @ColorInt default: Int, keyAttr: TypedArray, provider: KeyboardDrawableProvider?): Int { fun getColorOrDefault(i: Int, @ColorInt default: Int, keyAttr: TypedArray, provider: DynamicThemeProvider?): Int {
return (provider?.getColor(i)) ?: keyAttr.getColor(i, default) return (provider?.getColor(i)) ?: keyAttr.getColor(i, default)
} }
fun getDrawableOrDefault(i: Int, keyAttr: TypedArray, provider: KeyboardDrawableProvider?): Drawable? { fun getDrawableOrDefault(i: Int, keyAttr: TypedArray, provider: DynamicThemeProvider?): Drawable? {
return (provider?.getDrawable(i)) ?: keyAttr.getDrawable(i) return (provider?.getDrawable(i)) ?: keyAttr.getDrawable(i)
} }
} }
@ -89,7 +103,7 @@ interface KeyboardDrawableProvider {
// TODO: Expand the number of drawables this provides so it covers the full theme, and // TODO: Expand the number of drawables this provides so it covers the full theme, and
// build some system to dynamically change these colors // build some system to dynamically change these colors
class BasicThemeProvider(val context: Context) : KeyboardDrawableProvider { class BasicThemeProvider(val context: Context, val overrideColorScheme: ColorScheme? = null) : DynamicThemeProvider {
override val primaryKeyboardColor: Int override val primaryKeyboardColor: Int
override val keyboardBackground: Drawable override val keyboardBackground: Drawable
@ -152,7 +166,9 @@ class BasicThemeProvider(val context: Context) : KeyboardDrawableProvider {
} }
init { init {
val colorScheme = if(!DynamicColors.isDynamicColorAvailable()) { val colorScheme = if(overrideColorScheme != null) {
overrideColorScheme
}else if(!DynamicColors.isDynamicColorAvailable()) {
DarkColorScheme DarkColorScheme
} else { } else {
val dCtx = DynamicColors.wrapContextIfAvailable(context) val dCtx = DynamicColors.wrapContextIfAvailable(context)
@ -213,7 +229,7 @@ class BasicThemeProvider(val context: Context) : KeyboardDrawableProvider {
) )
addStateWithHighlightLayerOnPressed(highlight, intArrayOf(android.R.attr.state_checkable, android.R.attr.state_checked), addStateWithHighlightLayerOnPressed(highlight, intArrayOf(android.R.attr.state_checkable, android.R.attr.state_checked),
coloredRoundedRectangle(secondary, dp(8.dp)) coloredRoundedRectangle(colorScheme.secondaryContainer.toArgb(), dp(8.dp))
) )
addStateWithHighlightLayerOnPressed(highlight, intArrayOf(android.R.attr.state_checkable), addStateWithHighlightLayerOnPressed(highlight, intArrayOf(android.R.attr.state_checkable),
@ -264,20 +280,39 @@ class BasicThemeProvider(val context: Context) : KeyboardDrawableProvider {
} }
interface KeyboardDrawableProviderOwner { interface DynamicThemeProviderOwner {
fun getDrawableProvider(): KeyboardDrawableProvider fun getDrawableProvider(): DynamicThemeProvider
} }
class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, SavedStateRegistryOwner, LatinIMELegacy.SuggestionStripController, KeyboardDrawableProviderOwner {
private var drawableProvider: KeyboardDrawableProvider? = null
override fun getDrawableProvider(): KeyboardDrawableProvider { class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, SavedStateRegistryOwner, LatinIMELegacy.SuggestionStripController, DynamicThemeProviderOwner,
KeyboardManagerForAction {
private var activeColorScheme = DarkColorScheme
private var drawableProvider: DynamicThemeProvider? = null
override fun getDrawableProvider(): DynamicThemeProvider {
if(drawableProvider == null) { if(drawableProvider == null) {
drawableProvider = BasicThemeProvider(this) drawableProvider = BasicThemeProvider(this, activeColorScheme)
} }
return drawableProvider!! return drawableProvider!!
} }
private fun updateDrawableProvider(colorScheme: ColorScheme) {
activeColorScheme = colorScheme
// ... update drawableProvider with params
drawableProvider = BasicThemeProvider(this, overrideColorScheme = colorScheme)
// ... force change keyboard view
legacyInputView = latinIMELegacy.onCreateInputView()
latinIMELegacy.loadKeyboard()
setContent()
window.window?.navigationBarColor = drawableProvider!!.primaryKeyboardColor
}
private val latinIMELegacy = LatinIMELegacy( private val latinIMELegacy = LatinIMELegacy(
this as InputMethodService, this as InputMethodService,
this as LatinIMELegacy.SuggestionStripController this as LatinIMELegacy.SuggestionStripController
@ -305,6 +340,15 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
activeColorScheme = if(!DynamicColors.isDynamicColorAvailable()) {
DarkColorScheme
} else {
val dCtx = DynamicColors.wrapContextIfAvailable(this)
dynamicLightColorScheme(dCtx)
}
mSavedStateRegistryController.performRestore(null) mSavedStateRegistryController.performRestore(null)
handleLifecycleEvent(Lifecycle.Event.ON_RESUME) handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
@ -359,26 +403,95 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
return composeView!! return composeView!!
} }
private var currWindowAction: Action? = null
private fun onActionActivated(action: Action) {
if(action.windowImpl != null) {
currWindowAction = action
setContent()
} else if(action.simplePressImpl != null) {
action.simplePressImpl.invoke(this)
} else {
throw IllegalStateException("An action must have either a window implementation or a simple press implementation")
}
}
private var inputViewHeight: Int = -1
private var shouldShowSuggestionStrip: Boolean = true private var shouldShowSuggestionStrip: Boolean = true
private var suggestedWords: SuggestedWords? = null private var suggestedWords: SuggestedWords? = null
@Composable
private fun LegacyKeyboardView() {
key(legacyInputView) {
AndroidView(factory = {
legacyInputView!!
}, update = { }, modifier = Modifier.onSizeChanged {
inputViewHeight = it.height
})
}
}
@Composable
private fun MainKeyboardViewWithActionBar() {
Column {
if (shouldShowSuggestionStrip) {
ActionBar(
suggestedWords,
latinIMELegacy,
onActionActivated = { onActionActivated(it) }
)
}
LegacyKeyboardView()
}
}
private fun returnBackToMainKeyboardViewFromAction() {
assert(currWindowAction != null)
currWindowAction = null
setContent()
}
@Composable
private fun ActionViewWithHeader(action: Action) {
val windowImpl = action.windowImpl!!
println("The height is $inputViewHeight, which in DP is ${ with(LocalDensity.current) { inputViewHeight.toDp() }}")
Column {
Surface(modifier = Modifier
.fillMaxWidth()
.height(40.dp), color = MaterialTheme.colorScheme.background)
{
Row {
IconButton(onClick = {
returnBackToMainKeyboardViewFromAction()
}) {
Icon(
painter = painterResource(id = R.drawable.arrow_left),
contentDescription = "Back"
)
}
Text(windowImpl.windowName(), style = Typography.titleMedium, modifier = Modifier.align(CenterVertically))
}
}
Box(modifier = Modifier
.fillMaxWidth()
.height(with(LocalDensity.current) { inputViewHeight.toDp() })) {
windowImpl.WindowContents(manager = this@LatinIME)
}
}
}
private fun setContent() { private fun setContent() {
composeView?.setContent { composeView?.setContent {
Column { UixThemeWrapper(activeColorScheme) {
Spacer(modifier = Modifier.weight(1.0f)) Column {
Surface(modifier = Modifier.onSizeChanged { Spacer(modifier = Modifier.weight(1.0f))
touchableHeight = it.height Surface(modifier = Modifier.onSizeChanged {
}, color = MaterialTheme.colorScheme.surface) { touchableHeight = it.height
Column { }) {
if(shouldShowSuggestionStrip) { when {
ActionBar( currWindowAction != null -> ActionViewWithHeader(currWindowAction!!)
suggestedWords, else -> MainKeyboardViewWithActionBar()
latinIMELegacy
)
}
key(legacyInputView) {
AndroidView(factory = {
legacyInputView!!
}, update = { })
} }
} }
} }
@ -500,7 +613,7 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
val touchLeft = 0 val touchLeft = 0
val touchTop = visibleTopY val touchTop = visibleTopY
val touchRight = legacyInputView!!.width val touchRight = composeView!!.width
val touchBottom = inputHeight val touchBottom = inputHeight
latinIMELegacy.setInsets(outInsets!!.apply { latinIMELegacy.setInsets(outInsets!!.apply {
@ -549,4 +662,17 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
override fun maybeShowImportantNoticeTitle(): Boolean { override fun maybeShowImportantNoticeTitle(): Boolean {
return false return false
} }
override fun triggerSystemVoiceInput() {
latinIMELegacy.onCodeInput(
Constants.CODE_SHORTCUT,
Constants.SUGGESTION_STRIP_COORDINATE,
Constants.SUGGESTION_STRIP_COORDINATE,
false
);
}
override fun updateTheme(newTheme: ColorScheme) {
updateDrawableProvider(newTheme)
}
} }

View File

@ -846,6 +846,7 @@ public class LatinIMELegacy implements KeyboardActionListener,
public View onCreateInputView() { public View onCreateInputView() {
StatsUtils.onCreateInputView(); StatsUtils.onCreateInputView();
assert mDisplayContext != null; assert mDisplayContext != null;
mKeyboardSwitcher.queueThemeSwitch();
return mKeyboardSwitcher.onCreateInputView(mDisplayContext, return mKeyboardSwitcher.onCreateInputView(mDisplayContext,
mIsHardwareAcceleratedDrawingEnabled); mIsHardwareAcceleratedDrawingEnabled);
} }
@ -1948,7 +1949,7 @@ public class LatinIMELegacy implements KeyboardActionListener,
} }
private void setNavigationBarVisibility(final boolean visible) { private void setNavigationBarVisibility(final boolean visible) {
int color = ((KeyboardDrawableProviderOwner)getInputMethodService()).getDrawableProvider().getPrimaryKeyboardColor(); int color = ((DynamicThemeProviderOwner)getInputMethodService()).getDrawableProvider().getPrimaryKeyboardColor();
if (BuildCompatUtils.EFFECTIVE_SDK_INT > Build.VERSION_CODES.M) { if (BuildCompatUtils.EFFECTIVE_SDK_INT > Build.VERSION_CODES.M) {
// For N and later, IMEs can specify Color.TRANSPARENT to make the navigation bar // For N and later, IMEs can specify Color.TRANSPARENT to make the navigation bar
// transparent. For other colors the system uses the default color. // transparent. For other colors the system uses the default color.

View File

@ -0,0 +1,27 @@
package org.futo.inputmethod.latin.uix
import androidx.annotation.DrawableRes
import androidx.compose.material3.ColorScheme
import androidx.compose.runtime.Composable
interface KeyboardManagerForAction {
fun triggerSystemVoiceInput()
fun updateTheme(newTheme: ColorScheme)
}
interface ActionWindow {
@Composable
fun windowName(): String
@Composable
fun WindowContents(manager: KeyboardManagerForAction)
}
data class Action(
@DrawableRes val icon: Int,
val name: String, // TODO: @StringRes Int
val windowImpl: ActionWindow?,
val simplePressImpl: ((KeyboardManagerForAction) -> Unit)?
)

View File

@ -1,9 +1,12 @@
package org.futo.inputmethod.latin.uix package org.futo.inputmethod.latin.uix
import androidx.annotation.DrawableRes import android.os.Build
import androidx.compose.foundation.Canvas import androidx.compose.foundation.Canvas
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.scrollable
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
@ -12,14 +15,21 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ColorScheme
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.IconButtonColors
import androidx.compose.material3.IconButtonDefaults import androidx.compose.material3.IconButtonDefaults
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
@ -31,10 +41,11 @@ import androidx.compose.ui.draw.rotate
import androidx.compose.ui.geometry.CornerRadius import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.drawscope.scale import androidx.compose.ui.graphics.drawscope.scale
import androidx.compose.ui.graphics.drawscope.translate import androidx.compose.ui.graphics.drawscope.translate
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.ExperimentalTextApi import androidx.compose.ui.text.ExperimentalTextApi
@ -55,8 +66,8 @@ import org.futo.inputmethod.latin.SuggestedWords.SuggestedWordInfo
import org.futo.inputmethod.latin.SuggestedWords.SuggestedWordInfo.KIND_TYPED import org.futo.inputmethod.latin.SuggestedWords.SuggestedWordInfo.KIND_TYPED
import org.futo.inputmethod.latin.common.Constants import org.futo.inputmethod.latin.common.Constants
import org.futo.inputmethod.latin.suggestions.SuggestionStripView import org.futo.inputmethod.latin.suggestions.SuggestionStripView
import org.futo.inputmethod.latin.uix.theme.Typography import org.futo.inputmethod.latin.uix.theme.DarkColorScheme
import org.futo.inputmethod.latin.uix.theme.WhisperVoiceInputTheme import org.futo.inputmethod.latin.uix.theme.UixThemeWrapper
import java.lang.Integer.min import java.lang.Integer.min
import kotlin.math.ceil import kotlin.math.ceil
import kotlin.math.roundToInt import kotlin.math.roundToInt
@ -249,48 +260,54 @@ fun RowScope.SuggestionItems(words: SuggestedWords, onClick: (i: Int) -> Unit) {
} }
} }
data class Action(
@DrawableRes val icon: Int
// TODO: How should the actual action abstraction look?
)
@Composable @Composable
fun ActionItem() { fun ActionItem(action: Action, onSelect: (Action) -> Unit) {
val col = MaterialTheme.colorScheme.secondary val col = MaterialTheme.colorScheme.secondary
val contentCol = MaterialTheme.colorScheme.onSecondary val contentCol = MaterialTheme.colorScheme.onSecondary
IconButton(onClick = { /*TODO*/ }, modifier = Modifier IconButton(onClick = { onSelect(action) }, modifier = Modifier
.drawBehind { .drawBehind {
val radius = size.height / 4.0f val radius = size.height / 4.0f
drawRoundRect( drawRoundRect(
col, col,
topLeft = Offset(size.width * 0.1f, size.height * 0.1f), topLeft = Offset(size.width * 0.1f, size.height * 0.05f),
size = Size(size.width * 0.8f, size.height * 0.8f), size = Size(size.width * 0.8f, size.height * 0.9f),
cornerRadius = CornerRadius(radius, radius) cornerRadius = CornerRadius(radius, radius)
) )
} }
.width(50.dp) .width(64.dp)
.fillMaxHeight(), .fillMaxHeight(),
colors = IconButtonDefaults.iconButtonColors(contentColor = contentCol) colors = IconButtonDefaults.iconButtonColors(contentColor = contentCol)
) { ) {
Icon( Icon(
painter = painterResource(id = R.drawable.mic_fill), painter = painterResource(id = action.icon),
contentDescription = "Voice Input" contentDescription = action.name
) )
} }
} }
@Composable @Composable
fun RowScope.ActionItems() { fun ActionItemSmall(action: Action, onSelect: (Action) -> Unit) {
// TODO IconButton(onClick = {
ActionItem() onSelect(action)
ActionItem() }, modifier = Modifier
ActionItem() .width(42.dp)
.fillMaxHeight()) {
Icon(
painter = painterResource(id = action.icon),
contentDescription = action.name
)
}
}
@Composable
fun RowScope.ActionItems(onSelect: (Action) -> Unit) {
ActionItem(VoiceInputAction, onSelect)
ActionItem(ThemeAction, onSelect)
Box(modifier = Modifier Box(modifier = Modifier
.fillMaxHeight() .fillMaxHeight()
.weight(1.0f)) { .weight(1.0f)) {
AutoFitText("Note: Actions not yet implemented", style = Typography.bodyMedium.copy(color = MaterialTheme.colorScheme.onBackground))
} }
} }
@ -341,49 +358,31 @@ fun ExpandActionsButton(isActionsOpen: Boolean, onClick: () -> Unit) {
fun ActionBar( fun ActionBar(
words: SuggestedWords?, words: SuggestedWords?,
suggestionStripListener: SuggestionStripView.Listener, suggestionStripListener: SuggestionStripView.Listener,
forceOpenActionsInitially: Boolean = false onActionActivated: (Action) -> Unit,
forceOpenActionsInitially: Boolean = false,
) { ) {
val isActionsOpen = remember { mutableStateOf(forceOpenActionsInitially) } val isActionsOpen = remember { mutableStateOf(forceOpenActionsInitially) }
WhisperVoiceInputTheme {
Surface(modifier = Modifier
.fillMaxWidth()
.height(40.dp), color = MaterialTheme.colorScheme.background)
{
Row {
ExpandActionsButton(isActionsOpen.value) { isActionsOpen.value = !isActionsOpen.value }
if(isActionsOpen.value) { Surface(modifier = Modifier
ActionItems() .fillMaxWidth()
} else if(words != null) { .height(40.dp), color = MaterialTheme.colorScheme.background)
SuggestionItems(words) { {
suggestionStripListener.pickSuggestionManually( Row {
words.getInfo(it) ExpandActionsButton(isActionsOpen.value) { isActionsOpen.value = !isActionsOpen.value }
)
}
} else {
Spacer(modifier = Modifier.weight(1.0f))
}
if(isActionsOpen.value) {
// TODO: For now, this calls CODE_SHORTCUT. In the future, we will want to ActionItems(onActionActivated)
// ask the main UI to hide the keyboard and show our own voice input menu } else if(words != null) {
IconButton(onClick = { SuggestionItems(words) {
suggestionStripListener.onCodeInput( suggestionStripListener.pickSuggestionManually(
Constants.CODE_SHORTCUT, words.getInfo(it)
Constants.SUGGESTION_STRIP_COORDINATE,
Constants.SUGGESTION_STRIP_COORDINATE,
false
);
}, modifier = Modifier
.width(42.dp)
.fillMaxHeight()) {
Icon(
painter = painterResource(id = R.drawable.mic_fill),
contentDescription = "Voice Input"
) )
} }
} else {
Spacer(modifier = Modifier.weight(1.0f))
} }
ActionItemSmall(VoiceInputAction, onActionActivated)
} }
} }
} }
@ -437,18 +436,74 @@ val exampleSuggestedWordsEmpty = SuggestedWords(
@Composable @Composable
@Preview @Preview
fun PreviewActionBarWithSuggestions() { fun PreviewActionBarWithSuggestions(colorScheme: ColorScheme = DarkColorScheme) {
ActionBar(words = exampleSuggestedWords, suggestionStripListener = ExampleListener()) UixThemeWrapper(colorScheme) {
ActionBar(
words = exampleSuggestedWords,
onActionActivated = { },
suggestionStripListener = ExampleListener()
)
}
} }
@Composable @Composable
@Preview @Preview
fun PreviewActionBarWithEmptySuggestions() { fun PreviewActionBarWithEmptySuggestions(colorScheme: ColorScheme = DarkColorScheme) {
ActionBar(words = exampleSuggestedWordsEmpty, suggestionStripListener = ExampleListener()) UixThemeWrapper(colorScheme) {
ActionBar(
words = exampleSuggestedWordsEmpty,
onActionActivated = { },
suggestionStripListener = ExampleListener()
)
}
} }
@Composable @Composable
@Preview @Preview
fun PreviewExpandedActionBar() { fun PreviewExpandedActionBar(colorScheme: ColorScheme = DarkColorScheme) {
ActionBar(words = exampleSuggestedWordsEmpty, suggestionStripListener = ExampleListener(), forceOpenActionsInitially = true) UixThemeWrapper(colorScheme) {
ActionBar(
words = exampleSuggestedWordsEmpty,
onActionActivated = { },
suggestionStripListener = ExampleListener(),
forceOpenActionsInitially = true
)
}
}
@Composable
@Preview
fun PreviewActionBarWithSuggestionsDynamicLight() {
PreviewActionBarWithSuggestions(dynamicLightColorScheme(LocalContext.current))
}
@Composable
@Preview
fun PreviewActionBarWithEmptySuggestionsDynamicLight() {
PreviewActionBarWithEmptySuggestions(dynamicLightColorScheme(LocalContext.current))
}
@Composable
@Preview
fun PreviewExpandedActionBarDynamicLight() {
PreviewExpandedActionBar(dynamicLightColorScheme(LocalContext.current))
}
@Composable
@Preview
fun PreviewActionBarWithSuggestionsDynamicDark() {
PreviewActionBarWithSuggestions(dynamicDarkColorScheme(LocalContext.current))
}
@Composable
@Preview
fun PreviewActionBarWithEmptySuggestionsDynamicDark() {
PreviewActionBarWithEmptySuggestions(dynamicDarkColorScheme(LocalContext.current))
}
@Composable
@Preview
fun PreviewExpandedActionBarDynamicDark() {
PreviewExpandedActionBar(dynamicDarkColorScheme(LocalContext.current))
} }

View File

@ -0,0 +1,211 @@
@file:Suppress("LocalVariableName")
package org.futo.inputmethod.latin.uix
import android.os.Build
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.scrollable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.rememberScrollState
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import org.futo.inputmethod.latin.R
import org.futo.inputmethod.latin.uix.theme.DarkColorScheme
// TODO: For now, this calls CODE_SHORTCUT. In the future, we will want to
// make this a window
val VoiceInputAction = Action(
icon = R.drawable.mic_fill,
name = "Voice Input",
simplePressImpl = {
it.triggerSystemVoiceInput()
},
windowImpl = null
)
val ThemeAction = Action(
icon = R.drawable.eye,
name = "Theme Switcher",
simplePressImpl = null,
windowImpl = object : ActionWindow {
@Composable
override fun windowName(): String {
return "Theme Switcher"
}
@Composable
override fun WindowContents(manager: KeyboardManagerForAction) {
val context = LocalContext.current
Column(modifier = Modifier.fillMaxSize().scrollable(rememberScrollState(), Orientation.Vertical)) {
Button(onClick = {
manager.updateTheme(DarkColorScheme)
}) {
Text("Default voice input theme")
}
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
Button(onClick = {
manager.updateTheme(dynamicLightColorScheme(context))
}) {
Text("Dynamic light color scheme")
}
Button(onClick = {
manager.updateTheme(dynamicDarkColorScheme(context))
}) {
Text("Dynamic dark color scheme")
}
}
Button(onClick = {
val md_theme_light_primary = Color(0xFF6750A4)
val md_theme_light_onPrimary = Color(0xFFFFFFFF)
val md_theme_light_primaryContainer = Color(0xFFEADDFF)
val md_theme_light_onPrimaryContainer = Color(0xFF21005D)
val md_theme_light_secondary = Color(0xFF625B71)
val md_theme_light_onSecondary = Color(0xFFFFFFFF)
val md_theme_light_secondaryContainer = Color(0xFFE8DEF8)
val md_theme_light_onSecondaryContainer = Color(0xFF1D192B)
val md_theme_light_tertiary = Color(0xFF7D5260)
val md_theme_light_onTertiary = Color(0xFFFFFFFF)
val md_theme_light_tertiaryContainer = Color(0xFFFFD8E4)
val md_theme_light_onTertiaryContainer = Color(0xFF31111D)
val md_theme_light_error = Color(0xFFB3261E)
val md_theme_light_onError = Color(0xFFFFFFFF)
val md_theme_light_errorContainer = Color(0xFFF9DEDC)
val md_theme_light_onErrorContainer = Color(0xFF410E0B)
val md_theme_light_outline = Color(0xFF79747E)
val md_theme_light_background = Color(0xFFFFFBFE)
val md_theme_light_onBackground = Color(0xFF1C1B1F)
val md_theme_light_surface = Color(0xFFFFFBFE)
val md_theme_light_onSurface = Color(0xFF1C1B1F)
val md_theme_light_surfaceVariant = Color(0xFFE7E0EC)
val md_theme_light_onSurfaceVariant = Color(0xFF49454F)
val md_theme_light_inverseSurface = Color(0xFF313033)
val md_theme_light_inverseOnSurface = Color(0xFFF4EFF4)
val md_theme_light_inversePrimary = Color(0xFFD0BCFF)
val md_theme_light_shadow = Color(0xFF000000)
val md_theme_light_surfaceTint = Color(0xFF6750A4)
val md_theme_light_outlineVariant = Color(0xFFCAC4D0)
val md_theme_light_scrim = Color(0xFF000000)
manager.updateTheme(
lightColorScheme(
primary = md_theme_light_primary,
onPrimary = md_theme_light_onPrimary,
primaryContainer = md_theme_light_primaryContainer,
onPrimaryContainer = md_theme_light_onPrimaryContainer,
secondary = md_theme_light_secondary,
onSecondary = md_theme_light_onSecondary,
secondaryContainer = md_theme_light_secondaryContainer,
onSecondaryContainer = md_theme_light_onSecondaryContainer,
tertiary = md_theme_light_tertiary,
onTertiary = md_theme_light_onTertiary,
tertiaryContainer = md_theme_light_tertiaryContainer,
onTertiaryContainer = md_theme_light_onTertiaryContainer,
error = md_theme_light_error,
onError = md_theme_light_onError,
errorContainer = md_theme_light_errorContainer,
onErrorContainer = md_theme_light_onErrorContainer,
outline = md_theme_light_outline,
background = md_theme_light_background,
onBackground = md_theme_light_onBackground,
surface = md_theme_light_surface,
onSurface = md_theme_light_onSurface,
surfaceVariant = md_theme_light_surfaceVariant,
onSurfaceVariant = md_theme_light_onSurfaceVariant,
inverseSurface = md_theme_light_inverseSurface,
inverseOnSurface = md_theme_light_inverseOnSurface,
inversePrimary = md_theme_light_inversePrimary,
surfaceTint = md_theme_light_surfaceTint,
outlineVariant = md_theme_light_outlineVariant,
scrim = md_theme_light_scrim,
)
)
}) {
Text("Some random light theme")
}
Button(onClick = {
val md_theme_dark_primary = Color(0xFFD0BCFF)
val md_theme_dark_onPrimary = Color(0xFF381E72)
val md_theme_dark_primaryContainer = Color(0xFF4F378B)
val md_theme_dark_onPrimaryContainer = Color(0xFFEADDFF)
val md_theme_dark_secondary = Color(0xFFCCC2DC)
val md_theme_dark_onSecondary = Color(0xFF332D41)
val md_theme_dark_secondaryContainer = Color(0xFF4A4458)
val md_theme_dark_onSecondaryContainer = Color(0xFFE8DEF8)
val md_theme_dark_tertiary = Color(0xFFEFB8C8)
val md_theme_dark_onTertiary = Color(0xFF492532)
val md_theme_dark_tertiaryContainer = Color(0xFF633B48)
val md_theme_dark_onTertiaryContainer = Color(0xFFFFD8E4)
val md_theme_dark_error = Color(0xFFF2B8B5)
val md_theme_dark_onError = Color(0xFF601410)
val md_theme_dark_errorContainer = Color(0xFF8C1D18)
val md_theme_dark_onErrorContainer = Color(0xFFF9DEDC)
val md_theme_dark_outline = Color(0xFF938F99)
val md_theme_dark_background = Color(0xFF1C1B1F)
val md_theme_dark_onBackground = Color(0xFFE6E1E5)
val md_theme_dark_surface = Color(0xFF1C1B1F)
val md_theme_dark_onSurface = Color(0xFFE6E1E5)
val md_theme_dark_surfaceVariant = Color(0xFF49454F)
val md_theme_dark_onSurfaceVariant = Color(0xFFCAC4D0)
val md_theme_dark_inverseSurface = Color(0xFFE6E1E5)
val md_theme_dark_inverseOnSurface = Color(0xFF313033)
val md_theme_dark_inversePrimary = Color(0xFF6750A4)
val md_theme_dark_shadow = Color(0xFF000000)
val md_theme_dark_surfaceTint = Color(0xFFD0BCFF)
val md_theme_dark_outlineVariant = Color(0xFF49454F)
val md_theme_dark_scrim = Color(0xFF000000)
manager.updateTheme(
darkColorScheme(
primary = md_theme_dark_primary,
onPrimary = md_theme_dark_onPrimary,
primaryContainer = md_theme_dark_primaryContainer,
onPrimaryContainer = md_theme_dark_onPrimaryContainer,
secondary = md_theme_dark_secondary,
onSecondary = md_theme_dark_onSecondary,
secondaryContainer = md_theme_dark_secondaryContainer,
onSecondaryContainer = md_theme_dark_onSecondaryContainer,
tertiary = md_theme_dark_tertiary,
onTertiary = md_theme_dark_onTertiary,
tertiaryContainer = md_theme_dark_tertiaryContainer,
onTertiaryContainer = md_theme_dark_onTertiaryContainer,
error = md_theme_dark_error,
onError = md_theme_dark_onError,
errorContainer = md_theme_dark_errorContainer,
onErrorContainer = md_theme_dark_onErrorContainer,
outline = md_theme_dark_outline,
background = md_theme_dark_background,
onBackground = md_theme_dark_onBackground,
surface = md_theme_dark_surface,
onSurface = md_theme_dark_onSurface,
surfaceVariant = md_theme_dark_surfaceVariant,
onSurfaceVariant = md_theme_dark_onSurfaceVariant,
inverseSurface = md_theme_dark_inverseSurface,
inverseOnSurface = md_theme_dark_inverseOnSurface,
inversePrimary = md_theme_dark_inversePrimary,
surfaceTint = md_theme_dark_surfaceTint,
outlineVariant = md_theme_dark_outlineVariant,
scrim = md_theme_dark_scrim,
)
)
}) {
Text("Some random dark theme")
}
}
}
}
)

View File

@ -2,6 +2,7 @@ package org.futo.inputmethod.latin.uix.theme
import android.app.Activity import android.app.Activity
import android.os.Build import android.os.Build
import androidx.compose.material3.ColorScheme
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme import androidx.compose.material3.dynamicDarkColorScheme
@ -45,37 +46,10 @@ val DarkColorScheme = darkColorScheme(
) )
@Composable @Composable
fun WhisperVoiceInputTheme(content: @Composable () -> Unit) { fun UixThemeWrapper(colorScheme: ColorScheme, content: @Composable () -> Unit) {
// TODO: Switch light or dark mode
val colorScheme = when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
dynamicLightColorScheme(context)
}
else -> DarkColorScheme
}
//val colorScheme = DarkColorScheme // TODO: Figure out light/dynamic if it's worth it
val view = LocalView.current
if (!view.isInEditMode) {
SideEffect {
if(view.context is Activity) {
val window = (view.context as Activity).window
window.statusBarColor = colorScheme.primary.toArgb()
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars =
false
}
}
}
MaterialTheme( MaterialTheme(
colorScheme = colorScheme, colorScheme = colorScheme,
typography = Typography, typography = Typography,
content = content, content = content,
) )
} }