added offical support for Android 15 (upgraded the target and compile SDKs to 35)
This commit is contained in:
parent
d7506e72d9
commit
80d50fb546
14 changed files with 162 additions and 30 deletions
|
|
@ -49,7 +49,7 @@ To support a new language one needs to:
|
||||||
- Find a suitable dictionary and add it to the `app/languages/dictionaries/` folder. Two file formats are supported, [see below](#dictionary-formats).
|
- Find a suitable dictionary and add it to the `app/languages/dictionaries/` folder. Two file formats are supported, [see below](#dictionary-formats).
|
||||||
- Do not forget to include the dictionary license (or readme) file in the `docs/` folder.
|
- Do not forget to include the dictionary license (or readme) file in the `docs/` folder.
|
||||||
- Create a new `.yml` file in `app/languages/definitions/` and define the language properties.
|
- Create a new `.yml` file in `app/languages/definitions/` and define the language properties.
|
||||||
- `locale` contains the language and the country codes (e.g. "en-US", "es-AR", "it-IT"). Refer to the list of [supported locales in Java](https://www.oracle.com/java/technologies/javase/jdk8-jre8-suported-locales.html#util-text).
|
- `locale` contains the language and the country codes (e.g. "en-US", "es-AR", "it-IT"). Refer to the list of [supported locales in Java](https://www.oracle.com/java/technologies/javase/jdk17-suported-locales.html#modules).
|
||||||
- `dictionaryFile` is the name of the dictionary in `app/languages/dictionaries/` folder.
|
- `dictionaryFile` is the name of the dictionary in `app/languages/dictionaries/` folder.
|
||||||
- `layout` contains the letters and punctuation marks associated with each key.
|
- `layout` contains the letters and punctuation marks associated with each key.
|
||||||
- For 0-key `[SPECIAL]`, will be fine in most languages, but you could define your own set of special characters, for example: `[@, #, $]`.
|
- For 0-key `[SPECIAL]`, will be fine in most languages, but you could define your own set of special characters, for example: `[@, #, $]`.
|
||||||
|
|
|
||||||
|
|
@ -69,12 +69,12 @@ def getVersionString = { flavor -> return flavor == 'debug' ? getDebugVersion()
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace PACKAGE_NAME
|
namespace PACKAGE_NAME
|
||||||
compileSdk 34
|
compileSdk 35
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId PACKAGE_NAME
|
applicationId PACKAGE_NAME
|
||||||
minSdk 19
|
minSdk 19
|
||||||
targetSdk 34
|
targetSdk 35
|
||||||
versionCode getVerCode()
|
versionCode getVerCode()
|
||||||
versionName getVerName()
|
versionName getVerName()
|
||||||
}
|
}
|
||||||
|
|
@ -97,6 +97,10 @@ android {
|
||||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_17
|
||||||
|
targetCompatibility JavaVersion.VERSION_17
|
||||||
|
}
|
||||||
|
|
||||||
flavorDimensions = ['app']
|
flavorDimensions = ['app']
|
||||||
productFlavors {
|
productFlavors {
|
||||||
|
|
|
||||||
|
|
@ -230,6 +230,11 @@ public class TraditionalT9 extends MainViewHandler {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTimeout(int startId) {
|
||||||
|
onZombie();
|
||||||
|
super.onTimeout(startId);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean onNumber(int key, boolean hold, int repeat) {
|
protected boolean onNumber(int key, boolean hold, int repeat) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
package io.github.sspanak.tt9.languages;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deals with inconsistencies between Java and Android language codes.
|
||||||
|
*/
|
||||||
|
class LocaleCompat {
|
||||||
|
private final Locale locale;
|
||||||
|
|
||||||
|
LocaleCompat(Locale locale) {
|
||||||
|
this.locale = locale;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getCountry() {
|
||||||
|
String country = locale != null ? locale.getCountry() : "";
|
||||||
|
return country.equals("YI") ? "JI" : country;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getLanguage() {
|
||||||
|
String language = locale != null ? locale.getLanguage() : "";
|
||||||
|
return switch (language) {
|
||||||
|
case "yi" -> "ji";
|
||||||
|
case "he" -> "iw";
|
||||||
|
case "id" -> "in";
|
||||||
|
default -> language;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return (getLanguage() + getCountry()).toUpperCase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -134,7 +134,7 @@ public class NaturalLanguage extends Language implements Comparable<NaturalLangu
|
||||||
@Override
|
@Override
|
||||||
public int getId() {
|
public int getId() {
|
||||||
if (id == 0) {
|
if (id == 0) {
|
||||||
String idString = (locale.getLanguage() + locale.getCountry()).toUpperCase();
|
String idString = new LocaleCompat(locale).toString();
|
||||||
for (int i = 0; i < idString.length(); i++) {
|
for (int i = 0; i < idString.length(); i++) {
|
||||||
id |= (idString.codePointAt(i) & 31) << (i * 5);
|
id |= (idString.codePointAt(i) & 31) << (i * 5);
|
||||||
}
|
}
|
||||||
|
|
@ -149,14 +149,11 @@ public class NaturalLanguage extends Language implements Comparable<NaturalLangu
|
||||||
return "hi";
|
return "hi";
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (getLocale().getLanguage()) {
|
return switch (getLocale().getLanguage()) {
|
||||||
case "fi":
|
case "fi" -> "su";
|
||||||
return "su";
|
case "sw" -> "ki";
|
||||||
case "sw":
|
default -> getLocale().toString();
|
||||||
return "ki";
|
};
|
||||||
default:
|
|
||||||
return getLocale().toString();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,13 @@ public class PreferencesActivity extends ActivityWithNavigation implements Prefe
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttachedToWindow() {
|
||||||
|
super.onAttachedToWindow();
|
||||||
|
preventEdgeToEdge(findViewById(R.id.preferences_container));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onPreferenceStartFragment(@NonNull PreferenceFragmentCompat caller, @NonNull Preference pref) {
|
public boolean onPreferenceStartFragment(@NonNull PreferenceFragmentCompat caller, @NonNull Preference pref) {
|
||||||
BaseScreenFragment fragment = getScreen((getScreenName(pref)));
|
BaseScreenFragment fragment = getScreen((getScreenName(pref)));
|
||||||
|
|
@ -68,6 +75,7 @@ public class PreferencesActivity extends ActivityWithNavigation implements Prefe
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onResume() {
|
protected void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,6 @@ abstract public class BaseScreenFragment extends PreferenceFragmentCompat {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public void resetFontSize(boolean reloadList) {
|
public void resetFontSize(boolean reloadList) {
|
||||||
initPreferencesList();
|
initPreferencesList();
|
||||||
preferencesList.getAll(reloadList, true);
|
preferencesList.getAll(reloadList, true);
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ import android.view.inputmethod.InputConnection;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
|
||||||
|
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
|
|
@ -17,7 +16,7 @@ import io.github.sspanak.tt9.ime.helpers.Key;
|
||||||
import io.github.sspanak.tt9.preferences.settings.SettingsStore;
|
import io.github.sspanak.tt9.preferences.settings.SettingsStore;
|
||||||
import io.github.sspanak.tt9.util.Logger;
|
import io.github.sspanak.tt9.util.Logger;
|
||||||
|
|
||||||
abstract public class ActivityWithNavigation extends AppCompatActivity {
|
abstract public class ActivityWithNavigation extends EdgeToEdgeActivity {
|
||||||
public static final String LOG_TAG = ActivityWithNavigation.class.getSimpleName();
|
public static final String LOG_TAG = ActivityWithNavigation.class.getSimpleName();
|
||||||
|
|
||||||
protected SettingsStore settings;
|
protected SettingsStore settings;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
package io.github.sspanak.tt9.ui;
|
||||||
|
|
||||||
|
import android.os.Build;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.WindowInsets;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
|
||||||
|
|
||||||
|
public class EdgeToEdgeActivity extends AppCompatActivity {
|
||||||
|
public void preventEdgeToEdge(View view) {
|
||||||
|
if (view == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
WindowInsets insets = getInsets(view);
|
||||||
|
if (insets == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
view.setPadding(
|
||||||
|
insets.getStableInsetLeft(),
|
||||||
|
insets.getStableInsetTop(),
|
||||||
|
insets.getStableInsetRight(),
|
||||||
|
insets.getStableInsetBottom()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttachedToWindow() {
|
||||||
|
super.onAttachedToWindow();
|
||||||
|
applyThemeToSystemUi();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyThemeToSystemUi() {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
|
getWindow().setStatusBarContrastEnforced(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private WindowInsets getInsets(View view) {
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
WindowInsets newInsets = view != null ? view.getRootWindowInsets() : null;
|
||||||
|
return newInsets == null ? getWindow().getDecorView().getRootWindowInsets() : newInsets;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,19 +2,36 @@ package io.github.sspanak.tt9.ui;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.Base64;
|
import android.util.Base64;
|
||||||
|
import android.view.View;
|
||||||
import android.webkit.WebView;
|
import android.webkit.WebView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.ActionBar;
|
import androidx.appcompat.app.ActionBar;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
|
||||||
|
|
||||||
abstract public class WebViewActivity extends AppCompatActivity {
|
abstract public class WebViewActivity extends EdgeToEdgeActivity implements View.OnAttachStateChangeListener {
|
||||||
|
private WebView container;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
buildLayout();
|
buildLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewAttachedToWindow(@NonNull View view) {
|
||||||
|
preventEdgeToEdge((View) view.getParent());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewDetachedFromWindow(@NonNull View view) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
container.removeOnAttachStateChangeListener(this);
|
||||||
|
super.onDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onSupportNavigateUp() {
|
public boolean onSupportNavigateUp() {
|
||||||
finish();
|
finish();
|
||||||
|
|
@ -35,7 +52,8 @@ abstract public class WebViewActivity extends AppCompatActivity {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setContent() {
|
private void setContent() {
|
||||||
WebView container = new WebView(this);
|
container = new WebView(this);
|
||||||
|
container.addOnAttachStateChangeListener(this);
|
||||||
|
|
||||||
// On API > 30 the WebView does not load the entire String with .loadData(),
|
// On API > 30 the WebView does not load the entire String with .loadData(),
|
||||||
// so we need to do this weird shit.
|
// so we need to do this weird shit.
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
package io.github.sspanak.tt9.ui.main;
|
package io.github.sspanak.tt9.ui.main;
|
||||||
|
|
||||||
|
import android.os.Build;
|
||||||
import android.view.ContextThemeWrapper;
|
import android.view.ContextThemeWrapper;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.view.WindowInsets;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
|
@ -60,6 +62,21 @@ abstract class BaseMainLayout {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the bottom padding for the edge-to-edge mode in Android 15+. Without padding,
|
||||||
|
* the bottom of the View will be cut off by the system navigation bar.
|
||||||
|
*/
|
||||||
|
protected int getBottomInsetSize() {
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM || tt9 == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int DEFAULT_SIZE = 96;
|
||||||
|
WindowInsets insets = tt9.getWindow().findViewById(android.R.id.content).getRootWindowInsets();
|
||||||
|
return insets != null ? insets.getStableInsetBottom() : DEFAULT_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
protected void enableClickHandlers() {
|
protected void enableClickHandlers() {
|
||||||
for (SoftKey key : getKeys()) {
|
for (SoftKey key : getKeys()) {
|
||||||
key.setTT9(tt9);
|
key.setTT9(tt9);
|
||||||
|
|
|
||||||
|
|
@ -203,7 +203,8 @@ class MainLayoutNumpad extends BaseMainLayout {
|
||||||
Resources resources = tt9.getResources();
|
Resources resources = tt9.getResources();
|
||||||
height = getKeyHeightCompat() * 4
|
height = getKeyHeightCompat() * 4
|
||||||
+ resources.getDimensionPixelSize(R.dimen.numpad_candidate_height)
|
+ resources.getDimensionPixelSize(R.dimen.numpad_candidate_height)
|
||||||
+ resources.getDimensionPixelSize(R.dimen.numpad_padding_bottom) * 4;
|
+ Math.round(resources.getDimension(R.dimen.numpad_padding_bottom))
|
||||||
|
+ getBottomInsetSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
return height;
|
return height;
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ class MainLayoutTray extends BaseMainLayout {
|
||||||
int getHeight(boolean forceRecalculate) {
|
int getHeight(boolean forceRecalculate) {
|
||||||
if (height <= 0 || forceRecalculate) {
|
if (height <= 0 || forceRecalculate) {
|
||||||
Resources resources = tt9.getResources();
|
Resources resources = tt9.getResources();
|
||||||
height = resources.getDimensionPixelSize(R.dimen.candidate_height);
|
height = resources.getDimensionPixelSize(R.dimen.candidate_height) + getBottomInsetSize();
|
||||||
|
|
||||||
if (isCommandPaletteShown() || isTextEditingPaletteShown()) {
|
if (isCommandPaletteShown() || isTextEditingPaletteShown()) {
|
||||||
height += resources.getDimensionPixelSize(R.dimen.numpad_key_height);
|
height += resources.getDimensionPixelSize(R.dimen.numpad_key_height);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package io.github.sspanak.tt9.ui.main;
|
package io.github.sspanak.tt9.ui.main;
|
||||||
|
|
||||||
|
import android.os.Build;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
@ -29,9 +30,11 @@ public class ResizableMainView extends MainView implements View.OnAttachStateCha
|
||||||
|
|
||||||
|
|
||||||
private void calculateSnapHeights() {
|
private void calculateSnapHeights() {
|
||||||
heightNumpad = new MainLayoutNumpad(tt9).getHeight();
|
boolean forceRecalculate = Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM;
|
||||||
heightSmall = new MainLayoutSmall(tt9).getHeight();
|
|
||||||
heightTray = new MainLayoutTray(tt9).getHeight();
|
heightNumpad = new MainLayoutNumpad(tt9).getHeight(forceRecalculate);
|
||||||
|
heightSmall = new MainLayoutSmall(tt9).getHeight(forceRecalculate);
|
||||||
|
heightTray = new MainLayoutTray(tt9).getHeight(forceRecalculate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -55,13 +58,7 @@ public class ResizableMainView extends MainView implements View.OnAttachStateCha
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onCreateAdjustHeight() {
|
@Override public void onViewAttachedToWindow(@NonNull View v) { setHeight(height, heightSmall, heightNumpad); }
|
||||||
if (tt9.getSettings().isMainLayoutNumpad() && height > heightSmall && height <= heightNumpad) {
|
|
||||||
setHeight(height, heightSmall, heightNumpad);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public void onViewAttachedToWindow(@NonNull View v) { onCreateAdjustHeight(); }
|
|
||||||
@Override public void onViewDetachedFromWindow(@NonNull View v) {}
|
@Override public void onViewDetachedFromWindow(@NonNull View v) {}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue