Create certain drawables during runtime for theming

This commit is contained in:
Aleksandras Kostarevas 2023-08-18 15:55:09 +03:00
parent 039f9145c3
commit 5a4f42cafe
5 changed files with 192 additions and 13 deletions

View File

@ -30,10 +30,13 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.NinePatchDrawable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.ContextThemeWrapper;
import android.view.View;
import org.futo.inputmethod.keyboard.internal.KeyDrawParams;
import org.futo.inputmethod.keyboard.internal.KeyVisualAttributes;
import org.futo.inputmethod.latin.KeyboardDrawableProvider;
import org.futo.inputmethod.latin.KeyboardDrawableProviderOwner;
import org.futo.inputmethod.latin.R;
import org.futo.inputmethod.latin.common.Constants;
import org.futo.inputmethod.latin.utils.TypefaceUtils;
@ -89,9 +92,11 @@ public class KeyboardView extends View {
private final float mKeyShiftedLetterHintPadding;
private final float mKeyTextShadowRadius;
private final float mVerticalCorrection;
private final Drawable mKeyboardBackground;
private final Drawable mKeyBackground;
private final Drawable mFunctionalKeyBackground;
private final Drawable mSpacebarBackground;
private final KeyboardDrawableProvider mDrawableProvider;
private final float mSpacebarIconWidthRatio;
private final Rect mKeyBackgroundPadding = new Rect();
private static final float KET_TEXT_SHADOW_RADIUS_DISABLED = -1.0f;
@ -131,15 +136,26 @@ public class KeyboardView extends View {
final TypedArray keyboardViewAttr = context.obtainStyledAttributes(attrs,
R.styleable.KeyboardView, defStyle, R.style.KeyboardView);
mKeyBackground = keyboardViewAttr.getDrawable(R.styleable.KeyboardView_keyBackground);
assert(context instanceof ContextThemeWrapper);
assert(((ContextThemeWrapper) context).getBaseContext() instanceof KeyboardDrawableProviderOwner);
mDrawableProvider = ((KeyboardDrawableProviderOwner) ((ContextThemeWrapper) context).getBaseContext()).getDrawableProvider();
boolean isMoreKeys = defStyle == R.attr.moreKeysKeyboardViewStyle || defStyle == R.attr.moreKeysKeyboardViewForActionStyle;
mKeyboardBackground = isMoreKeys ?
mDrawableProvider.getMoreKeysKeyboardBackground() : mDrawableProvider.getKeyboardBackground();
setBackground(mKeyboardBackground);
mKeyBackground = isMoreKeys ?
mDrawableProvider.getPopupKey() : mDrawableProvider.getKeyBackground();
mKeyBackground.getPadding(mKeyBackgroundPadding);
final Drawable functionalKeyBackground = keyboardViewAttr.getDrawable(
R.styleable.KeyboardView_functionalKeyBackground);
mFunctionalKeyBackground = (functionalKeyBackground != null) ? functionalKeyBackground
: mKeyBackground;
final Drawable spacebarBackground = keyboardViewAttr.getDrawable(
R.styleable.KeyboardView_spacebarBackground);
mSpacebarBackground = (spacebarBackground != null) ? spacebarBackground : mKeyBackground;
mFunctionalKeyBackground = mKeyBackground;
mSpacebarBackground = mDrawableProvider.getSpaceBarBackground();
mSpacebarIconWidthRatio = keyboardViewAttr.getFloat(
R.styleable.KeyboardView_spacebarIconWidthRatio, 1.0f);
mKeyHintLetterPadding = keyboardViewAttr.getDimension(

View File

@ -2,22 +2,37 @@ package org.futo.inputmethod.latin
import android.content.Context
import android.content.res.Configuration
import android.graphics.Color
import android.graphics.drawable.Drawable
import android.graphics.drawable.GradientDrawable
import android.graphics.drawable.InsetDrawable
import android.graphics.drawable.LayerDrawable
import android.graphics.drawable.ShapeDrawable
import android.graphics.drawable.StateListDrawable
import android.graphics.drawable.shapes.OvalShape
import android.graphics.drawable.shapes.Shape
import android.inputmethodservice.InputMethodService
import android.util.TypedValue
import android.view.KeyEvent
import android.view.View
import android.view.inputmethod.CompletionInfo
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
import android.view.inputmethod.InputMethodSubtype
import androidx.annotation.ColorInt
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.key
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
@ -34,8 +49,151 @@ import androidx.savedstate.SavedStateRegistryOwner
import androidx.savedstate.findViewTreeSavedStateRegistryOwner
import androidx.savedstate.setViewTreeSavedStateRegistryOwner
import org.futo.inputmethod.latin.uix.ActionBar
import org.futo.inputmethod.latin.uix.theme.DarkColorScheme
interface KeyboardDrawableProvider {
val primaryKeyboardColor: Int
val keyboardBackground: Drawable
val keyBackground: Drawable
val spaceBarBackground: Drawable
val moreKeysKeyboardBackground: Drawable
val popupKey: Drawable
}
// TODO: Expand the number of drawables this provides so it covers the full theme, and
// build some system to dynamically change these colors
class BasicThemeProvider(val context: Context) : KeyboardDrawableProvider {
override val primaryKeyboardColor: Int
override val keyboardBackground: Drawable
override val keyBackground: Drawable
override val spaceBarBackground: Drawable
override val moreKeysKeyboardBackground: Drawable
override val popupKey: Drawable
private fun dp(dp: Dp): Float {
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
dp.value,
context.resources.displayMetrics
);
}
private fun coloredRectangle(@ColorInt color: Int): GradientDrawable {
return GradientDrawable().apply {
shape = GradientDrawable.RECTANGLE
setColor(color)
}
}
private fun coloredRoundedRectangle(@ColorInt color: Int, radius: Float): GradientDrawable {
return GradientDrawable().apply {
shape = GradientDrawable.RECTANGLE
cornerRadius = radius
setColor(color)
}
}
private fun coloredOval(@ColorInt color: Int): GradientDrawable {
return GradientDrawable().apply {
shape = GradientDrawable.OVAL
cornerRadius = Float.MAX_VALUE
setColor(color)
}
}
private fun StateListDrawable.addStateWithHighlightLayerOnPressed(@ColorInt highlight: Int, stateSet: IntArray, drawable: Drawable) {
addState(intArrayOf(android.R.attr.state_pressed) + stateSet, LayerDrawable(arrayOf(
drawable,
coloredRoundedRectangle(highlight, dp(8.dp))
)))
addState(stateSet, drawable)
}
init {
val primary = DarkColorScheme.primary.toArgb()
val secondary = DarkColorScheme.secondary.toArgb()
val highlight = DarkColorScheme.outline.copy(alpha = 0.33f).toArgb()
val background = DarkColorScheme.surface.toArgb()
val surface = DarkColorScheme.background.toArgb()
val outline = DarkColorScheme.outline.toArgb()
val transparent = Color.TRANSPARENT
primaryKeyboardColor = background
keyboardBackground = LayerDrawable(arrayOf(
coloredRectangle(primary),
InsetDrawable(coloredRectangle(background),
0, 1, 0, 0)
))
keyBackground = StateListDrawable().apply {
addStateWithHighlightLayerOnPressed(highlight, intArrayOf(android.R.attr.state_active),
coloredRoundedRectangle(secondary, dp(8.dp)).apply {
setSize(dp(64.dp).toInt(), dp(48.dp).toInt())
}
)
addStateWithHighlightLayerOnPressed(highlight, intArrayOf(android.R.attr.state_checkable, android.R.attr.state_checked),
coloredRoundedRectangle(secondary, dp(8.dp))
)
addStateWithHighlightLayerOnPressed(highlight, intArrayOf(android.R.attr.state_checkable),
coloredRectangle(transparent)
)
addStateWithHighlightLayerOnPressed(highlight, intArrayOf(android.R.attr.state_empty),
coloredRectangle(transparent)
)
addStateWithHighlightLayerOnPressed(highlight, intArrayOf(),
coloredRectangle(transparent)
)
}
spaceBarBackground = StateListDrawable().apply {
addState(intArrayOf(android.R.attr.state_pressed),
LayerDrawable(arrayOf(
coloredRoundedRectangle(secondary, dp(32.dp)),
coloredRoundedRectangle(highlight, dp(32.dp))
))
)
addState(intArrayOf(),
coloredRoundedRectangle(secondary, dp(32.dp))
)
}
moreKeysKeyboardBackground = coloredRoundedRectangle(surface, dp(8.dp)).apply {
}
popupKey = StateListDrawable().apply {
addStateWithHighlightLayerOnPressed(highlight, intArrayOf(),
coloredRoundedRectangle(surface, dp(8.dp))
)
}
}
}
interface KeyboardDrawableProviderOwner {
fun getDrawableProvider(): KeyboardDrawableProvider
}
class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, SavedStateRegistryOwner, LatinIMELegacy.SuggestionStripController, KeyboardDrawableProviderOwner {
private var drawableProvider: KeyboardDrawableProvider? = null
override fun getDrawableProvider(): KeyboardDrawableProvider {
if(drawableProvider == null) {
drawableProvider = BasicThemeProvider(this)
}
return drawableProvider!!
}
class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, SavedStateRegistryOwner, LatinIMELegacy.SuggestionStripController {
private val latinIMELegacy = LatinIMELegacy(
this as InputMethodService,
this as LatinIMELegacy.SuggestionStripController

View File

@ -823,6 +823,8 @@ public class LatinIMELegacy implements KeyboardActionListener,
* @see android.content.Context#createDisplayContext(Display)
*/
private @NonNull Context getDisplayContext() {
// TODO: We need to pass LatinIME for theming
/*
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
// createDisplayContext is not available.
return mInputMethodService;
@ -837,6 +839,8 @@ public class LatinIMELegacy implements KeyboardActionListener,
// keyboard layout with this context.
final WindowManager wm = (WindowManager) mInputMethodService.getSystemService(Context.WINDOW_SERVICE);
return mInputMethodService.createDisplayContext(wm.getDefaultDisplay());
*/
return mInputMethodService;
}
public View onCreateInputView() {
@ -1944,11 +1948,12 @@ public class LatinIMELegacy implements KeyboardActionListener,
}
private void setNavigationBarVisibility(final boolean visible) {
int color = ((KeyboardDrawableProviderOwner)getInputMethodService()).getDrawableProvider().getPrimaryKeyboardColor();
if (BuildCompatUtils.EFFECTIVE_SDK_INT > Build.VERSION_CODES.M) {
// For N and later, IMEs can specify Color.TRANSPARENT to make the navigation bar
// transparent. For other colors the system uses the default color.
mInputMethodService.getWindow().getWindow().setNavigationBarColor(
visible ? Color.BLACK : Color.TRANSPARENT);
visible ? color : Color.TRANSPARENT);
}
}

View File

@ -277,7 +277,7 @@ fun ExpandActionsButton(isActionsOpen: Boolean, onClick: () -> Unit) {
val moreActionsFill = if(isActionsOpen) {
MaterialTheme.colorScheme.primary
} else {
MaterialTheme.colorScheme.surface
MaterialTheme.colorScheme.background
}
IconButton(
onClick = onClick,
@ -314,7 +314,7 @@ fun ActionBar(
WhisperVoiceInputTheme {
Surface(modifier = Modifier
.fillMaxWidth()
.height(40.dp), color = MaterialTheme.colorScheme.surface)
.height(40.dp), color = MaterialTheme.colorScheme.background)
{
Row {
ExpandActionsButton(isActionsOpen.value) { isActionsOpen.value = !isActionsOpen.value }

View File

@ -9,7 +9,7 @@ import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalView
import androidx.core.view.WindowCompat
private val DarkColorScheme = darkColorScheme(
val DarkColorScheme = darkColorScheme(
primary = Slate600,
onPrimary = Slate50,