Merge branch 'main' into side_padding

This commit is contained in:
Helium314 2025-01-26 15:31:54 +01:00 committed by GitHub
commit d9e81e7a33
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
76 changed files with 505 additions and 277 deletions

View file

@ -9,7 +9,7 @@ If you have difficulties implementing some functionality, you're welcome to ask
# Guidelines # Guidelines
HeliBoard is a complex application, when contributing, you must take a step back and make sure your contribution: HeliBoard is a complex application, when contributing, you must take a step back and make sure your contribution:
- **Is actually wanted**. Best check related open issues before you start working on a PR. Issues with "PR" and "contributor needed" labels are accepted, but still it would be good if you announced that you are working on it, so we can discuss how changes are best implemented. - **Is actually wanted**. Best check related open issues before you start working on a PR. Issues with the [labels](https://github.com/Helium314/HeliBoard/labels) [_PR_](https://github.com/Helium314/HeliBoard/labels/PR) and [_contributor needed_](https://github.com/Helium314/HeliBoard/issues?q=label%3A%22contributor%20needed%22) (even closed ones) are accepted, but still it would be good if you announced that you are working on it, so we can discuss how changes are best implemented.
If there is no accepted issue related to your intended contribution, it's a good idea to open a new one (and of course getting one of "PR" or "contributor needed" labels) to avoid disappointment of the contribution not being accepted. For small changes or fixing obvious bugs this step is not necessary. If there is no accepted issue related to your intended contribution, it's a good idea to open a new one (and of course getting one of "PR" or "contributor needed" labels) to avoid disappointment of the contribution not being accepted. For small changes or fixing obvious bugs this step is not necessary.
- **Is only about a single thing**. Mixing unrelated or semi-related contributions into a single PR is hard to review and can get messy. - **Is only about a single thing**. Mixing unrelated or semi-related contributions into a single PR is hard to review and can get messy.
- **Is finished or a draft**. When you keep changing the PR without reviewer's feedback, any attempt to review it is doomed and a waste of time. Better mark it as a draft in this case. - **Is finished or a draft**. When you keep changing the PR without reviewer's feedback, any attempt to review it is doomed and a waste of time. Better mark it as a draft in this case.
@ -18,6 +18,7 @@ HeliBoard is a complex application, when contributing, you must take a step back
- **Has a low footprint**. Some parts of the code are executed very frequently, and the keyboard should stay responsive even on older devices. - **Has a low footprint**. Some parts of the code are executed very frequently, and the keyboard should stay responsive even on older devices.
- **Does not bring any non-free code or proprietary binary blobs**. This also applies to code/binaries with unknown licenses. Make sure you do not introduce any closed-source library from Google. - **Does not bring any non-free code or proprietary binary blobs**. This also applies to code/binaries with unknown licenses. Make sure you do not introduce any closed-source library from Google.
If your contribution contains code that is not your own, provide a link to the source. If your contribution contains code that is not your own, provide a link to the source.
- **Does not increase app size too much**. Just code changes or adding icons is not in issue, but e.g. large dependencies or adding more default dictionaries will not be accepted.
- **Complies with the user privacy principle HeliBoard follows**. - **Complies with the user privacy principle HeliBoard follows**.
A good description and small scope ("single thing") massively help with reviewing. Don't be surprised when your PR gets closes if you clearly / repeatedly violate these parts of the guidelines. A good description and small scope ("single thing") massively help with reviewing. Don't be surprised when your PR gets closes if you clearly / repeatedly violate these parts of the guidelines.

View file

@ -1,117 +0,0 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
android {
compileSdk 34
buildToolsVersion = '34.0.0'
defaultConfig {
applicationId "helium314.keyboard"
minSdkVersion 21
targetSdkVersion 34
versionCode 2301
versionName '2.3'
ndk {
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
}
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
buildTypes {
release {
minifyEnabled true
shrinkResources false
debuggable false
jniDebuggable false
renderscriptDebuggable false
}
nouserlib { // same as release, but does not allow the user to provide a library
minifyEnabled true
shrinkResources false
debuggable false
jniDebuggable false
renderscriptDebuggable false
}
debug {
minifyEnabled true
jniDebuggable false
applicationIdSuffix ".debug"
}
runTests { // build variant for running tests on CI that skips tests known to fail
minifyEnabled true
jniDebuggable false
}
archivesBaseName = "HeliBoard_" + defaultConfig.versionName
}
buildFeatures {
viewBinding true
buildConfig true
}
externalNativeBuild {
ndkBuild {
path 'src/main/jni/Android.mk'
}
}
ndkVersion '26.2.11394342'
packagingOptions {
jniLibs {
// shrinks APK by 3 MB, zipped size unchanged
useLegacyPackaging true
}
}
testOptions {
unitTests {
includeAndroidResources = true
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_17.toString()
}
// see https://github.com/Helium314/HeliBoard/issues/477
dependenciesInfo {
includeInApk = false
includeInBundle = false
}
namespace "helium314.keyboard.latin"
lint {
abortOnError true
}
}
dependencies {
// androidx
implementation 'androidx.core:core-ktx:1.13.1'
implementation 'androidx.appcompat:appcompat:1.7.0'
implementation 'androidx.preference:preference:1.2.1'
implementation 'androidx.recyclerview:recyclerview:1.3.2'
implementation 'androidx.autofill:autofill:1.1.0'
// kotlin
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3"
// color picker for user-defined colors
implementation 'com.github.martin-stone:hsv-alpha-color-picker-android:3.1.0'
// test
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.mockito:mockito-core:5.14.2'
testImplementation 'org.robolectric:robolectric:4.12.1'
testImplementation 'androidx.test:runner:1.6.2'
testImplementation 'androidx.test:core:1.6.1'
}

118
app/build.gradle.kts Executable file
View file

@ -0,0 +1,118 @@
plugins {
id("com.android.application")
kotlin("android")
kotlin("plugin.serialization") version "2.0.21"
}
android {
compileSdk = 34
buildToolsVersion = "34.0.0"
defaultConfig {
applicationId = "helium314.keyboard"
minSdk = 21
targetSdk = 34
versionCode = 2301
versionName = "2.3"
ndk {
abiFilters.clear()
abiFilters.addAll(listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64"))
}
proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
}
buildTypes {
release {
isMinifyEnabled = true
isShrinkResources = false
isDebuggable = false
isJniDebuggable = false
}
create("nouserlib") { // same as release, but does not allow the user to provide a library
isMinifyEnabled = true
isShrinkResources = false
isDebuggable = false
isJniDebuggable = false
}
debug {
isMinifyEnabled = true
isJniDebuggable = false
applicationIdSuffix = ".debug"
}
create("runTests") { // build variant for running tests on CI that skips tests known to fail
isMinifyEnabled = true
isJniDebuggable = false
}
base.archivesBaseName = "HeliBoard_" + defaultConfig.versionName
}
buildFeatures {
viewBinding = true
buildConfig = true
}
externalNativeBuild {
ndkBuild {
path = File("src/main/jni/Android.mk")
}
}
ndkVersion = "26.2.11394342"
packagingOptions {
jniLibs {
// shrinks APK by 3 MB, zipped size unchanged
useLegacyPackaging = true
}
}
testOptions {
unitTests {
isIncludeAndroidResources = true
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_17.toString()
}
// see https://github.com/Helium314/HeliBoard/issues/477
dependenciesInfo {
includeInApk = false
includeInBundle = false
}
namespace = "helium314.keyboard.latin"
lint {
abortOnError = true
}
}
dependencies {
// androidx
implementation("androidx.core:core-ktx:1.13.1")
implementation("androidx.appcompat:appcompat:1.7.0")
implementation("androidx.preference:preference:1.2.1")
implementation("androidx.recyclerview:recyclerview:1.3.2")
implementation("androidx.autofill:autofill:1.1.0")
// kotlin
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.0")
// color picker for user-defined colors
implementation("com.github.martin-stone:hsv-alpha-color-picker-android:3.1.0")
// test
testImplementation(kotlin("test"))
testImplementation("junit:junit:4.13.2")
testImplementation("org.mockito:mockito-core:5.15.2")
testImplementation("org.robolectric:robolectric:4.12.1")
testImplementation("androidx.test:runner:1.6.2")
testImplementation("androidx.test:core:1.6.1")
}

View file

@ -6,3 +6,6 @@ o ó ô ö ò õ œ ø ō
u ú û ü ù ū u ú û ü ù ū
n ñ ń n ñ ń
y ý ij y ý ij
[tlds]
za

View file

@ -24,3 +24,6 @@ question: ؟
[number_row] [number_row]
١ ٢ ٣ ٤ ٥ ٦ ٧ ٨ ٩ ٠ ١ ٢ ٣ ٤ ٥ ٦ ٧ ٨ ٩ ٠
[tlds]
sa

View file

@ -3,3 +3,6 @@
[labels] [labels]
alphabet: АБВ alphabet: АБВ
[tlds]
bg

View file

@ -10,3 +10,6 @@ punctuation !autoColumnOrder!9 \, ? ! · # ) ( / ; ' @ : - " + \% &
[extra_keys] [extra_keys]
2: ç 2: ç
[tlds]
cat es

View file

@ -14,3 +14,6 @@ y ý
z ž z ž
' '
" ” „ “ » « " ” „ “ » «
[tlds]
cz

View file

@ -9,3 +9,6 @@ o ø
1: å 1: å
2: æ ä 2: æ ä
2: ø ö 2: ø ö
[tlds]
dk

View file

@ -5,3 +5,6 @@ u ü
s ß s ß
' '
" ” „ “ » « " ” „ “ » «
[tlds]
de at ch

View file

@ -9,3 +9,6 @@
[labels] [labels]
alphabet: ΑΒΓ alphabet: ΑΒΓ
[tlds]
gr

View file

@ -10,3 +10,6 @@ punctuation !autoColumnOrder!9 \, ? ! # ) ( / ; ¡ ' @ : - " + \% & ¿
[extra_keys] [extra_keys]
2: ñ 2: ñ
[tlds]
es com.es

View file

@ -12,3 +12,6 @@ z ž
2: ö õ 2: ö õ
2: ä 2: ä
3: õ 3: õ
[tlds]
ee

View file

@ -17,3 +17,6 @@ question: ؟
[number_row] [number_row]
۱ ۲ ۳ ۴ ۵ ۶ ۷ ۸ ۹ ۰ ۱ ۲ ۳ ۴ ۵ ۶ ۷ ۸ ۹ ۰
[tlds]
ir

View file

@ -8,3 +8,6 @@ z ž
1: å 1: å
2: ö ø 2: ö ø
2: ä æ 2: ä æ
[tlds]
fi

View file

@ -11,3 +11,6 @@ y ÿ
1: è ü 1: è ü
2: é ö 2: é ö
2: à ä 2: à ä
[tlds]
fr

View file

@ -0,0 +1,2 @@
[tlds]
in

View file

@ -73,3 +73,6 @@ period: ।
[number_row] [number_row]
१ २ ३ ४ ५ ६ ७ ८ ९ १ २ ३ ४ ५ ६ ७ ८ ९
[tlds]
in

View file

@ -5,3 +5,6 @@ c č ć
d đ d đ
' '
" “ „ ” » « " “ „ ” » «
[tlds]
hr

View file

@ -6,3 +6,6 @@ o ó ö ő
u ú ü ű u ú ü ű
' '
" “ „ ” » « " “ „ ” » «
[tlds]
hu gov.hu

View file

@ -6,3 +6,6 @@ punctuation !autoColumnOrder!8 \, ՞ ՜ … ' = / ՝ ՛ ֊ » « ― ) (
[labels] [labels]
alphabet: ԱԲԳ alphabet: ԱԲԳ
period: ։ period: ։
[tlds]
am

View file

@ -9,3 +9,6 @@ y ý
t þ t þ
' '
" ” „ “ " ” „ “
[tlds]
is

View file

@ -4,3 +4,6 @@ e è é ə
i ì i ì
o ò ó º o ò ó º
u ù u ù
[tlds]
it gov.it edu.it

View file

@ -10,3 +10,6 @@
[labels] [labels]
alphabet: אבג alphabet: אבג
[tlds]
il co.il gov.il

View file

@ -14,3 +14,5 @@ b p
[labels] [labels]
alphabet: AƐΓ alphabet: AƐΓ
[tlds]
dz fr

View file

@ -1,2 +1,5 @@
[labels] [labels]
alphabet: ㄱㄴㄷ alphabet: ㄱㄴㄷ
[tlds]
kr

View file

@ -8,3 +8,6 @@ u ū ų
z ž z ž
' '
" ” „ “ " ” „ “
[tlds]
lt

View file

@ -13,3 +13,6 @@ u ū
z ž z ž
' '
" ” „ “ " ” „ “
[tlds]
lv

View file

@ -9,3 +9,6 @@ o ø
1: å 1: å
2: ø ö 2: ø ö
2: æ ä 2: æ ä
[tlds]
no

View file

@ -9,3 +9,6 @@ z ż ź
l ł l ł
' '
" “ „ ” " “ „ ”
[tlds]
pl

View file

@ -5,3 +5,6 @@ s ș
t ț t ț
' '
" “ „ ” " “ „ ”
[tlds]
ro

View file

@ -6,3 +6,6 @@
[labels] [labels]
alphabet: АБВ alphabet: АБВ
[tlds]
ru

View file

@ -15,3 +15,6 @@ z ž
l ľ ĺ l ľ ĺ
' '
" ” „ “ » « " ” „ “ » «
[tlds]
sk

View file

@ -4,3 +4,6 @@ c č ć
z ž z ž
' '
" ” „ “ » « " ” „ “ » «
[tlds]
si

View file

@ -10,3 +10,6 @@ d đ
2: ć 2: ć
3: đ 3: đ
3: ž 3: ž
[tlds]
rs

View file

@ -6,3 +6,6 @@
[labels] [labels]
alphabet: АБВ alphabet: АБВ
[tlds]
rs

View file

@ -8,3 +8,6 @@ o ö
1: å 1: å
2: ö 2: ö
2: ä 2: ä
[tlds]
sv

View file

@ -7,3 +7,6 @@ u ü û
s ş s ş
g ğ g ğ
c ç c ç
[tlds]
tr gov.tr edu.tr com.tr

View file

@ -7,3 +7,6 @@
[labels] [labels]
alphabet: АБВ alphabet: АБВ
[tlds]
ua

View file

@ -6,3 +6,6 @@ o ò ó ỏ õ ọ ô ồ ố ổ ỗ ộ ơ ờ ớ ở ỡ ợ
u ù ú ủ ũ ụ ư ừ ứ ử ữ ự u ù ú ủ ũ ụ ư ừ ứ ử ữ ự
y ỳ ý ỷ ỹ ỵ y ỳ ý ỷ ỹ ỵ
d đ d đ
[tlds]
vn

View file

@ -8,6 +8,7 @@ package helium314.keyboard.keyboard;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
@ -40,8 +41,10 @@ import helium314.keyboard.latin.RichInputMethodSubtype;
import helium314.keyboard.latin.WordComposer; import helium314.keyboard.latin.WordComposer;
import helium314.keyboard.latin.settings.Settings; import helium314.keyboard.latin.settings.Settings;
import helium314.keyboard.latin.settings.SettingsValues; import helium314.keyboard.latin.settings.SettingsValues;
import helium314.keyboard.latin.suggestions.SuggestionStripView;
import helium314.keyboard.latin.utils.AdditionalSubtypeUtils; import helium314.keyboard.latin.utils.AdditionalSubtypeUtils;
import helium314.keyboard.latin.utils.CapsModeUtils; import helium314.keyboard.latin.utils.CapsModeUtils;
import helium314.keyboard.latin.utils.DeviceProtectedUtils;
import helium314.keyboard.latin.utils.LanguageOnSpacebarUtils; import helium314.keyboard.latin.utils.LanguageOnSpacebarUtils;
import helium314.keyboard.latin.utils.Log; import helium314.keyboard.latin.utils.Log;
import helium314.keyboard.latin.utils.RecapitalizeStatus; import helium314.keyboard.latin.utils.RecapitalizeStatus;
@ -59,7 +62,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
private View mEmojiTabStripView; private View mEmojiTabStripView;
private LinearLayout mClipboardStripView; private LinearLayout mClipboardStripView;
private HorizontalScrollView mClipboardStripScrollView; private HorizontalScrollView mClipboardStripScrollView;
private View mSuggestionStripView; private SuggestionStripView mSuggestionStripView;
private ClipboardHistoryView mClipboardHistoryView; private ClipboardHistoryView mClipboardHistoryView;
private TextView mFakeToastView; private TextView mFakeToastView;
private LatinIME mLatinIME; private LatinIME mLatinIME;
@ -325,7 +328,6 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
if (DEBUG_ACTION) { if (DEBUG_ACTION) {
Log.d(TAG, "setEmojiKeyboard"); Log.d(TAG, "setEmojiKeyboard");
} }
final Keyboard keyboard = mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET);
mMainKeyboardFrame.setVisibility(View.VISIBLE); mMainKeyboardFrame.setVisibility(View.VISIBLE);
// The visibility of {@link #mKeyboardView} must be aligned with {@link #MainKeyboardFrame}. // The visibility of {@link #mKeyboardView} must be aligned with {@link #MainKeyboardFrame}.
// @see #getVisibleKeyboardView() and // @see #getVisibleKeyboardView() and
@ -346,7 +348,6 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
if (DEBUG_ACTION) { if (DEBUG_ACTION) {
Log.d(TAG, "setClipboardKeyboard"); Log.d(TAG, "setClipboardKeyboard");
} }
final Keyboard keyboard = mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET);
mMainKeyboardFrame.setVisibility(View.VISIBLE); mMainKeyboardFrame.setVisibility(View.VISIBLE);
// The visibility of {@link #mKeyboardView} must be aligned with {@link #MainKeyboardFrame}. // The visibility of {@link #mKeyboardView} must be aligned with {@link #MainKeyboardFrame}.
// @see #getVisibleKeyboardView() and // @see #getVisibleKeyboardView() and
@ -634,6 +635,11 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
mKeyboardView.closing(); mKeyboardView.closing();
} }
PointerTracker.clearOldViewData(); PointerTracker.clearOldViewData();
final SharedPreferences prefs = DeviceProtectedUtils.getSharedPreferences(displayContext);
if (mSuggestionStripView != null)
prefs.unregisterOnSharedPreferenceChangeListener(mSuggestionStripView);
if (mClipboardHistoryView != null)
prefs.unregisterOnSharedPreferenceChangeListener(mClipboardHistoryView);
updateKeyboardThemeAndContextThemeWrapper(displayContext, KeyboardTheme.getKeyboardTheme(displayContext)); updateKeyboardThemeAndContextThemeWrapper(displayContext, KeyboardTheme.getKeyboardTheme(displayContext));
mCurrentInputView = (InputView)LayoutInflater.from(mThemeContext).inflate(R.layout.input_view, null); mCurrentInputView = (InputView)LayoutInflater.from(mThemeContext).inflate(R.layout.input_view, null);
@ -656,6 +662,8 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
mClipboardStripScrollView = mCurrentInputView.findViewById(R.id.clipboard_strip_scroll_view); mClipboardStripScrollView = mCurrentInputView.findViewById(R.id.clipboard_strip_scroll_view);
mSuggestionStripView = mCurrentInputView.findViewById(R.id.suggestion_strip_view); mSuggestionStripView = mCurrentInputView.findViewById(R.id.suggestion_strip_view);
prefs.registerOnSharedPreferenceChangeListener(mSuggestionStripView);
prefs.registerOnSharedPreferenceChangeListener(mClipboardHistoryView);
PointerTracker.switchTo(mKeyboardView); PointerTracker.switchTo(mKeyboardView);
return mCurrentInputView; return mCurrentInputView;
} }

