mirror of
https://gitlab.futo.org/keyboard/latinime.git
synced 2024-09-28 14:54:30 +01:00
Replace SuggestionStripView with ActionBar
This commit is contained in:
parent
69f6c29860
commit
138d3a7886
@ -128,6 +128,9 @@ dependencies {
|
|||||||
|
|
||||||
implementation 'com.google.code.findbugs:jsr305:3.0.2'
|
implementation 'com.google.code.findbugs:jsr305:3.0.2'
|
||||||
|
|
||||||
|
debugImplementation 'androidx.compose.ui:ui-tooling'
|
||||||
|
debugImplementation 'androidx.compose.ui:ui-test-manifest'
|
||||||
|
|
||||||
testImplementation 'junit:junit:4.13.2'
|
testImplementation 'junit:junit:4.13.2'
|
||||||
androidTestImplementation "org.mockito:mockito-core:1.9.5"
|
androidTestImplementation "org.mockito:mockito-core:1.9.5"
|
||||||
androidTestImplementation 'com.google.dexmaker:dexmaker:1.2'
|
androidTestImplementation 'com.google.dexmaker:dexmaker:1.2'
|
||||||
|
13
java/res/drawable/chevron_right.xml
Normal file
13
java/res/drawable/chevron_right.xml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="25.5dp"
|
||||||
|
android:height="25.5dp"
|
||||||
|
android:viewportWidth="25.5"
|
||||||
|
android:viewportHeight="25.5">
|
||||||
|
<path
|
||||||
|
android:pathData="M9.75,18.75L15.75,12.75L9.75,6.75"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="1.5"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#FFFFFF"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
39
java/res/drawable/mic_fill.xml
Normal file
39
java/res/drawable/mic_fill.xml
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="19.5dp"
|
||||||
|
android:height="19.5dp"
|
||||||
|
android:viewportWidth="19.5"
|
||||||
|
android:viewportHeight="19.5">
|
||||||
|
<path
|
||||||
|
android:pathData="M9.75,1.5C8.507,1.5 7.5,2.507 7.5,3.75L7.5,9.75C7.5,10.993 8.507,12 9.75,12C10.993,12 12,10.993 12,9.75L12,3.75C12,2.507 10.993,1.5 9.75,1.5ZZ"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M9.75,1.5C8.507,1.5 7.5,2.507 7.5,3.75L7.5,9.75C7.5,10.993 8.507,12 9.75,12C10.993,12 12,10.993 12,9.75L12,3.75C12,2.507 10.993,1.5 9.75,1.5ZZ"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="1.5"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#FFFFFF"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M15,8.25L15,9.75C15,12.649 12.649,15 9.75,15C6.851,15 4.5,12.649 4.5,9.75L4.5,8.25"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="1.5"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#FFFFFF"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M9.75,15L9.75,18"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="1.5"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#FFFFFF"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M6.75,18L12.75,18"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="1.5"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#FFFFFF"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
15
java/res/drawable/top_suggestion.xml
Normal file
15
java/res/drawable/top_suggestion.xml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="14.4dp"
|
||||||
|
android:height="3dp"
|
||||||
|
android:viewportWidth="14.4"
|
||||||
|
android:viewportHeight="3">
|
||||||
|
<path
|
||||||
|
android:pathData="M0,1.5a1.5,1.5 0,1 0,3 0a1.5,1.5 0,1 0,-3 0z"
|
||||||
|
android:fillColor="#FFFFFF"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M5.7,1.5a1.5,1.5 0,1 0,3 0a1.5,1.5 0,1 0,-3 0z"
|
||||||
|
android:fillColor="#FFFFFF"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M11.4,1.5a1.5,1.5 0,1 0,3 0a1.5,1.5 0,1 0,-3 0z"
|
||||||
|
android:fillColor="#FFFFFF"/>
|
||||||
|
</vector>
|
@ -24,17 +24,6 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="bottom"
|
android:layout_gravity="bottom"
|
||||||
android:orientation="vertical" >
|
android:orientation="vertical" >
|
||||||
|
|
||||||
<!-- To ensure that key preview popup is correctly placed when the current system locale is
|
|
||||||
one of RTL locales, layoutDirection="ltr" is needed in the SDK version 17+. -->
|
|
||||||
<org.futo.inputmethod.latin.suggestions.SuggestionStripView
|
|
||||||
android:id="@+id/suggestion_strip_view"
|
|
||||||
android:layoutDirection="ltr"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="@dimen/config_suggestions_strip_height"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
style="?attr/suggestionStripViewStyle" />
|
|
||||||
|
|
||||||
<!-- To ensure that key preview popup is correctly placed when the current system locale is
|
<!-- To ensure that key preview popup is correctly placed when the current system locale is
|
||||||
one of RTL locales, layoutDirection="ltr" is needed in the SDK version 17+. -->
|
one of RTL locales, layoutDirection="ltr" is needed in the SDK version 17+. -->
|
||||||
<org.futo.inputmethod.keyboard.MainKeyboardView
|
<org.futo.inputmethod.keyboard.MainKeyboardView
|
||||||
|
@ -31,8 +31,6 @@ import org.futo.inputmethod.latin.suggestions.SuggestionStripView;
|
|||||||
public final class InputView extends FrameLayout {
|
public final class InputView extends FrameLayout {
|
||||||
private final Rect mInputViewRect = new Rect();
|
private final Rect mInputViewRect = new Rect();
|
||||||
private MainKeyboardView mMainKeyboardView;
|
private MainKeyboardView mMainKeyboardView;
|
||||||
private KeyboardTopPaddingForwarder mKeyboardTopPaddingForwarder;
|
|
||||||
private MoreSuggestionsViewCanceler mMoreSuggestionsViewCanceler;
|
|
||||||
private MotionEventForwarder<?, ?> mActiveForwarder;
|
private MotionEventForwarder<?, ?> mActiveForwarder;
|
||||||
|
|
||||||
public InputView(final Context context, final AttributeSet attrs) {
|
public InputView(final Context context, final AttributeSet attrs) {
|
||||||
@ -41,17 +39,11 @@ public final class InputView extends FrameLayout {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onFinishInflate() {
|
protected void onFinishInflate() {
|
||||||
final SuggestionStripView suggestionStripView =
|
|
||||||
(SuggestionStripView)findViewById(R.id.suggestion_strip_view);
|
|
||||||
mMainKeyboardView = (MainKeyboardView)findViewById(R.id.keyboard_view);
|
mMainKeyboardView = (MainKeyboardView)findViewById(R.id.keyboard_view);
|
||||||
mKeyboardTopPaddingForwarder = new KeyboardTopPaddingForwarder(
|
|
||||||
mMainKeyboardView, suggestionStripView);
|
|
||||||
mMoreSuggestionsViewCanceler = new MoreSuggestionsViewCanceler(
|
|
||||||
mMainKeyboardView, suggestionStripView);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setKeyboardTopPadding(final int keyboardTopPadding) {
|
public void setKeyboardTopPadding(final int keyboardTopPadding) {
|
||||||
mKeyboardTopPaddingForwarder.setKeyboardTopPadding(keyboardTopPadding);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -73,20 +65,6 @@ public final class InputView extends FrameLayout {
|
|||||||
final int x = (int)me.getX(index) + rect.left;
|
final int x = (int)me.getX(index) + rect.left;
|
||||||
final int y = (int)me.getY(index) + rect.top;
|
final int y = (int)me.getY(index) + rect.top;
|
||||||
|
|
||||||
// The touch events that hit the top padding of keyboard should be forwarded to
|
|
||||||
// {@link SuggestionStripView}.
|
|
||||||
if (mKeyboardTopPaddingForwarder.onInterceptTouchEvent(x, y, me)) {
|
|
||||||
mActiveForwarder = mKeyboardTopPaddingForwarder;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// To cancel {@link MoreSuggestionsView}, we should intercept a touch event to
|
|
||||||
// {@link MainKeyboardView} and dismiss the {@link MoreSuggestionsView}.
|
|
||||||
if (mMoreSuggestionsViewCanceler.onInterceptTouchEvent(x, y, me)) {
|
|
||||||
mActiveForwarder = mMoreSuggestionsViewCanceler;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
mActiveForwarder = null;
|
mActiveForwarder = null;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -13,11 +13,7 @@ import androidx.compose.foundation.layout.Column
|
|||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
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.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.key
|
import androidx.compose.runtime.key
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.layout.onSizeChanged
|
import androidx.compose.ui.layout.onSizeChanged
|
||||||
import androidx.compose.ui.platform.ComposeView
|
import androidx.compose.ui.platform.ComposeView
|
||||||
@ -37,9 +33,13 @@ import androidx.savedstate.SavedStateRegistryController
|
|||||||
import androidx.savedstate.SavedStateRegistryOwner
|
import androidx.savedstate.SavedStateRegistryOwner
|
||||||
import androidx.savedstate.findViewTreeSavedStateRegistryOwner
|
import androidx.savedstate.findViewTreeSavedStateRegistryOwner
|
||||||
import androidx.savedstate.setViewTreeSavedStateRegistryOwner
|
import androidx.savedstate.setViewTreeSavedStateRegistryOwner
|
||||||
|
import org.futo.inputmethod.latin.uix.ActionBar
|
||||||
|
|
||||||
class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, SavedStateRegistryOwner {
|
class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, SavedStateRegistryOwner, LatinIMELegacy.SuggestionStripController {
|
||||||
private val latinIMELegacy = LatinIMELegacy(this as InputMethodService)
|
private val latinIMELegacy = LatinIMELegacy(
|
||||||
|
this as InputMethodService,
|
||||||
|
this as LatinIMELegacy.SuggestionStripController
|
||||||
|
)
|
||||||
|
|
||||||
private val mSavedStateRegistryController = SavedStateRegistryController.create(this)
|
private val mSavedStateRegistryController = SavedStateRegistryController.create(this)
|
||||||
|
|
||||||
@ -115,6 +115,8 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
|
|||||||
return composeView!!
|
return composeView!!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var shouldShowSuggestionStrip: Boolean = true
|
||||||
|
private var suggestedWords: SuggestedWords? = null
|
||||||
private fun setContent() {
|
private fun setContent() {
|
||||||
composeView?.setContent {
|
composeView?.setContent {
|
||||||
Column {
|
Column {
|
||||||
@ -123,6 +125,12 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
|
|||||||
touchableHeight = it.height
|
touchableHeight = it.height
|
||||||
}, color = MaterialTheme.colorScheme.surface) {
|
}, color = MaterialTheme.colorScheme.surface) {
|
||||||
Column {
|
Column {
|
||||||
|
if(shouldShowSuggestionStrip) {
|
||||||
|
ActionBar(
|
||||||
|
suggestedWords,
|
||||||
|
latinIMELegacy
|
||||||
|
)
|
||||||
|
}
|
||||||
key(legacyInputView) {
|
key(legacyInputView) {
|
||||||
AndroidView(factory = {
|
AndroidView(factory = {
|
||||||
legacyInputView!!
|
legacyInputView!!
|
||||||
@ -276,4 +284,18 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
|
|||||||
override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean {
|
override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean {
|
||||||
return latinIMELegacy.onKeyUp(keyCode, event) || super.onKeyUp(keyCode, event)
|
return latinIMELegacy.onKeyUp(keyCode, event) || super.onKeyUp(keyCode, event)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun updateVisibility(shouldShowSuggestionsStrip: Boolean, fullscreenMode: Boolean) {
|
||||||
|
this.shouldShowSuggestionStrip = shouldShowSuggestionsStrip
|
||||||
|
setContent()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setSuggestions(suggestedWords: SuggestedWords?, rtlSubtype: Boolean) {
|
||||||
|
this.suggestedWords = suggestedWords
|
||||||
|
setContent()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun maybeShowImportantNoticeTitle(): Boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
@ -119,6 +119,14 @@ public class LatinIMELegacy implements KeyboardActionListener,
|
|||||||
DictionaryFacilitator.DictionaryInitializationListener,
|
DictionaryFacilitator.DictionaryInitializationListener,
|
||||||
PermissionsManager.PermissionsResultCallback {
|
PermissionsManager.PermissionsResultCallback {
|
||||||
|
|
||||||
|
public interface SuggestionStripController {
|
||||||
|
public void updateVisibility(boolean shouldShowSuggestionsStrip, boolean fullscreenMode);
|
||||||
|
|
||||||
|
public void setSuggestions(SuggestedWords suggestedWords, boolean rtlSubtype);
|
||||||
|
|
||||||
|
public boolean maybeShowImportantNoticeTitle();
|
||||||
|
}
|
||||||
|
|
||||||
private final InputMethodService mInputMethodService;
|
private final InputMethodService mInputMethodService;
|
||||||
|
|
||||||
static final String TAG = LatinIMELegacy.class.getSimpleName();
|
static final String TAG = LatinIMELegacy.class.getSimpleName();
|
||||||
@ -160,7 +168,7 @@ public class LatinIMELegacy implements KeyboardActionListener,
|
|||||||
private View mInputView;
|
private View mInputView;
|
||||||
private View mComposeInputView;
|
private View mComposeInputView;
|
||||||
private InsetsUpdater mInsetsUpdater;
|
private InsetsUpdater mInsetsUpdater;
|
||||||
private SuggestionStripView mSuggestionStripView;
|
private final SuggestionStripController mSuggestionStripController;
|
||||||
|
|
||||||
private RichInputMethodManager mRichImm;
|
private RichInputMethodManager mRichImm;
|
||||||
@UsedForTesting final KeyboardSwitcher mKeyboardSwitcher;
|
@UsedForTesting final KeyboardSwitcher mKeyboardSwitcher;
|
||||||
@ -586,9 +594,10 @@ public class LatinIMELegacy implements KeyboardActionListener,
|
|||||||
JniUtils.loadNativeLibrary();
|
JniUtils.loadNativeLibrary();
|
||||||
}
|
}
|
||||||
|
|
||||||
public LatinIMELegacy(InputMethodService inputMethodService) {
|
public LatinIMELegacy(InputMethodService inputMethodService, SuggestionStripController suggestionStripController) {
|
||||||
super();
|
super();
|
||||||
mInputMethodService = inputMethodService;
|
mInputMethodService = inputMethodService;
|
||||||
|
mSuggestionStripController = suggestionStripController;
|
||||||
mSettings = Settings.getInstance();
|
mSettings = Settings.getInstance();
|
||||||
mKeyboardSwitcher = KeyboardSwitcher.getInstance();
|
mKeyboardSwitcher = KeyboardSwitcher.getInstance();
|
||||||
mStatsUtilsManager = StatsUtilsManager.getInstance();
|
mStatsUtilsManager = StatsUtilsManager.getInstance();
|
||||||
@ -846,10 +855,6 @@ public class LatinIMELegacy implements KeyboardActionListener,
|
|||||||
|
|
||||||
public void setInputView(final View view) {
|
public void setInputView(final View view) {
|
||||||
mInputView = view;
|
mInputView = view;
|
||||||
mSuggestionStripView = (SuggestionStripView)view.findViewById(R.id.suggestion_strip_view);
|
|
||||||
if (hasSuggestionStripView()) {
|
|
||||||
mSuggestionStripView.setListener(this, view);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCandidatesView(final View view) {
|
public void setCandidatesView(final View view) {
|
||||||
@ -1495,7 +1500,7 @@ public class LatinIMELegacy implements KeyboardActionListener,
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasSuggestionStripView() {
|
public boolean hasSuggestionStripView() {
|
||||||
return null != mSuggestionStripView;
|
return null != mSuggestionStripController;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setSuggestedWords(final SuggestedWords suggestedWords) {
|
private void setSuggestedWords(final SuggestedWords suggestedWords) {
|
||||||
@ -1520,7 +1525,7 @@ public class LatinIMELegacy implements KeyboardActionListener,
|
|||||||
|| currentSettingsValues.isApplicationSpecifiedCompletionsOn();
|
|| currentSettingsValues.isApplicationSpecifiedCompletionsOn();
|
||||||
final boolean shouldShowSuggestionsStrip = shouldShowSuggestionsStripUnlessPassword
|
final boolean shouldShowSuggestionsStrip = shouldShowSuggestionsStripUnlessPassword
|
||||||
&& !currentSettingsValues.mInputAttributes.mIsPasswordField;
|
&& !currentSettingsValues.mInputAttributes.mIsPasswordField;
|
||||||
mSuggestionStripView.updateVisibility(shouldShowSuggestionsStrip, mInputMethodService.isFullscreenMode());
|
mSuggestionStripController.updateVisibility(shouldShowSuggestionsStrip, mInputMethodService.isFullscreenMode());
|
||||||
if (!shouldShowSuggestionsStrip) {
|
if (!shouldShowSuggestionsStrip) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1536,7 +1541,7 @@ public class LatinIMELegacy implements KeyboardActionListener,
|
|||||||
final boolean noSuggestionsToOverrideImportantNotice = noSuggestionsFromDictionaries
|
final boolean noSuggestionsToOverrideImportantNotice = noSuggestionsFromDictionaries
|
||||||
|| isBeginningOfSentencePrediction;
|
|| isBeginningOfSentencePrediction;
|
||||||
if (shouldShowImportantNotice && noSuggestionsToOverrideImportantNotice) {
|
if (shouldShowImportantNotice && noSuggestionsToOverrideImportantNotice) {
|
||||||
if (mSuggestionStripView.maybeShowImportantNoticeTitle()) {
|
if (mSuggestionStripController.maybeShowImportantNoticeTitle()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1545,7 +1550,7 @@ public class LatinIMELegacy implements KeyboardActionListener,
|
|||||||
|| currentSettingsValues.isApplicationSpecifiedCompletionsOn()
|
|| currentSettingsValues.isApplicationSpecifiedCompletionsOn()
|
||||||
// We should clear the contextual strip if there is no suggestion from dictionaries.
|
// We should clear the contextual strip if there is no suggestion from dictionaries.
|
||||||
|| noSuggestionsFromDictionaries) {
|
|| noSuggestionsFromDictionaries) {
|
||||||
mSuggestionStripView.setSuggestions(suggestedWords,
|
mSuggestionStripController.setSuggestions(suggestedWords,
|
||||||
mRichImm.getCurrentSubtype().isRtlSubtype());
|
mRichImm.getCurrentSubtype().isRtlSubtype());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
402
java/src/org/futo/inputmethod/latin/uix/ActionBar.kt
Normal file
402
java/src/org/futo/inputmethod/latin/uix/ActionBar.kt
Normal file
@ -0,0 +1,402 @@
|
|||||||
|
package org.futo.inputmethod.latin.uix
|
||||||
|
|
||||||
|
import androidx.compose.foundation.Canvas
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.RowScope
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.material3.ButtonColors
|
||||||
|
import androidx.compose.material3.ButtonDefaults
|
||||||
|
import androidx.compose.material3.Divider
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.LocalTextStyle
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Alignment.Companion.CenterVertically
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.alpha
|
||||||
|
import androidx.compose.ui.draw.drawBehind
|
||||||
|
import androidx.compose.ui.draw.rotate
|
||||||
|
import androidx.compose.ui.geometry.CornerRadius
|
||||||
|
import androidx.compose.ui.geometry.Offset
|
||||||
|
import androidx.compose.ui.geometry.Size
|
||||||
|
import androidx.compose.ui.graphics.RectangleShape
|
||||||
|
import androidx.compose.ui.graphics.drawscope.scale
|
||||||
|
import androidx.compose.ui.graphics.drawscope.translate
|
||||||
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
|
import androidx.compose.ui.text.ExperimentalTextApi
|
||||||
|
import androidx.compose.ui.text.TextStyle
|
||||||
|
import androidx.compose.ui.text.drawText
|
||||||
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.rememberTextMeasurer
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.Constraints
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
|
import androidx.compose.ui.unit.IntSize
|
||||||
|
import androidx.compose.ui.unit.LayoutDirection
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import org.futo.inputmethod.latin.R
|
||||||
|
import org.futo.inputmethod.latin.SuggestedWords
|
||||||
|
import org.futo.inputmethod.latin.SuggestedWords.SuggestedWordInfo
|
||||||
|
import org.futo.inputmethod.latin.common.Constants
|
||||||
|
import org.futo.inputmethod.latin.suggestions.SuggestionStripView
|
||||||
|
import org.futo.inputmethod.latin.uix.theme.Typography
|
||||||
|
import org.futo.inputmethod.latin.uix.theme.WhisperVoiceInputTheme
|
||||||
|
import java.lang.Float.max
|
||||||
|
import java.lang.IndexOutOfBoundsException
|
||||||
|
import java.lang.Integer.min
|
||||||
|
import kotlin.math.ceil
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The UIX Action Bar is intended to replace the previous top bar of the AOSP keyboard.
|
||||||
|
* Its goal is to function similar to the old top bar by showing predictions, but also modernize
|
||||||
|
* it with actions and new features.
|
||||||
|
*
|
||||||
|
* Example bar:
|
||||||
|
* [>] word1 | word2 | word3 [mic]
|
||||||
|
*
|
||||||
|
* The [>] button expands the action bar, replacing word predictions with actions the user can take.
|
||||||
|
* Actions have little icons which perform an action. Some examples:
|
||||||
|
* - Microphone: opens the voice input menu
|
||||||
|
* - Undo/Redo
|
||||||
|
* - Text editing: switches to the text editing menu
|
||||||
|
* - Settings: opens the keyboard settings menu
|
||||||
|
* - Report problem: opens the report menu
|
||||||
|
*
|
||||||
|
* Generally there are a few kinds of actions:
|
||||||
|
* - Take an action on the text being typed (undo/redo)
|
||||||
|
* - Switch from the keyboard UI to something else (voice input, text editing)
|
||||||
|
* - Open an app (settings, report)
|
||||||
|
*
|
||||||
|
* The UIX effort is to modernize the AOSP Keyboard by replacing and extending
|
||||||
|
* parts of it with UI written in Android Compose, while keeping most of the
|
||||||
|
* battle-tested original keyboard code the same
|
||||||
|
*
|
||||||
|
* TODO: Will need to make RTL languages work
|
||||||
|
*/
|
||||||
|
|
||||||
|
val exampleSuggestionsList = arrayListOf(
|
||||||
|
SuggestedWordInfo("verylongword123", "", 100, 1, null, 0, 0),
|
||||||
|
SuggestedWordInfo("world understanding of patience", "", 99, 1, null, 0, 0),
|
||||||
|
SuggestedWordInfo("b", "", 98, 1, null, 0, 0),
|
||||||
|
SuggestedWordInfo("extra1", "", 97, 1, null, 0, 0),
|
||||||
|
SuggestedWordInfo("extra2", "", 96, 1, null, 0, 0),
|
||||||
|
SuggestedWordInfo("extra3", "", 95, 1, null, 0, 0)
|
||||||
|
)
|
||||||
|
|
||||||
|
val exampleSuggestedWords = SuggestedWords(
|
||||||
|
exampleSuggestionsList,
|
||||||
|
exampleSuggestionsList,
|
||||||
|
exampleSuggestionsList[0],
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
|
||||||
|
val exampleSuggestedWordsEmpty = SuggestedWords(
|
||||||
|
arrayListOf(),
|
||||||
|
arrayListOf(),
|
||||||
|
exampleSuggestionsList[0],
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
|
||||||
|
val suggestionStylePrimary = TextStyle(
|
||||||
|
fontFamily = FontFamily.SansSerif,
|
||||||
|
fontWeight = FontWeight.Medium,
|
||||||
|
fontSize = 18.sp,
|
||||||
|
lineHeight = 26.sp,
|
||||||
|
letterSpacing = 0.5.sp,
|
||||||
|
//textAlign = TextAlign.Center
|
||||||
|
)
|
||||||
|
|
||||||
|
val suggestionStyleAlternative = TextStyle(
|
||||||
|
fontFamily = FontFamily.SansSerif,
|
||||||
|
fontWeight = FontWeight.Normal,
|
||||||
|
fontSize = 18.sp,
|
||||||
|
lineHeight = 26.sp,
|
||||||
|
letterSpacing = 0.5.sp,
|
||||||
|
//textAlign = TextAlign.Center
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@OptIn(ExperimentalTextApi::class)
|
||||||
|
@Composable fun RowScope.SuggestionItem(words: SuggestedWords, idx: Int, onClick: () -> Unit) {
|
||||||
|
val word = try {
|
||||||
|
words.getWord(idx)
|
||||||
|
} catch(e: IndexOutOfBoundsException) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
val topSuggestionIcon = painterResource(id = R.drawable.top_suggestion)
|
||||||
|
val textButtonModifier = when (idx) {
|
||||||
|
0 -> Modifier.drawBehind {
|
||||||
|
with(topSuggestionIcon) {
|
||||||
|
val iconSize = topSuggestionIcon.intrinsicSize
|
||||||
|
translate(
|
||||||
|
left = (size.width - iconSize.width) / 2.0f,
|
||||||
|
top = size.height - iconSize.height * 2.0f
|
||||||
|
) {
|
||||||
|
draw(topSuggestionIcon.intrinsicSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> Modifier
|
||||||
|
}
|
||||||
|
|
||||||
|
val textModifier = when (idx) {
|
||||||
|
0 -> Modifier
|
||||||
|
else -> Modifier.alpha(0.75f)
|
||||||
|
}
|
||||||
|
|
||||||
|
val textStyle = when (idx) {
|
||||||
|
0 -> suggestionStylePrimary
|
||||||
|
else -> suggestionStyleAlternative
|
||||||
|
}.copy(color = MaterialTheme.colorScheme.onPrimary)
|
||||||
|
|
||||||
|
TextButton(
|
||||||
|
onClick = onClick,
|
||||||
|
modifier = textButtonModifier
|
||||||
|
.weight(1.0f)
|
||||||
|
.fillMaxHeight(),
|
||||||
|
shape = RectangleShape,
|
||||||
|
colors = ButtonDefaults.textButtonColors(contentColor = MaterialTheme.colorScheme.onSurface),
|
||||||
|
enabled = word != null
|
||||||
|
) {
|
||||||
|
if(word != null) {
|
||||||
|
val measurer = rememberTextMeasurer()
|
||||||
|
|
||||||
|
Canvas(modifier = textModifier.fillMaxSize()) {
|
||||||
|
val measurement = measurer.measure(
|
||||||
|
text = AnnotatedString(word),
|
||||||
|
style = textStyle,
|
||||||
|
overflow = TextOverflow.Visible,
|
||||||
|
softWrap = false,
|
||||||
|
maxLines = 1,
|
||||||
|
constraints = Constraints(
|
||||||
|
maxWidth = Int.MAX_VALUE,
|
||||||
|
maxHeight = ceil(this.size.height).roundToInt()
|
||||||
|
),
|
||||||
|
layoutDirection = LayoutDirection.Ltr,
|
||||||
|
density = this
|
||||||
|
)
|
||||||
|
|
||||||
|
val scale = Math.min(1.0f, size.width / measurement.size.width)
|
||||||
|
|
||||||
|
translate(left = (scale * (size.width - measurement.size.width)) / 2.0f) {
|
||||||
|
scale(scaleX = scale, scaleY = 1.0f) {
|
||||||
|
drawText(
|
||||||
|
measurement
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
Text(
|
||||||
|
word,
|
||||||
|
modifier = textModifier,
|
||||||
|
style = textStyle,
|
||||||
|
overflow = TextOverflow.Visible,
|
||||||
|
softWrap = false,
|
||||||
|
maxLines = 1
|
||||||
|
)
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable fun RowScope.SuggestionSeparator() {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxHeight(0.66f)
|
||||||
|
.align(CenterVertically)
|
||||||
|
.background(color = MaterialTheme.colorScheme.outline)
|
||||||
|
.width((1f / LocalDensity.current.density).dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Show the most probable in the middle, then left, then right
|
||||||
|
val ORDER_OF_SUGGESTIONS = listOf(1, 0, 2)
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun RowScope.SuggestionItems(words: SuggestedWords, onClick: (i: Int) -> Unit) {
|
||||||
|
val maxSuggestions = min(ORDER_OF_SUGGESTIONS.size, words.size())
|
||||||
|
|
||||||
|
if(maxSuggestions == 0) {
|
||||||
|
Spacer(modifier = Modifier.weight(1.0f))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i in 0 until maxSuggestions) {
|
||||||
|
val remapped = ORDER_OF_SUGGESTIONS[i]
|
||||||
|
|
||||||
|
SuggestionItem(words, remapped) { onClick(remapped) }
|
||||||
|
|
||||||
|
if (i < maxSuggestions - 1) SuggestionSeparator()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun RowScope.ActionItem() {
|
||||||
|
val col = MaterialTheme.colorScheme.secondary
|
||||||
|
IconButton(onClick = { /*TODO*/ }, modifier = Modifier
|
||||||
|
.drawBehind {
|
||||||
|
val radius = size.height / 4.0f
|
||||||
|
drawRoundRect(
|
||||||
|
col,
|
||||||
|
topLeft = Offset(size.width * 0.1f, size.height * 0.1f),
|
||||||
|
size = Size(size.width * 0.8f, size.height * 0.8f),
|
||||||
|
cornerRadius = CornerRadius(radius, radius)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.width(50.dp)
|
||||||
|
.fillMaxHeight()) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(id = R.drawable.mic_fill),
|
||||||
|
contentDescription = "Voice Input"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun RowScope.ActionItems() {
|
||||||
|
|
||||||
|
ActionItem()
|
||||||
|
ActionItem()
|
||||||
|
ActionItem()
|
||||||
|
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.weight(1.0f))
|
||||||
|
}
|
||||||
|
|
||||||
|
class ExampleListener : SuggestionStripView.Listener {
|
||||||
|
override fun showImportantNoticeContents() {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun pickSuggestionManually(word: SuggestedWordInfo?) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCodeInput(primaryCode: Int, x: Int, y: Int, isKeyRepeat: Boolean) {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@Preview
|
||||||
|
fun ActionBar(
|
||||||
|
words: SuggestedWords? = exampleSuggestedWords,
|
||||||
|
suggestionStripListener: SuggestionStripView.Listener = ExampleListener()
|
||||||
|
) {
|
||||||
|
val isActionsOpen = remember { mutableStateOf(false) }
|
||||||
|
val actionsForcedOpenByUser = remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
//LaunchedEffect(words?.isEmpty) {
|
||||||
|
// isActionsOpen.value = actionsForcedOpenByUser.value || words == null || words.isEmpty
|
||||||
|
//}
|
||||||
|
|
||||||
|
LaunchedEffect(actionsForcedOpenByUser.value) {
|
||||||
|
isActionsOpen.value = actionsForcedOpenByUser.value //|| (words == null || words.isEmpty)
|
||||||
|
}
|
||||||
|
|
||||||
|
WhisperVoiceInputTheme {
|
||||||
|
Surface(modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(40.dp), color = MaterialTheme.colorScheme.surface)
|
||||||
|
{
|
||||||
|
Row {
|
||||||
|
val moreActionsColor = MaterialTheme.colorScheme.primary
|
||||||
|
|
||||||
|
val moreActionsFill = if(isActionsOpen.value) {
|
||||||
|
MaterialTheme.colorScheme.primary
|
||||||
|
} else {
|
||||||
|
MaterialTheme.colorScheme.surface
|
||||||
|
}
|
||||||
|
IconButton(
|
||||||
|
onClick = { actionsForcedOpenByUser.value = !actionsForcedOpenByUser.value },
|
||||||
|
modifier = Modifier
|
||||||
|
.width(42.dp)
|
||||||
|
.rotate(
|
||||||
|
if (isActionsOpen.value) {
|
||||||
|
180.0f
|
||||||
|
} else {
|
||||||
|
0.0f
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.fillMaxHeight()
|
||||||
|
.drawBehind {
|
||||||
|
drawCircle(color = moreActionsColor, radius = size.width / 3.0f + 1.0f)
|
||||||
|
drawCircle(color = moreActionsFill, radius = size.width / 3.0f - 2.0f)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(id = R.drawable.chevron_right),
|
||||||
|
contentDescription = "Open Actions"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isActionsOpen.value) {
|
||||||
|
ActionItems()
|
||||||
|
} else if(words != null) {
|
||||||
|
SuggestionItems(words) {
|
||||||
|
suggestionStripListener.pickSuggestionManually(
|
||||||
|
words.getInfo(it)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Spacer(modifier = Modifier.weight(1.0f))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: For now, this calls CODE_SHORTCUT. In the future, we will want to
|
||||||
|
// ask the main UI to hide the keyboard and show our own voice input menu
|
||||||
|
IconButton(onClick = {
|
||||||
|
suggestionStripListener.onCodeInput(
|
||||||
|
Constants.CODE_SHORTCUT,
|
||||||
|
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"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
292
java/src/org/futo/inputmethod/latin/uix/theme/Color.kt
Normal file
292
java/src/org/futo/inputmethod/latin/uix/theme/Color.kt
Normal file
@ -0,0 +1,292 @@
|
|||||||
|
@file:Suppress("unused")
|
||||||
|
|
||||||
|
package org.futo.inputmethod.latin.uix.theme
|
||||||
|
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
|
||||||
|
// Colors from Tailwind - https://tailwindcss.com/docs/customizing-colors
|
||||||
|
|
||||||
|
val Slate50 = Color(0xfff8fafc)
|
||||||
|
val Slate100 = Color(0xfff1f5f9)
|
||||||
|
val Slate200 = Color(0xffe2e8f0)
|
||||||
|
val Slate300 = Color(0xffcbd5e1)
|
||||||
|
val Slate400 = Color(0xff94a3b8)
|
||||||
|
val Slate500 = Color(0xff64748b)
|
||||||
|
val Slate600 = Color(0xff475569)
|
||||||
|
val Slate700 = Color(0xff334155)
|
||||||
|
val Slate800 = Color(0xff1e293b)
|
||||||
|
val Slate900 = Color(0xff0f172a)
|
||||||
|
val Slate950 = Color(0xff020617)
|
||||||
|
|
||||||
|
|
||||||
|
val Gray50 = Color(0xfff9fafb)
|
||||||
|
val Gray100 = Color(0xfff3f4f6)
|
||||||
|
val Gray200 = Color(0xffe5e7eb)
|
||||||
|
val Gray300 = Color(0xffd1d5db)
|
||||||
|
val Gray400 = Color(0xff9ca3af)
|
||||||
|
val Gray500 = Color(0xff6b7280)
|
||||||
|
val Gray600 = Color(0xff4b5563)
|
||||||
|
val Gray700 = Color(0xff374151)
|
||||||
|
val Gray800 = Color(0xff1f2937)
|
||||||
|
val Gray900 = Color(0xff111827)
|
||||||
|
val Gray950 = Color(0xff030712)
|
||||||
|
|
||||||
|
|
||||||
|
val Zinc50 = Color(0xfffafafa)
|
||||||
|
val Zinc100 = Color(0xfff4f4f5)
|
||||||
|
val Zinc200 = Color(0xffe4e4e7)
|
||||||
|
val Zinc300 = Color(0xffd4d4d8)
|
||||||
|
val Zinc400 = Color(0xffa1a1aa)
|
||||||
|
val Zinc500 = Color(0xff71717a)
|
||||||
|
val Zinc600 = Color(0xff52525b)
|
||||||
|
val Zinc700 = Color(0xff3f3f46)
|
||||||
|
val Zinc800 = Color(0xff27272a)
|
||||||
|
val Zinc900 = Color(0xff18181b)
|
||||||
|
val Zinc950 = Color(0xff09090b)
|
||||||
|
|
||||||
|
|
||||||
|
val Neutral50 = Color(0xfffafafa)
|
||||||
|
val Neutral100 = Color(0xfff5f5f5)
|
||||||
|
val Neutral200 = Color(0xffe5e5e5)
|
||||||
|
val Neutral300 = Color(0xffd4d4d4)
|
||||||
|
val Neutral400 = Color(0xffa3a3a3)
|
||||||
|
val Neutral500 = Color(0xff737373)
|
||||||
|
val Neutral600 = Color(0xff525252)
|
||||||
|
val Neutral700 = Color(0xff404040)
|
||||||
|
val Neutral800 = Color(0xff262626)
|
||||||
|
val Neutral900 = Color(0xff171717)
|
||||||
|
val Neutral950 = Color(0xff0a0a0a)
|
||||||
|
|
||||||
|
|
||||||
|
val Stone50 = Color(0xfffafaf9)
|
||||||
|
val Stone100 = Color(0xfff5f5f4)
|
||||||
|
val Stone200 = Color(0xffe7e5e4)
|
||||||
|
val Stone300 = Color(0xffd6d3d1)
|
||||||
|
val Stone400 = Color(0xffa8a29e)
|
||||||
|
val Stone500 = Color(0xff78716c)
|
||||||
|
val Stone600 = Color(0xff57534e)
|
||||||
|
val Stone700 = Color(0xff44403c)
|
||||||
|
val Stone800 = Color(0xff292524)
|
||||||
|
val Stone900 = Color(0xff1c1917)
|
||||||
|
val Stone950 = Color(0xff0c0a09)
|
||||||
|
|
||||||
|
|
||||||
|
val Red50 = Color(0xfffef2f2)
|
||||||
|
val Red100 = Color(0xfffee2e2)
|
||||||
|
val Red200 = Color(0xfffecaca)
|
||||||
|
val Red300 = Color(0xfffca5a5)
|
||||||
|
val Red400 = Color(0xfff87171)
|
||||||
|
val Red500 = Color(0xffef4444)
|
||||||
|
val Red600 = Color(0xffdc2626)
|
||||||
|
val Red700 = Color(0xffb91c1c)
|
||||||
|
val Red800 = Color(0xff991b1b)
|
||||||
|
val Red900 = Color(0xff7f1d1d)
|
||||||
|
val Red950 = Color(0xff450a0a)
|
||||||
|
|
||||||
|
|
||||||
|
val Orange50 = Color(0xfffff7ed)
|
||||||
|
val Orange100 = Color(0xffffedd5)
|
||||||
|
val Orange200 = Color(0xfffed7aa)
|
||||||
|
val Orange300 = Color(0xfffdba74)
|
||||||
|
val Orange400 = Color(0xfffb923c)
|
||||||
|
val Orange500 = Color(0xfff97316)
|
||||||
|
val Orange600 = Color(0xffea580c)
|
||||||
|
val Orange700 = Color(0xffc2410c)
|
||||||
|
val Orange800 = Color(0xff9a3412)
|
||||||
|
val Orange900 = Color(0xff7c2d12)
|
||||||
|
val Orange950 = Color(0xff431407)
|
||||||
|
|
||||||
|
|
||||||
|
val Amber50 = Color(0xfffffbeb)
|
||||||
|
val Amber100 = Color(0xfffef3c7)
|
||||||
|
val Amber200 = Color(0xfffde68a)
|
||||||
|
val Amber300 = Color(0xfffcd34d)
|
||||||
|
val Amber400 = Color(0xfffbbf24)
|
||||||
|
val Amber500 = Color(0xfff59e0b)
|
||||||
|
val Amber600 = Color(0xffd97706)
|
||||||
|
val Amber700 = Color(0xffb45309)
|
||||||
|
val Amber800 = Color(0xff92400e)
|
||||||
|
val Amber900 = Color(0xff78350f)
|
||||||
|
val Amber950 = Color(0xff451a03)
|
||||||
|
|
||||||
|
|
||||||
|
val Yellow50 = Color(0xfffefce8)
|
||||||
|
val Yellow100 = Color(0xfffef9c3)
|
||||||
|
val Yellow200 = Color(0xfffef08a)
|
||||||
|
val Yellow300 = Color(0xfffde047)
|
||||||
|
val Yellow400 = Color(0xfffacc15)
|
||||||
|
val Yellow500 = Color(0xffeab308)
|
||||||
|
val Yellow600 = Color(0xffca8a04)
|
||||||
|
val Yellow700 = Color(0xffa16207)
|
||||||
|
val Yellow800 = Color(0xff854d0e)
|
||||||
|
val Yellow900 = Color(0xff713f12)
|
||||||
|
val Yellow950 = Color(0xff422006)
|
||||||
|
|
||||||
|
|
||||||
|
val Lime50 = Color(0xfff7fee7)
|
||||||
|
val Lime100 = Color(0xffecfccb)
|
||||||
|
val Lime200 = Color(0xffd9f99d)
|
||||||
|
val Lime300 = Color(0xffbef264)
|
||||||
|
val Lime400 = Color(0xffa3e635)
|
||||||
|
val Lime500 = Color(0xff84cc16)
|
||||||
|
val Lime600 = Color(0xff65a30d)
|
||||||
|
val Lime700 = Color(0xff4d7c0f)
|
||||||
|
val Lime800 = Color(0xff3f6212)
|
||||||
|
val Lime900 = Color(0xff365314)
|
||||||
|
val Lime950 = Color(0xff1a2e05)
|
||||||
|
|
||||||
|
|
||||||
|
val Green50 = Color(0xfff0fdf4)
|
||||||
|
val Green100 = Color(0xffdcfce7)
|
||||||
|
val Green200 = Color(0xffbbf7d0)
|
||||||
|
val Green300 = Color(0xff86efac)
|
||||||
|
val Green400 = Color(0xff4ade80)
|
||||||
|
val Green500 = Color(0xff22c55e)
|
||||||
|
val Green600 = Color(0xff16a34a)
|
||||||
|
val Green700 = Color(0xff15803d)
|
||||||
|
val Green800 = Color(0xff166534)
|
||||||
|
val Green900 = Color(0xff14532d)
|
||||||
|
val Green950 = Color(0xff052e16)
|
||||||
|
|
||||||
|
|
||||||
|
val Emerald50 = Color(0xffecfdf5)
|
||||||
|
val Emerald100 = Color(0xffd1fae5)
|
||||||
|
val Emerald200 = Color(0xffa7f3d0)
|
||||||
|
val Emerald300 = Color(0xff6ee7b7)
|
||||||
|
val Emerald400 = Color(0xff34d399)
|
||||||
|
val Emerald500 = Color(0xff10b981)
|
||||||
|
val Emerald600 = Color(0xff059669)
|
||||||
|
val Emerald700 = Color(0xff047857)
|
||||||
|
val Emerald800 = Color(0xff065f46)
|
||||||
|
val Emerald900 = Color(0xff064e3b)
|
||||||
|
val Emerald950 = Color(0xff022c22)
|
||||||
|
|
||||||
|
|
||||||
|
val Teal50 = Color(0xfff0fdfa)
|
||||||
|
val Teal100 = Color(0xffccfbf1)
|
||||||
|
val Teal200 = Color(0xff99f6e4)
|
||||||
|
val Teal300 = Color(0xff5eead4)
|
||||||
|
val Teal400 = Color(0xff2dd4bf)
|
||||||
|
val Teal500 = Color(0xff14b8a6)
|
||||||
|
val Teal600 = Color(0xff0d9488)
|
||||||
|
val Teal700 = Color(0xff0f766e)
|
||||||
|
val Teal800 = Color(0xff115e59)
|
||||||
|
val Teal900 = Color(0xff134e4a)
|
||||||
|
val Teal950 = Color(0xff042f2e)
|
||||||
|
|
||||||
|
|
||||||
|
val Cyan50 = Color(0xffecfeff)
|
||||||
|
val Cyan100 = Color(0xffcffafe)
|
||||||
|
val Cyan200 = Color(0xffa5f3fc)
|
||||||
|
val Cyan300 = Color(0xff67e8f9)
|
||||||
|
val Cyan400 = Color(0xff22d3ee)
|
||||||
|
val Cyan500 = Color(0xff06b6d4)
|
||||||
|
val Cyan600 = Color(0xff0891b2)
|
||||||
|
val Cyan700 = Color(0xff0e7490)
|
||||||
|
val Cyan800 = Color(0xff155e75)
|
||||||
|
val Cyan900 = Color(0xff164e63)
|
||||||
|
val Cyan950 = Color(0xff083344)
|
||||||
|
|
||||||
|
|
||||||
|
val Sky50 = Color(0xfff0f9ff)
|
||||||
|
val Sky100 = Color(0xffe0f2fe)
|
||||||
|
val Sky200 = Color(0xffbae6fd)
|
||||||
|
val Sky300 = Color(0xff7dd3fc)
|
||||||
|
val Sky400 = Color(0xff38bdf8)
|
||||||
|
val Sky500 = Color(0xff0ea5e9)
|
||||||
|
val Sky600 = Color(0xff0284c7)
|
||||||
|
val Sky700 = Color(0xff0369a1)
|
||||||
|
val Sky800 = Color(0xff075985)
|
||||||
|
val Sky900 = Color(0xff0c4a6e)
|
||||||
|
val Sky950 = Color(0xff082f49)
|
||||||
|
|
||||||
|
|
||||||
|
val Blue50 = Color(0xffeff6ff)
|
||||||
|
val Blue100 = Color(0xffdbeafe)
|
||||||
|
val Blue200 = Color(0xffbfdbfe)
|
||||||
|
val Blue300 = Color(0xff93c5fd)
|
||||||
|
val Blue400 = Color(0xff60a5fa)
|
||||||
|
val Blue500 = Color(0xff3b82f6)
|
||||||
|
val Blue600 = Color(0xff2563eb)
|
||||||
|
val Blue700 = Color(0xff1d4ed8)
|
||||||
|
val Blue800 = Color(0xff1e40af)
|
||||||
|
val Blue900 = Color(0xff1e3a8a)
|
||||||
|
val Blue950 = Color(0xff172554)
|
||||||
|
|
||||||
|
|
||||||
|
val Indigo50 = Color(0xffeef2ff)
|
||||||
|
val Indigo100 = Color(0xffe0e7ff)
|
||||||
|
val Indigo200 = Color(0xffc7d2fe)
|
||||||
|
val Indigo300 = Color(0xffa5b4fc)
|
||||||
|
val Indigo400 = Color(0xff818cf8)
|
||||||
|
val Indigo500 = Color(0xff6366f1)
|
||||||
|
val Indigo600 = Color(0xff4f46e5)
|
||||||
|
val Indigo700 = Color(0xff4338ca)
|
||||||
|
val Indigo800 = Color(0xff3730a3)
|
||||||
|
val Indigo900 = Color(0xff312e81)
|
||||||
|
val Indigo950 = Color(0xff1e1b4b)
|
||||||
|
|
||||||
|
|
||||||
|
val Violet50 = Color(0xfff5f3ff)
|
||||||
|
val Violet100 = Color(0xffede9fe)
|
||||||
|
val Violet200 = Color(0xffddd6fe)
|
||||||
|
val Violet300 = Color(0xffc4b5fd)
|
||||||
|
val Violet400 = Color(0xffa78bfa)
|
||||||
|
val Violet500 = Color(0xff8b5cf6)
|
||||||
|
val Violet600 = Color(0xff7c3aed)
|
||||||
|
val Violet700 = Color(0xff6d28d9)
|
||||||
|
val Violet800 = Color(0xff5b21b6)
|
||||||
|
val Violet900 = Color(0xff4c1d95)
|
||||||
|
val Violet950 = Color(0xff2e1065)
|
||||||
|
|
||||||
|
|
||||||
|
val Purple50 = Color(0xfffaf5ff)
|
||||||
|
val Purple100 = Color(0xfff3e8ff)
|
||||||
|
val Purple200 = Color(0xffe9d5ff)
|
||||||
|
val Purple300 = Color(0xffd8b4fe)
|
||||||
|
val Purple400 = Color(0xffc084fc)
|
||||||
|
val Purple500 = Color(0xffa855f7)
|
||||||
|
val Purple600 = Color(0xff9333ea)
|
||||||
|
val Purple700 = Color(0xff7e22ce)
|
||||||
|
val Purple800 = Color(0xff6b21a8)
|
||||||
|
val Purple900 = Color(0xff581c87)
|
||||||
|
val Purple950 = Color(0xff3b0764)
|
||||||
|
|
||||||
|
|
||||||
|
val Fuchsia50 = Color(0xfffdf4ff)
|
||||||
|
val Fuchsia100 = Color(0xfffae8ff)
|
||||||
|
val Fuchsia200 = Color(0xfff5d0fe)
|
||||||
|
val Fuchsia300 = Color(0xfff0abfc)
|
||||||
|
val Fuchsia400 = Color(0xffe879f9)
|
||||||
|
val Fuchsia500 = Color(0xffd946ef)
|
||||||
|
val Fuchsia600 = Color(0xffc026d3)
|
||||||
|
val Fuchsia700 = Color(0xffa21caf)
|
||||||
|
val Fuchsia800 = Color(0xff86198f)
|
||||||
|
val Fuchsia900 = Color(0xff701a75)
|
||||||
|
val Fuchsia950 = Color(0xff4a044e)
|
||||||
|
|
||||||
|
|
||||||
|
val Pink50 = Color(0xfffdf2f8)
|
||||||
|
val Pink100 = Color(0xfffce7f3)
|
||||||
|
val Pink200 = Color(0xfffbcfe8)
|
||||||
|
val Pink300 = Color(0xfff9a8d4)
|
||||||
|
val Pink400 = Color(0xfff472b6)
|
||||||
|
val Pink500 = Color(0xffec4899)
|
||||||
|
val Pink600 = Color(0xffdb2777)
|
||||||
|
val Pink700 = Color(0xffbe185d)
|
||||||
|
val Pink800 = Color(0xff9d174d)
|
||||||
|
val Pink900 = Color(0xff831843)
|
||||||
|
val Pink950 = Color(0xff500724)
|
||||||
|
|
||||||
|
|
||||||
|
val Rose50 = Color(0xfffff1f2)
|
||||||
|
val Rose100 = Color(0xffffe4e6)
|
||||||
|
val Rose200 = Color(0xfffecdd3)
|
||||||
|
val Rose300 = Color(0xfffda4af)
|
||||||
|
val Rose400 = Color(0xfffb7185)
|
||||||
|
val Rose500 = Color(0xfff43f5e)
|
||||||
|
val Rose600 = Color(0xffe11d48)
|
||||||
|
val Rose700 = Color(0xffbe123c)
|
||||||
|
val Rose800 = Color(0xff9f1239)
|
||||||
|
val Rose900 = Color(0xff881337)
|
||||||
|
val Rose950 = Color(0xff4c0519)
|
77
java/src/org/futo/inputmethod/latin/uix/theme/Theme.kt
Normal file
77
java/src/org/futo/inputmethod/latin/uix/theme/Theme.kt
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
package org.futo.inputmethod.latin.uix.theme
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.darkColorScheme
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.SideEffect
|
||||||
|
import androidx.compose.ui.graphics.toArgb
|
||||||
|
import androidx.compose.ui.platform.LocalView
|
||||||
|
import androidx.core.view.WindowCompat
|
||||||
|
|
||||||
|
private val DarkColorScheme = darkColorScheme(
|
||||||
|
primary = Slate600,
|
||||||
|
onPrimary = Slate50,
|
||||||
|
|
||||||
|
primaryContainer = Slate700,
|
||||||
|
onPrimaryContainer = Slate50,
|
||||||
|
|
||||||
|
secondary = Slate700,
|
||||||
|
onSecondary = Slate50,
|
||||||
|
|
||||||
|
secondaryContainer = Slate600,
|
||||||
|
onSecondaryContainer = Slate50,
|
||||||
|
|
||||||
|
tertiary = Stone700,
|
||||||
|
onTertiary = Stone50,
|
||||||
|
|
||||||
|
tertiaryContainer = Stone600,
|
||||||
|
onTertiaryContainer = Stone50,
|
||||||
|
|
||||||
|
background = Slate900,
|
||||||
|
onBackground = Slate50,
|
||||||
|
|
||||||
|
surface = Slate800,
|
||||||
|
onSurface = Slate50,
|
||||||
|
|
||||||
|
outline = Slate300,
|
||||||
|
|
||||||
|
surfaceVariant = Slate800,
|
||||||
|
onSurfaceVariant = Slate300
|
||||||
|
)
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun WhisperVoiceInputTheme(content: @Composable () -> Unit) {
|
||||||
|
/*
|
||||||
|
val colorScheme = when {
|
||||||
|
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
||||||
|
val context = LocalContext.current
|
||||||
|
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
darkTheme -> DarkColorScheme
|
||||||
|
else -> LightColorScheme
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
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(
|
||||||
|
colorScheme = colorScheme,
|
||||||
|
typography = Typography,
|
||||||
|
content = content
|
||||||
|
)
|
||||||
|
}
|
34
java/src/org/futo/inputmethod/latin/uix/theme/Type.kt
Normal file
34
java/src/org/futo/inputmethod/latin/uix/theme/Type.kt
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package org.futo.inputmethod.latin.uix.theme
|
||||||
|
|
||||||
|
import androidx.compose.material3.Typography
|
||||||
|
import androidx.compose.ui.text.TextStyle
|
||||||
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
|
||||||
|
// Set of Material typography styles to start with
|
||||||
|
val Typography = Typography(
|
||||||
|
bodyLarge = TextStyle(
|
||||||
|
fontFamily = FontFamily.SansSerif,
|
||||||
|
fontWeight = FontWeight.Light,
|
||||||
|
fontSize = 20.sp,
|
||||||
|
lineHeight = 26.sp,
|
||||||
|
letterSpacing = 0.5.sp
|
||||||
|
),
|
||||||
|
/* Other default text styles to override
|
||||||
|
titleLarge = TextStyle(
|
||||||
|
fontFamily = FontFamily.Default,
|
||||||
|
fontWeight = FontWeight.Normal,
|
||||||
|
fontSize = 22.sp,
|
||||||
|
lineHeight = 28.sp,
|
||||||
|
letterSpacing = 0.sp
|
||||||
|
),
|
||||||
|
*/
|
||||||
|
labelSmall = TextStyle(
|
||||||
|
fontFamily = FontFamily.Default,
|
||||||
|
fontWeight = FontWeight.Medium,
|
||||||
|
fontSize = 11.sp,
|
||||||
|
lineHeight = 16.sp,
|
||||||
|
letterSpacing = 0.5.sp
|
||||||
|
)
|
||||||
|
)
|
@ -18,7 +18,7 @@ package org.futo.inputmethod.latin;
|
|||||||
|
|
||||||
public class LatinIMELegacyForTests extends LatinIMELegacy {
|
public class LatinIMELegacyForTests extends LatinIMELegacy {
|
||||||
public LatinIMELegacyForTests() {
|
public LatinIMELegacyForTests() {
|
||||||
super(mInputMethodService);
|
super(mInputMethodService, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
Loading…
x
Reference in New Issue
Block a user