View file

@ -65,6 +65,7 @@ public class KeyboardView extends View {
private static final float KET_TEXT_SHADOW_RADIUS_DISABLED = -1.0f; private static final float KET_TEXT_SHADOW_RADIUS_DISABLED = -1.0f;
private final Colors mColors; private final Colors mColors;
private float mKeyScaleForText; private float mKeyScaleForText;
protected float mFontSizeMultiplier;
// The maximum key label width in the proportion to the key width. // The maximum key label width in the proportion to the key width.
private static final float MAX_LABEL_RATIO = 0.90f; private static final float MAX_LABEL_RATIO = 0.90f;
@ -189,6 +190,9 @@ public class KeyboardView extends View {
mKeyDrawParams.updateParams(scaledKeyHeight, keyboard.mKeyVisualAttributes); mKeyDrawParams.updateParams(scaledKeyHeight, keyboard.mKeyVisualAttributes);
invalidateAllKeys(); invalidateAllKeys();
requestLayout(); requestLayout();
mFontSizeMultiplier = mKeyboard.mId.isEmojiKeyboard()
? Settings.getInstance().getCurrent().mFontSizeMultiplierEmoji
: Settings.getInstance().getCurrent().mFontSizeMultiplier;
} }
/** /**
@ -384,7 +388,7 @@ public class KeyboardView extends View {
final String label = key.getLabel(); final String label = key.getLabel();
if (label != null) { if (label != null) {
paint.setTypeface(mTypeface == null ? key.selectTypeface(params) : mTypeface); paint.setTypeface(mTypeface == null ? key.selectTypeface(params) : mTypeface);
paint.setTextSize(key.selectTextSize(params)); paint.setTextSize(key.selectTextSize(params) * mFontSizeMultiplier);
final float labelCharHeight = TypefaceUtils.getReferenceCharHeight(paint); final float labelCharHeight = TypefaceUtils.getReferenceCharHeight(paint);
final float labelCharWidth = TypefaceUtils.getReferenceCharWidth(paint); final float labelCharWidth = TypefaceUtils.getReferenceCharWidth(paint);
@ -446,10 +450,10 @@ public class KeyboardView extends View {
// Draw hint label. // Draw hint label.
final String hintLabel = key.getHintLabel(); final String hintLabel = key.getHintLabel();
if (hintLabel != null && mShowsHints) { if (hintLabel != null && mShowsHints) {
paint.setTextSize(key.selectHintTextSize(params)); paint.setTextSize(key.selectHintTextSize(params) * mFontSizeMultiplier); // maybe take sqrt to not have such extreme changes?
paint.setColor(key.selectHintTextColor(params)); paint.setColor(key.selectHintTextColor(params));
// TODO: Should add a way to specify type face for hint letters // TODO: Should add a way to specify type face for hint letters
paint.setTypeface(Typeface.DEFAULT_BOLD); paint.setTypeface(mTypeface == null ? Typeface.DEFAULT_BOLD : mTypeface);
blendAlpha(paint, params.mAnimAlpha); blendAlpha(paint, params.mAnimAlpha);
final float labelCharHeight = TypefaceUtils.getReferenceCharHeight(paint); final float labelCharHeight = TypefaceUtils.getReferenceCharHeight(paint);
final float labelCharWidth = TypefaceUtils.getReferenceCharWidth(paint); final float labelCharWidth = TypefaceUtils.getReferenceCharWidth(paint);
@ -561,7 +565,7 @@ public class KeyboardView extends View {
} else { } else {
paint.setColor(key.selectTextColor(mKeyDrawParams)); paint.setColor(key.selectTextColor(mKeyDrawParams));
paint.setTypeface(key.selectTypeface(mKeyDrawParams)); paint.setTypeface(key.selectTypeface(mKeyDrawParams));
paint.setTextSize(key.selectTextSize(mKeyDrawParams)); paint.setTextSize(key.selectTextSize(mKeyDrawParams) * mFontSizeMultiplier);
} }
return paint; return paint;
} }

View file

@ -164,7 +164,8 @@ public final class MainKeyboardView extends KeyboardView implements DrawingProxy
mBackgroundDimAlphaPaint.setColor(Color.BLACK); mBackgroundDimAlphaPaint.setColor(Color.BLACK);
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)
* Settings.getInstance().getCurrent().mFontSizeMultiplier;
final Colors colors = Settings.getInstance().getCurrent().mColors; final Colors colors = Settings.getInstance().getCurrent().mColors;
mLanguageOnSpacebarTextColor = colors.get(ColorType.SPACE_BAR_TEXT); mLanguageOnSpacebarTextColor = colors.get(ColorType.SPACE_BAR_TEXT);
mLanguageOnSpacebarTextShadowRadius = mainKeyboardViewAttr.getFloat( mLanguageOnSpacebarTextShadowRadius = mainKeyboardViewAttr.getFloat(

View file

@ -4,6 +4,7 @@ package helium314.keyboard.keyboard.clipboard
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.content.SharedPreferences
import android.util.AttributeSet import android.util.AttributeSet
import android.util.TypedValue import android.util.TypedValue
import android.view.View import android.view.View
@ -34,6 +35,7 @@ import helium314.keyboard.latin.utils.createToolbarKey
import helium314.keyboard.latin.utils.getCodeForToolbarKey import helium314.keyboard.latin.utils.getCodeForToolbarKey
import helium314.keyboard.latin.utils.getCodeForToolbarKeyLongClick import helium314.keyboard.latin.utils.getCodeForToolbarKeyLongClick
import helium314.keyboard.latin.utils.getEnabledClipboardToolbarKeys import helium314.keyboard.latin.utils.getEnabledClipboardToolbarKeys
import helium314.keyboard.latin.utils.setToolbarButtonsActivatedStateOnPrefChange
@SuppressLint("CustomViewStyleable") @SuppressLint("CustomViewStyleable")
class ClipboardHistoryView @JvmOverloads constructor( class ClipboardHistoryView @JvmOverloads constructor(
@ -41,7 +43,8 @@ class ClipboardHistoryView @JvmOverloads constructor(
attrs: AttributeSet?, attrs: AttributeSet?,
defStyle: Int = R.attr.clipboardHistoryViewStyle defStyle: Int = R.attr.clipboardHistoryViewStyle
) : LinearLayout(context, attrs, defStyle), View.OnClickListener, ) : LinearLayout(context, attrs, defStyle), View.OnClickListener,
ClipboardHistoryManager.OnHistoryChangeListener, OnKeyEventListener, View.OnLongClickListener { ClipboardHistoryManager.OnHistoryChangeListener, OnKeyEventListener,
View.OnLongClickListener, SharedPreferences.OnSharedPreferenceChangeListener {
private val clipboardLayoutParams = ClipboardLayoutParams(context) private val clipboardLayoutParams = ClipboardLayoutParams(context)
private val pinIconId: Int private val pinIconId: Int
@ -65,10 +68,6 @@ class ClipboardHistoryView @JvmOverloads constructor(
keyBackgroundId = keyboardViewAttr.getResourceId(R.styleable.KeyboardView_keyBackground, 0) keyBackgroundId = keyboardViewAttr.getResourceId(R.styleable.KeyboardView_keyBackground, 0)
keyboardViewAttr.recycle() keyboardViewAttr.recycle()
val keyboardAttr = context.obtainStyledAttributes(attrs, R.styleable.Keyboard, defStyle, R.style.SuggestionStripView) val keyboardAttr = context.obtainStyledAttributes(attrs, R.styleable.Keyboard, defStyle, R.style.SuggestionStripView)
// todo (maybe): setting the correct color only works because the activated state is inverted
// even when state is activated, the not activated color is set
// in suggestionStripView the same thing works correctly, wtf?
// need to properly fix it (and maybe undo the inverted isActivated) when adding a toggle key
getEnabledClipboardToolbarKeys(DeviceProtectedUtils.getSharedPreferences(context)) getEnabledClipboardToolbarKeys(DeviceProtectedUtils.getSharedPreferences(context))
.forEach { toolbarKeys.add(createToolbarKey(context, KeyboardIconsSet.instance, it)) } .forEach { toolbarKeys.add(createToolbarKey(context, KeyboardIconsSet.instance, it)) }
keyboardAttr.recycle() keyboardAttr.recycle()
@ -183,6 +182,9 @@ class ClipboardHistoryView @JvmOverloads constructor(
keyboardAttr.recycle() keyboardAttr.recycle()
setPadding(leftPadding, paddingTop, rightPadding, paddingBottom) setPadding(leftPadding, paddingTop, rightPadding, paddingBottom)
} }
// absurd workaround so Android sets the correct color from stateList (depending on "activated")
toolbarKeys.forEach { it.isEnabled = false; it.isEnabled = true }
} }
fun stopClipboardHistory() { fun stopClipboardHistory() {
@ -247,4 +249,8 @@ class ClipboardHistoryView @JvmOverloads constructor(
clipboardAdapter.notifyItemChanged(to) clipboardAdapter.notifyItemChanged(to)
if (to < from) clipboardRecyclerView.smoothScrollToPosition(to) if (to < from) clipboardRecyclerView.smoothScrollToPosition(to)
} }
override fun onSharedPreferenceChanged(prefs: SharedPreferences?, key: String?) {
setToolbarButtonsActivatedStateOnPrefChange(KeyboardSwitcher.getInstance().clipboardStrip, key)
}
} }

View file

@ -57,8 +57,8 @@ public class KeyPreviewView extends AppCompatTextView {
setCompoundDrawables(null, null, null, null); setCompoundDrawables(null, null, null, null);
setTextColor(drawParams.mPreviewTextColor); setTextColor(drawParams.mPreviewTextColor);
setTextSize(TypedValue.COMPLEX_UNIT_PX, key.selectPreviewTextSize(drawParams)); setTextSize(TypedValue.COMPLEX_UNIT_PX, key.selectPreviewTextSize(drawParams)
// wie hier machen? * Settings.getInstance().getCurrent().mFontSizeMultiplier);
setTypeface(mTypeface == null ? key.selectPreviewTypeface(drawParams) : mTypeface); setTypeface(mTypeface == null ? key.selectPreviewTypeface(drawParams) : mTypeface);
// TODO Should take care of temporaryShiftLabel here. // TODO Should take care of temporaryShiftLabel here.
setTextAndScaleX(key.getPreviewLabel()); setTextAndScaleX(key.getPreviewLabel());

View file

@ -52,8 +52,9 @@ public final class KeyboardCodesSet {
"key_emoji", "key_emoji",
"key_unspecified", "key_unspecified",
"key_clipboard", "key_clipboard",
"key_start_onehanded", "key_toggle_onehanded",
"key_stop_onehanded", "key_start_onehanded", // keep name to avoid breaking custom layouts
"key_stop_onehanded", // keep name to avoid breaking custom layouts
"key_switch_onehanded" "key_switch_onehanded"
}; };
@ -77,8 +78,9 @@ public final class KeyboardCodesSet {
KeyCode.EMOJI, KeyCode.EMOJI,
KeyCode.NOT_SPECIFIED, KeyCode.NOT_SPECIFIED,
KeyCode.CLIPBOARD, KeyCode.CLIPBOARD,
KeyCode.START_ONE_HANDED_MODE, KeyCode.TOGGLE_ONE_HANDED_MODE,
KeyCode.STOP_ONE_HANDED_MODE, KeyCode.TOGGLE_ONE_HANDED_MODE,
KeyCode.TOGGLE_ONE_HANDED_MODE,
KeyCode.SWITCH_ONE_HANDED_MODE KeyCode.SWITCH_ONE_HANDED_MODE
}; };

View file

@ -808,10 +808,8 @@ public final class KeyboardState {
toggleNumpad(false, autoCapsFlags, recapitalizeMode, false, true); toggleNumpad(false, autoCapsFlags, recapitalizeMode, false, true);
} else if (code == KeyCode.SYMBOL) { } else if (code == KeyCode.SYMBOL) {
setSymbolsKeyboard(); setSymbolsKeyboard();
} else if (code == KeyCode.START_ONE_HANDED_MODE) { } else if (code == KeyCode.TOGGLE_ONE_HANDED_MODE) {
setOneHandedModeEnabled(true); setOneHandedModeEnabled(!Settings.getInstance().getCurrent().mOneHandedModeEnabled);
} else if (code == KeyCode.STOP_ONE_HANDED_MODE) {
setOneHandedModeEnabled(false);
} else if (code == KeyCode.SWITCH_ONE_HANDED_MODE) { } else if (code == KeyCode.SWITCH_ONE_HANDED_MODE) {
switchOneHandedMode(); switchOneHandedMode();
} }

View file

@ -43,6 +43,7 @@ class LocaleKeyboardInfos(dataStream: InputStream?, locale: Locale) {
"mns" -> Key.LABEL_FLAGS_FOLLOW_KEY_LETTER_RATIO "mns" -> Key.LABEL_FLAGS_FOLLOW_KEY_LETTER_RATIO
else -> 0 else -> 0
} }
val tlds = getLocaleTlds(locale) // todo: USE IT
init { init {
readStream(dataStream, false, true) readStream(dataStream, false, true)
@ -74,17 +75,25 @@ class LocaleKeyboardInfos(dataStream: InputStream?, locale: Locale) {
"[extra_keys]" -> { mode = READER_MODE_EXTRA_KEYS; return@forEachLine } "[extra_keys]" -> { mode = READER_MODE_EXTRA_KEYS; return@forEachLine }
"[labels]" -> { mode = READER_MODE_LABELS; return@forEachLine } "[labels]" -> { mode = READER_MODE_LABELS; return@forEachLine }
"[number_row]" -> { mode = READER_MODE_NUMBER_ROW; return@forEachLine } "[number_row]" -> { mode = READER_MODE_NUMBER_ROW; return@forEachLine }
"[tlds]" -> { mode = READER_MODE_TLD; return@forEachLine }
} }
when (mode) { when (mode) {
READER_MODE_POPUP_KEYS -> addPopupKeys(line, priority) READER_MODE_POPUP_KEYS -> addPopupKeys(line, priority)
READER_MODE_EXTRA_KEYS -> if (!onlyPopupKeys) addExtraKey(line.split(colonSpaceRegex, 2)) READER_MODE_EXTRA_KEYS -> if (!onlyPopupKeys) addExtraKey(line.split(colonSpaceRegex, 2))
READER_MODE_LABELS -> if (!onlyPopupKeys) addLabel(line.split(colonSpaceRegex, 2)) READER_MODE_LABELS -> if (!onlyPopupKeys) addLabel(line.split(colonSpaceRegex, 2))
READER_MODE_NUMBER_ROW -> localizedNumberKeys = line.splitOnWhitespace() READER_MODE_NUMBER_ROW -> localizedNumberKeys = line.splitOnWhitespace()
READER_MODE_TLD -> line.splitOnWhitespace().forEach { tlds.add(".$it") }
} }
} }
} }
} }
fun addDefaultTlds(locale: Locale) {
if ((locale.language != "en" && euroLocales.matches(locale.language)) || euroCountries.matches(locale.country))
tlds.add(".eu")
tlds.addAll(defaultTlds.splitOnWhitespace())
}
/** Pair(extraKeysLeft, extraKeysRight) */ /** Pair(extraKeysLeft, extraKeysRight) */
fun getTabletExtraKeys(elementId: Int): Pair<List<KeyData>, List<KeyData>> { fun getTabletExtraKeys(elementId: Int): Pair<List<KeyData>, List<KeyData>> {
val flags = Key.LABEL_FLAGS_FONT_DEFAULT val flags = Key.LABEL_FLAGS_FONT_DEFAULT
@ -192,6 +201,7 @@ private fun createLocaleKeyTexts(context: Context, params: KeyboardParams, popup
if (locale == params.mId.locale) return@forEach if (locale == params.mId.locale) return@forEach
lkt.addFile(getStreamForLocale(locale, context), true) lkt.addFile(getStreamForLocale(locale, context), true)
} }
lkt.addDefaultTlds(params.mId.locale)
when (popupKeysSetting) { when (popupKeysSetting) {
POPUP_KEYS_MAIN -> lkt.addFile(context.assets.open("$LOCALE_TEXTS_FOLDER/more_popups_main.txt"), false) POPUP_KEYS_MAIN -> lkt.addFile(context.assets.open("$LOCALE_TEXTS_FOLDER/more_popups_main.txt"), false)
POPUP_KEYS_MORE -> lkt.addFile(context.assets.open("$LOCALE_TEXTS_FOLDER/more_popups_more.txt"), false) POPUP_KEYS_MORE -> lkt.addFile(context.assets.open("$LOCALE_TEXTS_FOLDER/more_popups_more.txt"), false)
@ -212,6 +222,20 @@ private fun getStreamForLocale(locale: Locale, context: Context) =
} }
} }
private fun getLocaleTlds(locale: Locale): LinkedHashSet<String> {
val ccLower = locale.country.lowercase()
val tlds = LinkedHashSet<String>()
if (ccLower.isEmpty() || ccLower == "zz")
return tlds
specialCountryTlds.forEach {
if (ccLower != it.first) return@forEach
tlds.addAll(it.second.splitOnWhitespace())
return tlds
}
tlds.add(".$ccLower")
return tlds
}
fun clearCache() = localeKeyboardInfosCache.clear() fun clearCache() = localeKeyboardInfosCache.clear()
// cache the texts, so they don't need to be read over and over // cache the texts, so they don't need to be read over and over
@ -222,6 +246,7 @@ private const val READER_MODE_POPUP_KEYS = 1
private const val READER_MODE_EXTRA_KEYS = 2 private const val READER_MODE_EXTRA_KEYS = 2
private const val READER_MODE_LABELS = 3 private const val READER_MODE_LABELS = 3
private const val READER_MODE_NUMBER_ROW = 4 private const val READER_MODE_NUMBER_ROW = 4
private const val READER_MODE_TLD = 5
// probably could be improved and extended, currently this is what's done in key_styles_currency.xml // probably could be improved and extended, currently this is what's done in key_styles_currency.xml
private fun getCurrencyKey(locale: Locale): Pair<String, List<String>> { private fun getCurrencyKey(locale: Locale): Pair<String, List<String>> {
@ -294,3 +319,16 @@ const val POPUP_KEYS_MAIN = 3
const val POPUP_KEYS_NORMAL = 0 const val POPUP_KEYS_NORMAL = 0
private const val LOCALE_TEXTS_FOLDER = "locale_key_texts" private const val LOCALE_TEXTS_FOLDER = "locale_key_texts"
// either tld is not simply lowercase ISO 3166-1 code, or there are multiple according to some list
private val specialCountryTlds = listOf(
"bd" to ".bd .com.bd",
"bq" to ".bq .an .nl",
"bl" to ".bl .gp .fr",
"sx" to ".sx .an",
"gb" to ".uk .co.uk",
"eh" to ".eh .ma",
"mf" to ".mf .gp .fr",
"tl" to ".tl .tp",
)
private const val defaultTlds = ".com .gov .edu .org .net"

View file

@ -125,8 +125,8 @@ object KeyCode {
// heliboard only codes // heliboard only codes
const val SYMBOL_ALPHA = -10001 const val SYMBOL_ALPHA = -10001
const val START_ONE_HANDED_MODE = -10002 const val TOGGLE_ONE_HANDED_MODE = -10002
const val STOP_ONE_HANDED_MODE = -10003 const val TOGGLE_ONE_HANDED_MODE_2 = -10003 // does the same as TOGGLE_ONE_HANDED_MODE (used to be start & stop)
const val SWITCH_ONE_HANDED_MODE = -10004 const val SWITCH_ONE_HANDED_MODE = -10004
const val SHIFT_ENTER = -10005 const val SHIFT_ENTER = -10005
const val ACTION_NEXT = -10006 const val ACTION_NEXT = -10006
@ -179,7 +179,7 @@ object KeyCode {
FN, CLIPBOARD_CLEAR_HISTORY, NUMPAD, FN, CLIPBOARD_CLEAR_HISTORY, NUMPAD,
// heliboard only // heliboard only
SYMBOL_ALPHA, START_ONE_HANDED_MODE, STOP_ONE_HANDED_MODE, SWITCH_ONE_HANDED_MODE, SHIFT_ENTER, SYMBOL_ALPHA, TOGGLE_ONE_HANDED_MODE, SWITCH_ONE_HANDED_MODE, SHIFT_ENTER,
ACTION_NEXT, ACTION_PREVIOUS, NOT_SPECIFIED, CLIPBOARD_COPY_ALL, WORD_LEFT, WORD_RIGHT, PAGE_UP, ACTION_NEXT, ACTION_PREVIOUS, NOT_SPECIFIED, CLIPBOARD_COPY_ALL, WORD_LEFT, WORD_RIGHT, PAGE_UP,
PAGE_DOWN, META, TAB, ESCAPE, INSERT, SLEEP, MEDIA_PLAY, MEDIA_PAUSE, MEDIA_PLAY_PAUSE, MEDIA_NEXT, PAGE_DOWN, META, TAB, ESCAPE, INSERT, SLEEP, MEDIA_PLAY, MEDIA_PAUSE, MEDIA_PLAY_PAUSE, MEDIA_NEXT,
MEDIA_PREVIOUS, VOL_UP, VOL_DOWN, MUTE, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, BACK MEDIA_PREVIOUS, VOL_UP, VOL_DOWN, MUTE, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, BACK
@ -189,6 +189,7 @@ object KeyCode {
IME_UI_MODE_TEXT -> ALPHA IME_UI_MODE_TEXT -> ALPHA
VIEW_PHONE -> ALPHA // phone keyboard is treated like alphabet, just with different layout VIEW_PHONE -> ALPHA // phone keyboard is treated like alphabet, just with different layout
VIEW_PHONE2 -> SYMBOL VIEW_PHONE2 -> SYMBOL
TOGGLE_ONE_HANDED_MODE_2 -> TOGGLE_ONE_HANDED_MODE
else -> throw IllegalStateException("key code $this not yet supported") else -> throw IllegalStateException("key code $this not yet supported")
} }

View file

@ -127,7 +127,7 @@ sealed interface KeyData : AbstractKeyData {
if (!params.mId.mLanguageSwitchKeyEnabled && !params.mId.isNumberLayout && RichInputMethodManager.canSwitchLanguage()) if (!params.mId.mLanguageSwitchKeyEnabled && !params.mId.isNumberLayout && RichInputMethodManager.canSwitchLanguage())
keys.add("!icon/language_switch_key|!code/key_language_switch") keys.add("!icon/language_switch_key|!code/key_language_switch")
if (!params.mId.mOneHandedModeEnabled) if (!params.mId.mOneHandedModeEnabled)
keys.add("!icon/start_onehanded_mode_key|!code/key_start_onehanded") keys.add("!icon/start_onehanded_mode_key|!code/key_toggle_onehanded")
if (!params.mId.mDeviceLocked) if (!params.mId.mDeviceLocked)
keys.add("!icon/settings_key|!code/key_settings") keys.add("!icon/settings_key|!code/key_settings")
return keys return keys

View file

@ -111,19 +111,17 @@ fun checkVersionUpgrade(context: Context) {
if (additionalSubtypeString.contains(".")) { // means there are custom layouts if (additionalSubtypeString.contains(".")) { // means there are custom layouts
val subtypeStrings = additionalSubtypeString.split(";") val subtypeStrings = additionalSubtypeString.split(";")
val newSubtypeStrings = subtypeStrings.mapNotNull { val newSubtypeStrings = subtypeStrings.mapNotNull {
if ("." !in it) // not a custom subtype, nothing to do
return@mapNotNull it
val split = it.split(":").toMutableList() val split = it.split(":").toMutableList()
Log.i("test", "0: $it")
if (split.size < 2) return@mapNotNull null // should never happen if (split.size < 2) return@mapNotNull null // should never happen
val oldName = split[1] val oldName = split[1]
val newName = oldName.substringBeforeLast(".") + "." val newName = oldName.substringBeforeLast(".") + "."
if (oldName == newName) return@mapNotNull split.joinToString(":") // should never happen if (oldName == newName) return@mapNotNull split.joinToString(":") // should never happen
val oldFile = getCustomLayoutFile(oldName, context) val oldFile = getCustomLayoutFile(oldName, context)
val newFile = getCustomLayoutFile(newName, context) val newFile = getCustomLayoutFile(newName, context)
Log.i("test", "1")
if (!oldFile.exists()) return@mapNotNull null // should never happen if (!oldFile.exists()) return@mapNotNull null // should never happen
Log.i("test", "2")
if (newFile.exists()) newFile.delete() // should never happen if (newFile.exists()) newFile.delete() // should never happen
Log.i("test", "3")
oldFile.renameTo(newFile) oldFile.renameTo(newFile)
val enabledSubtypes = prefs.getString(Settings.PREF_ENABLED_SUBTYPES, "")!! val enabledSubtypes = prefs.getString(Settings.PREF_ENABLED_SUBTYPES, "")!!
if (enabledSubtypes.contains(oldName)) if (enabledSubtypes.contains(oldName))

View file

@ -84,8 +84,7 @@ class KeyboardWrapperView @JvmOverloads constructor(
if (newScale == oldScale) return@setOnTouchListener true if (newScale == oldScale) return@setOnTouchListener true
Settings.getInstance().writeOneHandedModeScale(newScale) Settings.getInstance().writeOneHandedModeScale(newScale)
oneHandedModeEnabled = false // intentionally putting wrong value, so KeyboardSwitcher.setOneHandedModeEnabled does actually reload oneHandedModeEnabled = false // intentionally putting wrong value, so KeyboardSwitcher.setOneHandedModeEnabled does actually reload
keyboardActionListener?.onCodeInput(KeyCode.START_ONE_HANDED_MODE, KeyboardSwitcher.getInstance().setOneHandedModeEnabled(true)
Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, false)
} }
else -> x = 0f else -> x = 0f
} }
@ -119,7 +118,7 @@ class KeyboardWrapperView @JvmOverloads constructor(
override fun onClick(view: View) { override fun onClick(view: View) {
if (view === stopOneHandedModeBtn) { if (view === stopOneHandedModeBtn) {
keyboardActionListener?.onCodeInput(KeyCode.STOP_ONE_HANDED_MODE, keyboardActionListener?.onCodeInput(KeyCode.TOGGLE_ONE_HANDED_MODE,
Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE,
false /* isKeyRepeat */) false /* isKeyRepeat */)
} else if (view === switchOneHandedModeBtn) { } else if (view === switchOneHandedModeBtn) {

View file

@ -150,7 +150,11 @@ class DynamicColors(context: Context, override val themeStyle: String, override
private val spaceBarStateList: ColorStateList private val spaceBarStateList: ColorStateList
private val adjustedBackgroundStateList: ColorStateList private val adjustedBackgroundStateList: ColorStateList
private val stripBackgroundList: ColorStateList private val stripBackgroundList: ColorStateList
private val toolbarKeyStateList = activatedStateList(keyText, darken(darken(keyText))) private val toolbarKeyStateList = activatedStateList(
keyText,
if (isBrightColor(keyText)) darken(darken(keyText))
else brighten(brighten(keyText))
)
/** darkened variant of [accent] because the accent color is always light for dynamic colors */ /** darkened variant of [accent] because the accent color is always light for dynamic colors */
private val adjustedAccent: Int = darken(accent) private val adjustedAccent: Int = darken(accent)
@ -195,12 +199,12 @@ class DynamicColors(context: Context, override val themeStyle: String, override
} }
adjustedBackgroundStateList = adjustedBackgroundStateList =
if (themeStyle == STYLE_HOLO) { if (themeStyle == STYLE_HOLO) {
stateList(accent, adjustedBackground) pressedStateList(accent, adjustedBackground)
} else if (isNight) { } else if (isNight) {
if (hasKeyBorders) stateList(doubleAdjustedAccent, keyBackground) if (hasKeyBorders) pressedStateList(doubleAdjustedAccent, keyBackground)
else stateList(adjustedAccent, adjustedKeyBackground) else pressedStateList(adjustedAccent, adjustedKeyBackground)
} else { } else {
stateList(accent, Color.WHITE) pressedStateList(accent, Color.WHITE)
} }
val stripBackground = if (keyboardBackground == null && !hasKeyBorders) { val stripBackground = if (keyboardBackground == null && !hasKeyBorders) {
@ -210,7 +214,7 @@ class DynamicColors(context: Context, override val themeStyle: String, override
} }
val pressedStripElementBackground = if (keyboardBackground == null) adjustedBackground val pressedStripElementBackground = if (keyboardBackground == null) adjustedBackground
else if (isDarkColor(background)) 0x22ffffff else 0x11000000 else if (isDarkColor(background)) 0x22ffffff else 0x11000000
stripBackgroundList = stateList(pressedStripElementBackground, stripBackground) stripBackgroundList = pressedStateList(pressedStripElementBackground, stripBackground)
adjustedBackgroundFilter = adjustedBackgroundFilter =
if (themeStyle == STYLE_HOLO) colorFilter(adjustedBackground) if (themeStyle == STYLE_HOLO) colorFilter(adjustedBackground)
@ -218,47 +222,47 @@ class DynamicColors(context: Context, override val themeStyle: String, override
if (hasKeyBorders) { if (hasKeyBorders) {
backgroundStateList = backgroundStateList =
if (!isNight) stateList(adjustedFunctionalKey, background) if (!isNight) pressedStateList(adjustedFunctionalKey, background)
else stateList(adjustedKeyBackground, background) else pressedStateList(adjustedKeyBackground, background)
keyStateList = keyStateList =
if (!isNight) stateList(adjustedBackground, keyBackground) if (!isNight) pressedStateList(adjustedBackground, keyBackground)
else stateList(adjustedKeyBackground, keyBackground) else pressedStateList(adjustedKeyBackground, keyBackground)
functionalKeyStateList = functionalKeyStateList =
if (!isNight) stateList(doubleAdjustedFunctionalKey, functionalKey) if (!isNight) pressedStateList(doubleAdjustedFunctionalKey, functionalKey)
else stateList(functionalKey, doubleAdjustedKeyBackground) else pressedStateList(functionalKey, doubleAdjustedKeyBackground)
actionKeyStateList = actionKeyStateList =
if (!isNight) stateList(gesture, accent) if (!isNight) pressedStateList(gesture, accent)
else stateList(doubleAdjustedAccent, accent) else pressedStateList(doubleAdjustedAccent, accent)
spaceBarStateList = spaceBarStateList =
if (themeStyle == STYLE_HOLO) stateList(spaceBar, spaceBar) if (themeStyle == STYLE_HOLO) pressedStateList(spaceBar, spaceBar)
else keyStateList else keyStateList
} else { } else {
// need to set color to background if key borders are disabled, or there will be ugly keys // need to set color to background if key borders are disabled, or there will be ugly keys
backgroundStateList = backgroundStateList =
if (!isNight) stateList(adjustedFunctionalKey, background) if (!isNight) pressedStateList(adjustedFunctionalKey, background)
else stateList(adjustedKeyBackground, background) else pressedStateList(adjustedKeyBackground, background)
keyStateList = keyStateList =
if (!isNight) stateList(adjustedFunctionalKey, Color.TRANSPARENT) if (!isNight) pressedStateList(adjustedFunctionalKey, Color.TRANSPARENT)
else stateList(functionalKey, Color.TRANSPARENT) else pressedStateList(functionalKey, Color.TRANSPARENT)
functionalKeyStateList = functionalKeyStateList =
if (themeStyle == STYLE_HOLO) stateList(functionalKey, Color.TRANSPARENT) if (themeStyle == STYLE_HOLO) pressedStateList(functionalKey, Color.TRANSPARENT)
else keyStateList else keyStateList
actionKeyStateList = actionKeyStateList =
if (themeStyle == STYLE_HOLO) stateList(accent, Color.TRANSPARENT) if (themeStyle == STYLE_HOLO) pressedStateList(accent, Color.TRANSPARENT)
else if (!isNight) stateList(gesture, accent) else if (!isNight) pressedStateList(gesture, accent)
else stateList(doubleAdjustedAccent, accent) else pressedStateList(doubleAdjustedAccent, accent)
spaceBarStateList = spaceBarStateList =
if (!isNight) stateList(gesture, adjustedFunctionalKey) if (!isNight) pressedStateList(gesture, adjustedFunctionalKey)
else stateList(adjustedKeyBackground, spaceBar) else pressedStateList(adjustedKeyBackground, spaceBar)
} }
keyTextFilter = colorFilter(keyText) keyTextFilter = colorFilter(keyText)
@ -398,7 +402,11 @@ class DefaultColors (
private val spaceBarStateList: ColorStateList private val spaceBarStateList: ColorStateList
private val adjustedBackgroundStateList: ColorStateList private val adjustedBackgroundStateList: ColorStateList
private val stripBackgroundList: ColorStateList private val stripBackgroundList: ColorStateList
private val toolbarKeyStateList = activatedStateList(suggestionText, darken(darken(suggestionText))) private val toolbarKeyStateList = activatedStateList(
suggestionText,
if (isBrightColor(suggestionText)) darken(darken(suggestionText))
else brighten(brighten(suggestionText))
)
private var backgroundSetupDone = false private var backgroundSetupDone = false
init { init {
@ -409,7 +417,7 @@ class DefaultColors (
adjustedBackground = darken(background) adjustedBackground = darken(background)
doubleAdjustedBackground = darken(adjustedBackground) doubleAdjustedBackground = darken(adjustedBackground)
} }
adjustedBackgroundStateList = stateList(doubleAdjustedBackground, adjustedBackground) adjustedBackgroundStateList = pressedStateList(doubleAdjustedBackground, adjustedBackground)
val stripBackground: Int val stripBackground: Int
val pressedStripElementBackground: Int val pressedStripElementBackground: Int
@ -424,7 +432,7 @@ class DefaultColors (
stripBackground = adjustedBackground stripBackground = adjustedBackground
pressedStripElementBackground = doubleAdjustedBackground pressedStripElementBackground = doubleAdjustedBackground
} }
stripBackgroundList = stateList(pressedStripElementBackground, stripBackground) stripBackgroundList = pressedStateList(pressedStripElementBackground, stripBackground)
if (themeStyle == STYLE_HOLO && keyboardBackground == null) { if (themeStyle == STYLE_HOLO && keyboardBackground == null) {
val darkerBackground = adjustLuminosityAndKeepAlpha(background, -0.2f) val darkerBackground = adjustLuminosityAndKeepAlpha(background, -0.2f)
@ -437,22 +445,22 @@ class DefaultColors (
adjustedBackgroundFilter = colorFilter(adjustedBackground) adjustedBackgroundFilter = colorFilter(adjustedBackground)
if (hasKeyBorders) { if (hasKeyBorders) {
backgroundStateList = stateList(brightenOrDarken(background, true), background) backgroundStateList = pressedStateList(brightenOrDarken(background, true), background)
keyStateList = if (themeStyle == STYLE_HOLO) stateList(keyBackground, keyBackground) keyStateList = if (themeStyle == STYLE_HOLO) pressedStateList(keyBackground, keyBackground)
else stateList(brightenOrDarken(keyBackground, true), keyBackground) else pressedStateList(brightenOrDarken(keyBackground, true), keyBackground)
functionalKeyStateList = stateList(brightenOrDarken(functionalKey, true), functionalKey) functionalKeyStateList = pressedStateList(brightenOrDarken(functionalKey, true), functionalKey)
actionKeyStateList = if (themeStyle == STYLE_HOLO) functionalKeyStateList actionKeyStateList = if (themeStyle == STYLE_HOLO) functionalKeyStateList
else stateList(brightenOrDarken(accent, true), accent) else pressedStateList(brightenOrDarken(accent, true), accent)
spaceBarStateList = if (themeStyle == STYLE_HOLO) stateList(spaceBar, spaceBar) spaceBarStateList = if (themeStyle == STYLE_HOLO) pressedStateList(spaceBar, spaceBar)
else stateList(brightenOrDarken(spaceBar, true), spaceBar) else pressedStateList(brightenOrDarken(spaceBar, true), spaceBar)
} else { } else {
// need to set color to background if key borders are disabled, or there will be ugly keys // need to set color to background if key borders are disabled, or there will be ugly keys
backgroundStateList = stateList(brightenOrDarken(background, true), background) backgroundStateList = pressedStateList(brightenOrDarken(background, true), background)
keyStateList = stateList(keyBackground, Color.TRANSPARENT) keyStateList = pressedStateList(keyBackground, Color.TRANSPARENT)
functionalKeyStateList = keyStateList functionalKeyStateList = keyStateList
actionKeyStateList = if (themeStyle == STYLE_HOLO) functionalKeyStateList actionKeyStateList = if (themeStyle == STYLE_HOLO) functionalKeyStateList
else stateList(brightenOrDarken(accent, true), accent) else pressedStateList(brightenOrDarken(accent, true), accent)
spaceBarStateList = stateList(brightenOrDarken(spaceBar, true), spaceBar) spaceBarStateList = pressedStateList(brightenOrDarken(spaceBar, true), spaceBar)
} }
keyTextFilter = colorFilter(keyText) keyTextFilter = colorFilter(keyText)
actionKeyIconColorFilter = when { actionKeyIconColorFilter = when {
@ -556,7 +564,7 @@ class AllColors(private val colorMap: EnumMap<ColorType, Int>, override val them
override fun get(color: ColorType): Int = colorMap[color] ?: color.default() override fun get(color: ColorType): Int = colorMap[color] ?: color.default()
override fun setColor(drawable: Drawable, color: ColorType) { override fun setColor(drawable: Drawable, color: ColorType) {
val colorStateList = stateListMap.getOrPut(color) { stateList(brightenOrDarken(get(color), true), get(color)) } val colorStateList = stateListMap.getOrPut(color) { pressedStateList(brightenOrDarken(get(color), true), get(color)) }
DrawableCompat.setTintMode(drawable, PorterDuff.Mode.MULTIPLY) DrawableCompat.setTintMode(drawable, PorterDuff.Mode.MULTIPLY)
DrawableCompat.setTintList(drawable, colorStateList) DrawableCompat.setTintList(drawable, colorStateList)
} }
@ -621,14 +629,14 @@ private fun colorFilter(color: Int, mode: BlendModeCompat = BlendModeCompat.MODU
return BlendModeColorFilterCompat.createBlendModeColorFilterCompat(color, mode)!! return BlendModeColorFilterCompat.createBlendModeColorFilterCompat(color, mode)!!
} }
private fun stateList(pressed: Int, normal: Int): ColorStateList { private fun pressedStateList(pressed: Int, normal: Int): ColorStateList {
val states = arrayOf(intArrayOf(android.R.attr.state_pressed), intArrayOf(-android.R.attr.state_pressed)) val states = arrayOf(intArrayOf(android.R.attr.state_pressed), intArrayOf(-android.R.attr.state_pressed))
return ColorStateList(states, intArrayOf(pressed, normal)) return ColorStateList(states, intArrayOf(pressed, normal))
} }
private fun activatedStateList(normal: Int, activated: Int): ColorStateList { private fun activatedStateList(activated: Int, normal: Int): ColorStateList {
val states = arrayOf(intArrayOf(-android.R.attr.state_activated), intArrayOf(android.R.attr.state_activated)) val states = arrayOf(intArrayOf(android.R.attr.state_activated), intArrayOf(-android.R.attr.state_activated))
return ColorStateList(states, intArrayOf(normal, activated)) return ColorStateList(states, intArrayOf(activated, normal))
} }
enum class ColorType { enum class ColorType {

View file

@ -224,8 +224,7 @@ public final class Constants {
case CODE_TAB: return "tab"; case CODE_TAB: return "tab";
case CODE_ENTER: return "enter"; case CODE_ENTER: return "enter";
case CODE_SPACE: return "space"; case CODE_SPACE: return "space";
case KeyCode.START_ONE_HANDED_MODE: return "startOneHandedMode"; case KeyCode.TOGGLE_ONE_HANDED_MODE: return "toggleOneHandedMode";
case KeyCode.STOP_ONE_HANDED_MODE: return "stopOneHandedMode";
case KeyCode.SWITCH_ONE_HANDED_MODE: return "switchOneHandedMode"; case KeyCode.SWITCH_ONE_HANDED_MODE: return "switchOneHandedMode";
case KeyCode.NUMPAD: return "numpad"; case KeyCode.NUMPAD: return "numpad";
default: default:

View file

@ -779,7 +779,7 @@ public final class InputLogic {
// We need to switch to the shortcut IME. This is handled by LatinIME since the // We need to switch to the shortcut IME. This is handled by LatinIME since the
// input logic has no business with IME switching. // input logic has no business with IME switching.
case KeyCode.CAPS_LOCK, KeyCode.SYMBOL_ALPHA, KeyCode.ALPHA, KeyCode.SYMBOL, KeyCode.NUMPAD, KeyCode.EMOJI, case KeyCode.CAPS_LOCK, KeyCode.SYMBOL_ALPHA, KeyCode.ALPHA, KeyCode.SYMBOL, KeyCode.NUMPAD, KeyCode.EMOJI,
KeyCode.START_ONE_HANDED_MODE, KeyCode.STOP_ONE_HANDED_MODE, KeyCode.SWITCH_ONE_HANDED_MODE, KeyCode.TOGGLE_ONE_HANDED_MODE, KeyCode.SWITCH_ONE_HANDED_MODE,
KeyCode.CTRL, KeyCode.ALT, KeyCode.FN, KeyCode.META: KeyCode.CTRL, KeyCode.ALT, KeyCode.FN, KeyCode.META:
break; break;
default: default:

View file

@ -105,6 +105,9 @@ class AppearanceSettingsFragment : SubScreenFragment() {
setupScalePrefs(Settings.PREF_KEYBOARD_HEIGHT_SCALE, SettingsValues.DEFAULT_SIZE_SCALE) setupScalePrefs(Settings.PREF_KEYBOARD_HEIGHT_SCALE, SettingsValues.DEFAULT_SIZE_SCALE)
setupScalePrefs(Settings.PREF_BOTTOM_PADDING_SCALE, SettingsValues.DEFAULT_SIZE_SCALE) setupScalePrefs(Settings.PREF_BOTTOM_PADDING_SCALE, SettingsValues.DEFAULT_SIZE_SCALE)
setupScalePrefs(Settings.PREF_BOTTOM_PADDING_SCALE_LANDSCAPE, 0f)
setupScalePrefs(Settings.PREF_FONT_SCALE, SettingsValues.DEFAULT_SIZE_SCALE)
setupScalePrefs(Settings.PREF_EMOJI_FONT_SCALE, SettingsValues.DEFAULT_SIZE_SCALE)
setupScalePrefs(Settings.PREF_SIDE_PADDING_SCALE, 0f) setupScalePrefs(Settings.PREF_SIDE_PADDING_SCALE, 0f)
setupScalePrefs(Settings.PREF_SIDE_PADDING_SCALE_LANDSCAPE, 0f) setupScalePrefs(Settings.PREF_SIDE_PADDING_SCALE_LANDSCAPE, 0f)
if (splitScalePref != null) { if (splitScalePref != null) {

View file

@ -112,8 +112,11 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
public static final String PREF_SPLIT_SPACER_SCALE = "split_spacer_scale"; public static final String PREF_SPLIT_SPACER_SCALE = "split_spacer_scale";
public static final String PREF_KEYBOARD_HEIGHT_SCALE = "keyboard_height_scale"; public static final String PREF_KEYBOARD_HEIGHT_SCALE = "keyboard_height_scale";
public static final String PREF_BOTTOM_PADDING_SCALE = "bottom_padding_scale"; public static final String PREF_BOTTOM_PADDING_SCALE = "bottom_padding_scale";
public static final String PREF_BOTTOM_PADDING_SCALE_LANDSCAPE = "bottom_padding_scale_landscape";
public static final String PREF_SIDE_PADDING_SCALE = "side_padding_scale"; public static final String PREF_SIDE_PADDING_SCALE = "side_padding_scale";
public static final String PREF_SIDE_PADDING_SCALE_LANDSCAPE = "side_padding_scale_landscape"; public static final String PREF_SIDE_PADDING_SCALE_LANDSCAPE = "side_padding_scale_landscape";
public static final String PREF_FONT_SCALE = "font_scale";
public static final String PREF_EMOJI_FONT_SCALE = "emoji_font_scale";
public static final String PREF_SPACE_HORIZONTAL_SWIPE = "horizontal_space_swipe"; public static final String PREF_SPACE_HORIZONTAL_SWIPE = "horizontal_space_swipe";
public static final String PREF_SPACE_VERTICAL_SWIPE = "vertical_space_swipe"; public static final String PREF_SPACE_VERTICAL_SWIPE = "vertical_space_swipe";
public static final String PREF_DELETE_SWIPE = "delete_swipe"; public static final String PREF_DELETE_SWIPE = "delete_swipe";
@ -501,6 +504,12 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
(getCurrent().mDisplayOrientation == Configuration.ORIENTATION_PORTRAIT), gravity).apply(); (getCurrent().mDisplayOrientation == Configuration.ORIENTATION_PORTRAIT), gravity).apply();
} }
public static float readBottomPaddingScale(final SharedPreferences prefs, final boolean landscape) {
if (landscape)
return prefs.getFloat(PREF_BOTTOM_PADDING_SCALE_LANDSCAPE, 0f);
return prefs.getFloat(PREF_BOTTOM_PADDING_SCALE, SettingsValues.DEFAULT_SIZE_SCALE);
}
public static float readSidePaddingScale(final SharedPreferences prefs, final boolean landscape) { public static float readSidePaddingScale(final SharedPreferences prefs, final boolean landscape) {
if (landscape) if (landscape)
return prefs.getFloat(PREF_SIDE_PADDING_SCALE_LANDSCAPE, 0f); return prefs.getFloat(PREF_SIDE_PADDING_SCALE_LANDSCAPE, 0f);

View file

@ -121,6 +121,8 @@ public class SettingsValues {
public final boolean mAlphaAfterSymbolAndSpace; public final boolean mAlphaAfterSymbolAndSpace;
public final boolean mRemoveRedundantPopups; public final boolean mRemoveRedundantPopups;
public final String mSpaceBarText; public final String mSpaceBarText;
public final float mFontSizeMultiplier;
public final float mFontSizeMultiplierEmoji;
// From the input box // From the input box
@NonNull @NonNull
@ -263,7 +265,7 @@ public class SettingsValues {
prefs.getBoolean(Settings.PREF_GESTURE_SPACE_AWARE, false) prefs.getBoolean(Settings.PREF_GESTURE_SPACE_AWARE, false)
); );
mSpacingAndPunctuations = new SpacingAndPunctuations(res, mUrlDetectionEnabled); mSpacingAndPunctuations = new SpacingAndPunctuations(res, mUrlDetectionEnabled);
mBottomPaddingScale = prefs.getFloat(Settings.PREF_BOTTOM_PADDING_SCALE, DEFAULT_SIZE_SCALE); mBottomPaddingScale = Settings.readBottomPaddingScale(prefs, mDisplayOrientation == Configuration.ORIENTATION_LANDSCAPE);
mSidePaddingScale = Settings.readSidePaddingScale(prefs, mDisplayOrientation == Configuration.ORIENTATION_LANDSCAPE); mSidePaddingScale = Settings.readSidePaddingScale(prefs, mDisplayOrientation == Configuration.ORIENTATION_LANDSCAPE);
mLongPressSymbolsForNumpad = prefs.getBoolean(Settings.PREFS_LONG_PRESS_SYMBOLS_FOR_NUMPAD, false); mLongPressSymbolsForNumpad = prefs.getBoolean(Settings.PREFS_LONG_PRESS_SYMBOLS_FOR_NUMPAD, false);
mAutoShowToolbar = prefs.getBoolean(Settings.PREF_AUTO_SHOW_TOOLBAR, false); mAutoShowToolbar = prefs.getBoolean(Settings.PREF_AUTO_SHOW_TOOLBAR, false);
@ -275,6 +277,8 @@ public class SettingsValues {
mRemoveRedundantPopups = prefs.getBoolean(Settings.PREF_REMOVE_REDUNDANT_POPUPS, false); mRemoveRedundantPopups = prefs.getBoolean(Settings.PREF_REMOVE_REDUNDANT_POPUPS, false);
mSpaceBarText = prefs.getString(Settings.PREF_SPACE_BAR_TEXT, ""); mSpaceBarText = prefs.getString(Settings.PREF_SPACE_BAR_TEXT, "");
mEmojiMaxSdk = prefs.getInt(Settings.PREF_EMOJI_MAX_SDK, Build.VERSION.SDK_INT); mEmojiMaxSdk = prefs.getInt(Settings.PREF_EMOJI_MAX_SDK, Build.VERSION.SDK_INT);
mFontSizeMultiplier = prefs.getFloat(Settings.PREF_FONT_SCALE, DEFAULT_SIZE_SCALE);
mFontSizeMultiplierEmoji = prefs.getFloat(Settings.PREF_EMOJI_FONT_SCALE, DEFAULT_SIZE_SCALE);
} }
public boolean isApplicationSpecifiedCompletionsOn() { public boolean isApplicationSpecifiedCompletionsOn() {

View file

@ -70,7 +70,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
public final class SuggestionStripView extends RelativeLayout implements OnClickListener, public final class SuggestionStripView extends RelativeLayout implements OnClickListener,
OnLongClickListener { OnLongClickListener, SharedPreferences.OnSharedPreferenceChangeListener {
public interface Listener { public interface Listener {
void pickSuggestionManually(SuggestedWordInfo word); void pickSuggestionManually(SuggestedWordInfo word);
void onCodeInput(int primaryCode, int x, int y, boolean isKeyRepeat); void onCodeInput(int primaryCode, int x, int y, boolean isKeyRepeat);
@ -231,6 +231,12 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
colors.setBackground(this, ColorType.STRIP_BACKGROUND); colors.setBackground(this, ColorType.STRIP_BACKGROUND);
} }
@Override
public void onSharedPreferenceChanged(SharedPreferences prefs, @Nullable String key) {
ToolbarUtilsKt.setToolbarButtonsActivatedStateOnPrefChange(mPinnedKeys, key);
ToolbarUtilsKt.setToolbarButtonsActivatedStateOnPrefChange(mToolbar, key);
}
/** /**
* A connection back to the input method. * A connection back to the input method.
*/ */
@ -647,11 +653,8 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
if (code != KeyCode.UNSPECIFIED) { if (code != KeyCode.UNSPECIFIED) {
Log.d(TAG, "click toolbar key "+tag); Log.d(TAG, "click toolbar key "+tag);
mListener.onCodeInput(code, Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE, false); mListener.onCodeInput(code, Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE, false);
if (tag == ToolbarKey.INCOGNITO || tag == ToolbarKey.AUTOCORRECT || tag == ToolbarKey.ONE_HANDED) { if (tag == ToolbarKey.INCOGNITO)
if (tag == ToolbarKey.INCOGNITO) updateKeys(); // update expand key icon
updateKeys(); // update icon
view.setActivated(!view.isActivated());
}
return; return;
} }
} }

View file

@ -6,6 +6,7 @@ import android.content.Context
import android.content.DialogInterface import android.content.DialogInterface
import android.content.SharedPreferences import android.content.SharedPreferences
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.EditText import android.widget.EditText
import android.widget.ImageButton import android.widget.ImageButton
import android.widget.ImageView import android.widget.ImageView
@ -16,6 +17,7 @@ import androidx.core.content.ContextCompat
import androidx.core.content.edit import androidx.core.content.edit
import androidx.core.graphics.BlendModeColorFilterCompat import androidx.core.graphics.BlendModeColorFilterCompat
import androidx.core.graphics.BlendModeCompat import androidx.core.graphics.BlendModeCompat
import androidx.core.view.forEach
import androidx.core.view.isGone import androidx.core.view.isGone
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.widget.doAfterTextChanged import androidx.core.widget.doAfterTextChanged
@ -26,7 +28,9 @@ import helium314.keyboard.latin.R
import helium314.keyboard.latin.databinding.ReorderDialogItemBinding import helium314.keyboard.latin.databinding.ReorderDialogItemBinding
import helium314.keyboard.latin.settings.Settings import helium314.keyboard.latin.settings.Settings
import helium314.keyboard.latin.utils.ToolbarKey.* import helium314.keyboard.latin.utils.ToolbarKey.*
import kotlinx.serialization.encodeToString import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import java.util.EnumMap import java.util.EnumMap
import java.util.Locale import java.util.Locale
@ -38,14 +42,31 @@ fun createToolbarKey(context: Context, iconsSet: KeyboardIconsSet, key: ToolbarK
val contentDescriptionId = context.resources.getIdentifier(key.name.lowercase(), "string", context.packageName) val contentDescriptionId = context.resources.getIdentifier(key.name.lowercase(), "string", context.packageName)
if (contentDescriptionId != 0) if (contentDescriptionId != 0)
button.contentDescription = context.getString(contentDescriptionId) button.contentDescription = context.getString(contentDescriptionId)
button.isActivated = !when (key) { setToolbarButtonActivatedState(button)
INCOGNITO -> Settings.readAlwaysIncognitoMode(DeviceProtectedUtils.getSharedPreferences(context)) button.setImageDrawable(iconsSet.getNewDrawable(key.name, context))
return button
}
fun setToolbarButtonsActivatedStateOnPrefChange(buttonsGroup: ViewGroup, key: String?) {
// settings need to be updated when buttons change
if (key != Settings.PREF_AUTO_CORRECTION
&& key != Settings.PREF_ALWAYS_INCOGNITO_MODE
&& key?.startsWith(Settings.PREF_ONE_HANDED_MODE_PREFIX) == false)
return
GlobalScope.launch {
delay(10) // need to wait until SettingsValues are reloaded
buttonsGroup.forEach { if (it is ImageButton) setToolbarButtonActivatedState(it) }
}
}
private fun setToolbarButtonActivatedState(button: ImageButton) {
button.isActivated = when (button.tag) {
INCOGNITO -> Settings.readAlwaysIncognitoMode(DeviceProtectedUtils.getSharedPreferences(button.context))
ONE_HANDED -> Settings.getInstance().current.mOneHandedModeEnabled ONE_HANDED -> Settings.getInstance().current.mOneHandedModeEnabled
AUTOCORRECT -> Settings.getInstance().current.mAutoCorrectionEnabledPerUserSettings AUTOCORRECT -> Settings.getInstance().current.mAutoCorrectionEnabledPerUserSettings
else -> true else -> true
} }
button.setImageDrawable(iconsSet.getNewDrawable(key.name, context))
return button
} }
fun getCodeForToolbarKey(key: ToolbarKey) = Settings.getInstance().getCustomToolbarKeyCode(key) ?: when (key) { fun getCodeForToolbarKey(key: ToolbarKey) = Settings.getInstance().getCustomToolbarKeyCode(key) ?: when (key) {
@ -60,7 +81,7 @@ fun getCodeForToolbarKey(key: ToolbarKey) = Settings.getInstance().getCustomTool
COPY -> KeyCode.CLIPBOARD_COPY COPY -> KeyCode.CLIPBOARD_COPY
CUT -> KeyCode.CLIPBOARD_CUT CUT -> KeyCode.CLIPBOARD_CUT
PASTE -> KeyCode.CLIPBOARD_PASTE PASTE -> KeyCode.CLIPBOARD_PASTE
ONE_HANDED -> if (Settings.getInstance().current.mOneHandedModeEnabled) KeyCode.STOP_ONE_HANDED_MODE else KeyCode.START_ONE_HANDED_MODE ONE_HANDED -> KeyCode.TOGGLE_ONE_HANDED_MODE
INCOGNITO -> KeyCode.TOGGLE_INCOGNITO_MODE INCOGNITO -> KeyCode.TOGGLE_INCOGNITO_MODE
AUTOCORRECT -> KeyCode.TOGGLE_AUTOCORRECT AUTOCORRECT -> KeyCode.TOGGLE_AUTOCORRECT
CLEAR_CLIPBOARD -> KeyCode.CLIPBOARD_CLEAR_HISTORY CLEAR_CLIPBOARD -> KeyCode.CLIPBOARD_CLEAR_HISTORY

View file

@ -21,7 +21,7 @@
<dimen name="config_popup_keys_keyboard_slide_allowance">53.76dp</dimen> <dimen name="config_popup_keys_keyboard_slide_allowance">53.76dp</dimen>
<fraction name="config_keyboard_top_padding_holo">2.727%p</fraction> <fraction name="config_keyboard_top_padding_holo">2.727%p</fraction>
<fraction name="config_keyboard_bottom_padding_holo">0.0%p</fraction> <fraction name="config_keyboard_bottom_padding_holo">2.5%p</fraction>
<fraction name="config_key_vertical_gap_holo">5.368%p</fraction> <fraction name="config_key_vertical_gap_holo">5.368%p</fraction>
<fraction name="config_key_horizontal_gap_holo">1.020%p</fraction> <fraction name="config_key_horizontal_gap_holo">1.020%p</fraction>
<fraction name="config_key_vertical_gap_holo_narrow">4.85%p</fraction> <fraction name="config_key_vertical_gap_holo_narrow">4.85%p</fraction>

View file

@ -15,7 +15,7 @@
<dimen name="config_popup_keys_keyboard_key_height">81.9dp</dimen> <dimen name="config_popup_keys_keyboard_key_height">81.9dp</dimen>
<fraction name="config_keyboard_top_padding_holo">2.727%p</fraction> <fraction name="config_keyboard_top_padding_holo">2.727%p</fraction>
<fraction name="config_keyboard_bottom_padding_holo">0.0%p</fraction> <fraction name="config_keyboard_bottom_padding_holo">2.0%p</fraction>
<fraction name="config_key_vertical_gap_holo">4.5%p</fraction> <fraction name="config_key_vertical_gap_holo">4.5%p</fraction>
<fraction name="config_key_horizontal_gap_holo">0.9%p</fraction> <fraction name="config_key_horizontal_gap_holo">0.9%p</fraction>
<fraction name="config_key_vertical_gap_holo_narrow">4.5%p</fraction> <fraction name="config_key_vertical_gap_holo_narrow">4.5%p</fraction>

View file

@ -311,10 +311,16 @@
<string name="prefs_keyboard_height_scale">Keyboard height scale</string> <string name="prefs_keyboard_height_scale">Keyboard height scale</string>
<!-- Title of the setting for setting bottom padding height --> <!-- Title of the setting for setting bottom padding height -->
<string name="prefs_bottom_padding_scale">Bottom padding scale</string> <string name="prefs_bottom_padding_scale">Bottom padding scale</string>
<!-- Title of the setting for setting bottom padding height in landscape mode -->
<string name="prefs_bottom_padding_scale_landscape">Bottom padding scale (landscape)</string>
<!-- Title of the setting for adjusting font size on the keyboard -->
<!-- Title of the setting for setting side padding --> <!-- Title of the setting for setting side padding -->
<string name="prefs_side_padding_scale">Side padding scale</string> <string name="prefs_side_padding_scale">Side padding scale</string>
<!-- Title of the setting for setting side padding in landscape mode --> <!-- Title of the setting for setting side padding in landscape mode -->
<string name="prefs_side_padding_scale_landscape">Side padding scale (landscape)</string> <string name="prefs_side_padding_scale_landscape">Side padding scale (landscape)</string>
<string name="prefs_font_scale">Keyboard font scale</string>
<!-- Title of the setting for adjusting font size in emoji view -->
<string name="prefs_emoji_font_scale">Emoji view font scale</string>
<!-- Title of the setting for customizing space bar text --> <!-- Title of the setting for customizing space bar text -->
<string name="prefs_space_bar_text">Custom text on space bar</string> <string name="prefs_space_bar_text">Custom text on space bar</string>
<!-- Title of the setting for adding / removing custom font file --> <!-- Title of the setting for adding / removing custom font file -->

View file

@ -24,7 +24,7 @@
android:summary="@string/gnu_gpl" android:summary="@string/gnu_gpl"
android:icon="@drawable/ic_settings_about_license"> android:icon="@drawable/ic_settings_about_license">
<intent android:action="android.intent.action.VIEW" <intent android:action="android.intent.action.VIEW"
android:data="https://github.com/Helium314/HeliBoard/blob/main/LICENSE-GPL-3" /> android:data="https://github.com/Helium314/HeliBoard/blob/main/LICENSE" />
</Preference> </Preference>
<Preference <Preference

View file

@ -111,6 +111,13 @@
latin:minValue="0" latin:minValue="0"
latin:maxValue="500" /> <!-- percentage --> latin:maxValue="500" /> <!-- percentage -->
<helium314.keyboard.latin.settings.SeekBarDialogPreference
android:key="bottom_padding_scale_landscape"
android:title="@string/prefs_bottom_padding_scale_landscape"
latin:defaultValue="0"
latin:minValue="0"
latin:maxValue="500" /> <!-- percentage -->
<helium314.keyboard.latin.settings.SeekBarDialogPreference <helium314.keyboard.latin.settings.SeekBarDialogPreference
android:key="side_padding_scale" android:key="side_padding_scale"
android:title="@string/prefs_side_padding_scale" android:title="@string/prefs_side_padding_scale"
@ -137,6 +144,18 @@
android:defaultValue="" android:defaultValue=""
android:persistent="true" /> android:persistent="true" />
<helium314.keyboard.latin.settings.SeekBarDialogPreference
android:key="font_scale"
android:title="@string/prefs_font_scale"
latin:minValue="50"
latin:maxValue="150" /> <!-- percentage -->
<helium314.keyboard.latin.settings.SeekBarDialogPreference
android:key="emoji_font_scale"
android:title="@string/prefs_emoji_font_scale"
latin:minValue="50"
latin:maxValue="150" /> <!-- percentage -->
</PreferenceCategory> </PreferenceCategory>
</PreferenceScreen> </PreferenceScreen>

View file

@ -2,8 +2,8 @@ package helium314.keyboard
import helium314.keyboard.keyboard.internal.KeySpecParser import helium314.keyboard.keyboard.internal.KeySpecParser
import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode import helium314.keyboard.keyboard.internal.keyboard_parser.floris.KeyCode
import org.junit.Assert.assertEquals import kotlin.test.Test
import org.junit.Test import kotlin.test.assertEquals
class KeySpecParserTest { class KeySpecParserTest {
@Test fun label() { @Test fun label() {

View file

@ -9,6 +9,7 @@ import helium314.keyboard.keyboard.Keyboard
import helium314.keyboard.keyboard.KeyboardId import helium314.keyboard.keyboard.KeyboardId
import helium314.keyboard.keyboard.KeyboardLayoutSet import helium314.keyboard.keyboard.KeyboardLayoutSet
import helium314.keyboard.keyboard.internal.KeySpecParser import helium314.keyboard.keyboard.internal.KeySpecParser
import helium314.keyboard.keyboard.internal.KeySpecParser.KeySpecParserError
import helium314.keyboard.keyboard.internal.KeyboardBuilder import helium314.keyboard.keyboard.internal.KeyboardBuilder
import helium314.keyboard.keyboard.internal.KeyboardParams import helium314.keyboard.keyboard.internal.KeyboardParams
import helium314.keyboard.keyboard.internal.TouchPositionCorrection import helium314.keyboard.keyboard.internal.TouchPositionCorrection
@ -22,11 +23,6 @@ import helium314.keyboard.latin.RichInputMethodSubtype
import helium314.keyboard.latin.utils.AdditionalSubtypeUtils.createEmojiCapableAdditionalSubtype import helium314.keyboard.latin.utils.AdditionalSubtypeUtils.createEmojiCapableAdditionalSubtype
import helium314.keyboard.latin.utils.POPUP_KEYS_LAYOUT import helium314.keyboard.latin.utils.POPUP_KEYS_LAYOUT
import helium314.keyboard.latin.utils.checkKeys import helium314.keyboard.latin.utils.checkKeys
import org.junit.Assert.assertEquals
import org.junit.Assert.assertThrows
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.robolectric.Robolectric import org.robolectric.Robolectric
import org.robolectric.RobolectricTestRunner import org.robolectric.RobolectricTestRunner
@ -35,6 +31,11 @@ import org.robolectric.annotation.Implementation
import org.robolectric.annotation.Implements import org.robolectric.annotation.Implements
import org.robolectric.shadows.ShadowLog import org.robolectric.shadows.ShadowLog
import java.util.Locale import java.util.Locale
import kotlin.test.BeforeTest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertTrue
@RunWith(RobolectricTestRunner::class) @RunWith(RobolectricTestRunner::class)
@Config(shadows = [ @Config(shadows = [
@ -45,8 +46,7 @@ class ParserTest {
private lateinit var latinIME: LatinIME private lateinit var latinIME: LatinIME
private lateinit var params: KeyboardParams private lateinit var params: KeyboardParams
@Before @BeforeTest fun setUp() {
fun setUp() {
latinIME = Robolectric.setupService(LatinIME::class.java) latinIME = Robolectric.setupService(LatinIME::class.java)
ShadowLog.setupLogging() ShadowLog.setupLogging()
ShadowLog.stream = System.out ShadowLog.stream = System.out
@ -320,7 +320,7 @@ f""", // no newline at the end
} }
@Test fun invalidKeys() { @Test fun invalidKeys() {
assertThrows(KeySpecParser.KeySpecParserError::class.java) { assertFailsWith<KeySpecParserError> {
RawKeyboardParser.parseJsonString("""[[{ "label": "!icon/clipboard_action_key" }]]""").map { it.mapNotNull { it.compute(params)?.toKeyParams(params) } } RawKeyboardParser.parseJsonString("""[[{ "label": "!icon/clipboard_action_key" }]]""").map { it.mapNotNull { it.compute(params)?.toKeyParams(params) } }
} }
} }
@ -402,7 +402,7 @@ f""", // no newline at the end
} }
@Test fun invalidPopupKeys() { @Test fun invalidPopupKeys() {
assertThrows(KeySpecParser.KeySpecParserError::class.java) { assertFailsWith<KeySpecParserError> {
RawKeyboardParser.parseJsonString("""[[{ "label": "a", "popup": { RawKeyboardParser.parseJsonString("""[[{ "label": "a", "popup": {
"main": { "label": "!icon/clipboard_action_key" } "main": { "label": "!icon/clipboard_action_key" }
} }]]""").map { it.mapNotNull { it.compute(params)?.toKeyParams(params) } } } }]]""").map { it.mapNotNull { it.compute(params)?.toKeyParams(params) } }

View file

@ -24,9 +24,6 @@ import helium314.keyboard.latin.inputlogic.SpaceState
import helium314.keyboard.latin.settings.Settings import helium314.keyboard.latin.settings.Settings
import helium314.keyboard.latin.utils.DeviceProtectedUtils import helium314.keyboard.latin.utils.DeviceProtectedUtils
import helium314.keyboard.latin.utils.ScriptUtils import helium314.keyboard.latin.utils.ScriptUtils
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.mockito.Mockito import org.mockito.Mockito
import org.robolectric.Robolectric import org.robolectric.Robolectric
@ -37,6 +34,9 @@ import org.robolectric.annotation.Implements
import org.robolectric.shadows.ShadowLog import org.robolectric.shadows.ShadowLog
import java.util.* import java.util.*
import kotlin.math.min import kotlin.math.min
import kotlin.test.BeforeTest
import kotlin.test.Test
import kotlin.test.assertEquals
@RunWith(RobolectricTestRunner::class) @RunWith(RobolectricTestRunner::class)
@Config(shadows = [ @Config(shadows = [
@ -61,7 +61,8 @@ class InputLogicTest {
private val composingReader = RichInputConnection::class.java.getDeclaredField("mComposingText").apply { isAccessible = true } private val composingReader = RichInputConnection::class.java.getDeclaredField("mComposingText").apply { isAccessible = true }
private val connectionComposingText get() = (composingReader.get(connection) as CharSequence).toString() private val connectionComposingText get() = (composingReader.get(connection) as CharSequence).toString()
@Before fun setUp() { @BeforeTest
fun setUp() {
latinIME = Robolectric.setupService(LatinIME::class.java) latinIME = Robolectric.setupService(LatinIME::class.java)
// start logging only after latinIME is created, avoids showing the stack traces if library is not found // start logging only after latinIME is created, avoids showing the stack traces if library is not found
ShadowLog.setupLogging() ShadowLog.setupLogging()

View file

@ -2,8 +2,8 @@
package helium314.keyboard.latin package helium314.keyboard.latin
import helium314.keyboard.latin.common.LocaleUtils.constructLocale import helium314.keyboard.latin.common.LocaleUtils.constructLocale
import org.junit.Assert.assertEquals import kotlin.test.Test
import org.junit.Test import kotlin.test.assertEquals
class LocaleUtilsTest { class LocaleUtilsTest {
@Test fun createLocales() { @Test fun createLocales() {

View file

@ -6,8 +6,8 @@ import helium314.keyboard.latin.utils.ScriptUtils.SCRIPT_CYRILLIC
import helium314.keyboard.latin.utils.ScriptUtils.SCRIPT_DEVANAGARI import helium314.keyboard.latin.utils.ScriptUtils.SCRIPT_DEVANAGARI
import helium314.keyboard.latin.utils.ScriptUtils.SCRIPT_LATIN import helium314.keyboard.latin.utils.ScriptUtils.SCRIPT_LATIN
import helium314.keyboard.latin.utils.ScriptUtils.script import helium314.keyboard.latin.utils.ScriptUtils.script
import org.junit.Assert.assertEquals import kotlin.test.Test
import org.junit.Test import kotlin.test.assertEquals
class ScriptUtilsTest { class ScriptUtilsTest {
@Test fun defaultScript() { @Test fun defaultScript() {

View file

@ -3,8 +3,8 @@ package helium314.keyboard.latin
import helium314.keyboard.latin.common.StringUtils import helium314.keyboard.latin.common.StringUtils
import helium314.keyboard.latin.common.getFullEmojiAtEnd import helium314.keyboard.latin.common.getFullEmojiAtEnd
import org.junit.Assert.assertEquals import kotlin.test.Test
import org.junit.Test import kotlin.test.assertEquals
// todo: actually this test could/should be significantly expanded... // todo: actually this test could/should be significantly expanded...
class StringUtilsTest { class StringUtilsTest {

View file

@ -15,8 +15,6 @@ import helium314.keyboard.latin.settings.Settings
import helium314.keyboard.latin.settings.SettingsValuesForSuggestion import helium314.keyboard.latin.settings.SettingsValuesForSuggestion
import helium314.keyboard.latin.utils.DeviceProtectedUtils import helium314.keyboard.latin.utils.DeviceProtectedUtils
import helium314.keyboard.latin.utils.SuggestionResults import helium314.keyboard.latin.utils.SuggestionResults
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.robolectric.Robolectric import org.robolectric.Robolectric
import org.robolectric.RobolectricTestRunner import org.robolectric.RobolectricTestRunner
@ -25,6 +23,8 @@ import org.robolectric.annotation.Implementation
import org.robolectric.annotation.Implements import org.robolectric.annotation.Implements
import org.robolectric.shadows.ShadowLog import org.robolectric.shadows.ShadowLog
import java.util.* import java.util.*
import kotlin.test.BeforeTest
import kotlin.test.Test
@Suppress("NonAsciiCharacters") @Suppress("NonAsciiCharacters")
@RunWith(RobolectricTestRunner::class) @RunWith(RobolectricTestRunner::class)
@ -43,7 +43,7 @@ class SuggestTest {
private val thresholdAggressive = "1" private val thresholdAggressive = "1"
private val thresholdVeryAggressive = "2" private val thresholdVeryAggressive = "2"
@Before fun setUp() { @BeforeTest fun setUp() {
latinIME = Robolectric.setupService(LatinIME::class.java) latinIME = Robolectric.setupService(LatinIME::class.java)
// start logging only after latinIME is created, avoids showing the stack traces if library is not found // start logging only after latinIME is created, avoids showing the stack traces if library is not found
ShadowLog.setupLogging() ShadowLog.setupLogging()

View file

@ -1,33 +0,0 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '2.0.21'
repositories {
mavenCentral()
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:8.7.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
plugins {
id 'org.jetbrains.kotlin.plugin.serialization' version "$kotlin_version"
}
allprojects {
repositories {
google()
mavenCentral()
maven { url "https://jitpack.io" }
}
}
tasks.register('clean', Delete) {
delete rootProject.layout.buildDirectory
}

24
build.gradle.kts Executable file
View file

@ -0,0 +1,24 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
val kotlinVersion = "2.0.21"
repositories {
mavenCentral()
google()
}
dependencies {
classpath("com.android.tools.build:gradle:8.7.2")
classpath(kotlin("gradle-plugin", version = kotlinVersion))
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
mavenCentral()
maven { url = uri("https://jitpack.io") }
}
}

View file

@ -37,7 +37,7 @@ If the layout has exactly 2 keys in the bottom row, these keys will replace comm
* `shift_state_selector`: keys for `unshifted`, `shifted`, `shiftedManual`, `shiftedAutomatic`, `capsLock`, `manualOrLocked`, `default` (all optional) * `shift_state_selector`: keys for `unshifted`, `shifted`, `shiftedManual`, `shiftedAutomatic`, `capsLock`, `manualOrLocked`, `default` (all optional)
* `variation_selector`: keys for input types `datetime`, `time`, `date`, `password`, `normal`, `uri`, `email`, `default` (all optional) * `variation_selector`: keys for input types `datetime`, `time`, `date`, `password`, `normal`, `uri`, `email`, `default` (all optional)
* `keyboard_state_selector`: keys for `emojiKeyEnabled`, `languageKeyEnabled`, `symbols`, `moreSymbols`, `alphabet`, `default` (all optional) * `keyboard_state_selector`: keys for `emojiKeyEnabled`, `languageKeyEnabled`, `symbols`, `moreSymbols`, `alphabet`, `default` (all optional)
* the `keyEnabled` keys will be used if the corresponding setting is enabled, `symbols`, `moreSymbols`, `alphabet` will be used when the said keyboard view is active * the `<emoji/language>KeyEnabled` keys will be used if the corresponding setting is enabled, `symbols`, `moreSymbols`, `alphabet` will be used when the said keyboard view is active
* `layout_direction_selector`: keys for `ltr` and `rtl` (both mandatory) * `layout_direction_selector`: keys for `ltr` and `rtl` (both mandatory)
### Properties ### Properties
* A (non-selector) key can have the following properties: * A (non-selector) key can have the following properties:

View file

@ -25,11 +25,9 @@ task makeEmoji(type: JavaExec, dependsOn: ['jar']) {
} }
dependencies { dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib:2.0.21"
} }
java { java {
sourceCompatibility = JavaVersion.VERSION_21 sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21 targetCompatibility = JavaVersion.VERSION_21