1
0
Fork 0

code cleanup: a simpler way of detecting the Android version

This commit is contained in:
sspanak 2025-02-25 16:41:27 +02:00 committed by Dimo Karaivanov
parent 3b4ded4362
commit adf9363f62
26 changed files with 197 additions and 180 deletions

View file

@ -5,7 +5,6 @@ import android.content.ContentResolver;
import android.content.ContentValues; import android.content.ContentValues;
import android.media.MediaScannerConnection; import android.media.MediaScannerConnection;
import android.net.Uri; import android.net.Uri;
import android.os.Build;
import android.os.Environment; import android.os.Environment;
import android.provider.MediaStore; import android.provider.MediaStore;
@ -16,6 +15,7 @@ import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import io.github.sspanak.tt9.hacks.DeviceInfo;
import io.github.sspanak.tt9.util.Permissions; import io.github.sspanak.tt9.util.Permissions;
public abstract class AbstractExporter extends AbstractFileProcessor { public abstract class AbstractExporter extends AbstractFileProcessor {
@ -83,7 +83,7 @@ public abstract class AbstractExporter extends AbstractFileProcessor {
protected void write(Activity activity) throws Exception { protected void write(Activity activity) throws Exception {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { if (DeviceInfo.AT_LEAST_ANDROID_10) {
writeAndroid10(activity); writeAndroid10(activity);
} else { } else {
writeLegacy(activity); writeLegacy(activity);
@ -98,7 +98,7 @@ public abstract class AbstractExporter extends AbstractFileProcessor {
public String getOutputDir() { public String getOutputDir() {
// on some older phones, files may not be visible in the DOCUMENTS directory, so we use DOWNLOADS // on some older phones, files may not be visible in the DOCUMENTS directory, so we use DOWNLOADS
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q ? Environment.DIRECTORY_DOCUMENTS : Environment.DIRECTORY_DOWNLOADS; return DeviceInfo.AT_LEAST_ANDROID_10 ? Environment.DIRECTORY_DOCUMENTS : Environment.DIRECTORY_DOWNLOADS;
} }

View file

@ -1,31 +1,31 @@
package io.github.sspanak.tt9.hacks; package io.github.sspanak.tt9.hacks;
import android.content.Context; import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.content.res.Resources; import android.content.res.Resources;
import android.os.Build; import android.os.Build;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import androidx.annotation.NonNull;
import io.github.sspanak.tt9.R; import io.github.sspanak.tt9.R;
public class DeviceInfo { public class DeviceInfo extends HardwareInfo {
private static Resources resources; public static final boolean AT_LEAST_ANDROID_6 = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
public static final boolean AT_LEAST_ANDROID_7 = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
public static final boolean AT_LEAST_ANDROID_8 = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
public static final boolean AT_LEAST_ANDROID_8_1 = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1;
public static final boolean AT_LEAST_ANDROID_9 = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P;
public static final boolean AT_LEAST_ANDROID_10 = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q;
public static final boolean AT_LEAST_ANDROID_11 = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R;
public static final boolean AT_LEAST_ANDROID_12 = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S;
public static final boolean AT_LEAST_ANDROID_13 = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU;
public static final boolean AT_LEAST_ANDROID_14 = Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
public static final boolean AT_LEAST_ANDROID_15 = Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM;
private static Resources getResources(Context context) {
if (resources == null) {
resources = context.getResources();
}
return resources;
}
public static boolean isLandscapeOrientation(Context context) { public static boolean isLandscapeOrientation(Context context) {
return getResources(context).getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; return getResources(context).getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
} }
public static int getNavigationBarHeight(Context context, boolean isLandscape) { public static int getNavigationBarHeight(Context context, boolean isLandscape) {
Resources resources = getResources(context); Resources resources = getResources(context);
@ -40,73 +40,4 @@ public class DeviceInfo {
return navBarHeight; return navBarHeight;
} }
} }
public static int getScreenWidth(Context context) {
return getResources(context).getDisplayMetrics().widthPixels;
}
public static int getScreenHeight(Context context) {
return getResources(context).getDisplayMetrics().heightPixels;
}
public static float getScreenHeightDp(Context context) {
return getScreenHeight(context) / getResources(context).getDisplayMetrics().density;
}
public static float getScreenWidthDp(Context context) {
return getScreenWidth(context) / getResources(context).getDisplayMetrics().density;
}
public static boolean noBackspaceKey() {
return !KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_DEL) && !KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_CLEAR);
}
public static boolean noKeyboard(Context context) {
// all Xiaomi phones are only touchscreen, but some of them report they have a keyboard
// See: https://github.com/sspanak/tt9/issues/549
if (DeviceInfo.isXiaomi()) {
return true;
}
Configuration configuration = getResources(context).getConfiguration();
return
(configuration.keyboard == Configuration.KEYBOARD_NOKEYS || configuration.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES)
&& !KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_STAR)
&& !KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_POUND);
}
public static boolean noTouchScreen(Context context) {
return !context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN);
}
public static boolean isCatS22Flip() {
return Build.MANUFACTURER.equals("Cat") && Build.MODEL.contains("S22");
}
public static boolean isLgX100S() {
return Build.MANUFACTURER.equals("LGE") && Build.MODEL.contains("X100S");
}
public static boolean isQinF21() {
return Build.MANUFACTURER.equals("DuoQin") && Build.MODEL.contains("F21");
}
public static boolean isSonim() {
return Build.MANUFACTURER.equals("Sonimtech");
}
public static boolean isSonimGen2(Context context) {
return isSonim() && Build.VERSION.SDK_INT == Build.VERSION_CODES.R && noTouchScreen(context);
}
public static boolean isXiaomi() {
return Build.MANUFACTURER.equals("Xiaomi");
}
@NonNull
@Override
public String toString() {
return "\"" + Build.MANUFACTURER + "\" " + "\"" + Build.MODEL + "\"";
}
} }

View file

@ -0,0 +1,107 @@
package io.github.sspanak.tt9.hacks;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Build;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import androidx.annotation.NonNull;
public class HardwareInfo {
private static Resources resources;
protected static Resources getResources(Context context) {
if (resources == null) {
resources = context.getResources();
}
return resources;
}
public static int getScreenWidth(Context context) {
return getResources(context).getDisplayMetrics().widthPixels;
}
public static int getScreenHeight(Context context) {
return getResources(context).getDisplayMetrics().heightPixels;
}
public static float getScreenHeightDp(Context context) {
return getScreenHeight(context) / getResources(context).getDisplayMetrics().density;
}
public static float getScreenWidthDp(Context context) {
return getScreenWidth(context) / getResources(context).getDisplayMetrics().density;
}
public static boolean noBackspaceKey() {
return !KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_DEL) && !KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_CLEAR);
}
public static boolean noKeyboard(Context context) {
// all Xiaomi phones are only touchscreen, but some of them report they have a keyboard
// See: https://github.com/sspanak/tt9/issues/549
if (isXiaomi()) {
return true;
}
Configuration configuration = getResources(context).getConfiguration();
return
(configuration.keyboard == Configuration.KEYBOARD_NOKEYS || configuration.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES)
&& !KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_STAR)
&& !KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_POUND);
}
public static boolean noTouchScreen(Context context) {
return !context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN);
}
public static boolean isCatS22Flip() {
return Build.MANUFACTURER.equals("Cat") && Build.MODEL.contains("S22");
}
public static boolean isLgX100S() {
return Build.MANUFACTURER.equals("LGE") && Build.MODEL.contains("X100S");
}
public static boolean isQinF21() {
return Build.MANUFACTURER.equals("DuoQin") && Build.MODEL.contains("F21");
}
public static boolean isSonim() {
return Build.MANUFACTURER.equals("Sonimtech");
}
public static boolean isSonimGen2(Context context) {
return isSonim() && DeviceInfo.AT_LEAST_ANDROID_11 && noTouchScreen(context);
}
public static boolean isXiaomi() {
return Build.MANUFACTURER.equals("Xiaomi");
}
@NonNull
@Override
public String toString() {
return "\"" + Build.MANUFACTURER + "\" " + "\"" + Build.MODEL + "\"";
}
}

View file

@ -1,10 +1,9 @@
package io.github.sspanak.tt9.ime; package io.github.sspanak.tt9.ime;
import android.os.Build;
import io.github.sspanak.tt9.R; import io.github.sspanak.tt9.R;
import io.github.sspanak.tt9.db.DataStore; import io.github.sspanak.tt9.db.DataStore;
import io.github.sspanak.tt9.db.words.DictionaryLoader; import io.github.sspanak.tt9.db.words.DictionaryLoader;
import io.github.sspanak.tt9.hacks.DeviceInfo;
import io.github.sspanak.tt9.ime.modes.InputMode; import io.github.sspanak.tt9.ime.modes.InputMode;
import io.github.sspanak.tt9.ime.modes.InputModeKind; import io.github.sspanak.tt9.ime.modes.InputModeKind;
import io.github.sspanak.tt9.languages.LanguageCollection; import io.github.sspanak.tt9.languages.LanguageCollection;
@ -126,7 +125,7 @@ abstract public class CommandHandler extends TextEditingHandler {
suggestionOps.cancelDelayedAccept(); suggestionOps.cancelDelayedAccept();
stopVoiceInput(); stopVoiceInput();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { if (DeviceInfo.AT_LEAST_ANDROID_9) {
switchToPreviousInputMethod(); switchToPreviousInputMethod();
return; return;
} }

View file

@ -1,6 +1,5 @@
package io.github.sspanak.tt9.ime; package io.github.sspanak.tt9.ime;
import android.os.Build;
import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager;
import io.github.sspanak.tt9.R; import io.github.sspanak.tt9.R;
@ -80,7 +79,7 @@ abstract class UiHandler extends AbstractHandler {
return; return;
} }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { if (DeviceInfo.AT_LEAST_ANDROID_9) {
requestShowSelf(DeviceInfo.isSonimGen2(getApplicationContext()) ? 0 : InputMethodManager.SHOW_IMPLICIT); requestShowSelf(DeviceInfo.isSonimGen2(getApplicationContext()) ? 0 : InputMethodManager.SHOW_IMPLICIT);
} else { } else {
showWindow(true); showWindow(true);

View file

@ -1,6 +1,5 @@
package io.github.sspanak.tt9.ime.helpers; package io.github.sspanak.tt9.ime.helpers;
import android.os.Build;
import android.view.inputmethod.EditorInfo; import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputConnection;
@ -8,6 +7,7 @@ import androidx.annotation.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import io.github.sspanak.tt9.hacks.DeviceInfo;
import io.github.sspanak.tt9.languages.Language; import io.github.sspanak.tt9.languages.Language;
import io.github.sspanak.tt9.languages.LanguageCollection; import io.github.sspanak.tt9.languages.LanguageCollection;
@ -85,7 +85,7 @@ public class InputField {
*/ */
@Nullable @Nullable
public Language getLanguage(ArrayList<Integer> allowedLanguageIds) { public Language getLanguage(ArrayList<Integer> allowedLanguageIds) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N || field == null || field.hintLocales == null) { if (!DeviceInfo.AT_LEAST_ANDROID_7 || field == null || field.hintLocales == null) {
return null; return null;
} }

View file

@ -1,12 +1,12 @@
package io.github.sspanak.tt9.ime.voice; package io.github.sspanak.tt9.ime.voice;
import android.content.Context; import android.content.Context;
import android.os.Build;
import android.speech.SpeechRecognizer; import android.speech.SpeechRecognizer;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import io.github.sspanak.tt9.R; import io.github.sspanak.tt9.R;
import io.github.sspanak.tt9.hacks.DeviceInfo;
public class VoiceInputError { public class VoiceInputError {
public final static int ERROR_NOT_AVAILABLE = 101; public final static int ERROR_NOT_AVAILABLE = 101;
@ -35,9 +35,9 @@ public class VoiceInputError {
code == SpeechRecognizer.ERROR_NO_MATCH code == SpeechRecognizer.ERROR_NO_MATCH
|| code == SpeechRecognizer.ERROR_SPEECH_TIMEOUT || code == SpeechRecognizer.ERROR_SPEECH_TIMEOUT
|| code == SpeechRecognizer.ERROR_AUDIO || code == SpeechRecognizer.ERROR_AUDIO
|| (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE && code == SpeechRecognizer.ERROR_CANNOT_LISTEN_TO_DOWNLOAD_EVENTS) || (DeviceInfo.AT_LEAST_ANDROID_12 && code == SpeechRecognizer.ERROR_SERVER_DISCONNECTED)
|| (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && code == SpeechRecognizer.ERROR_CANNOT_CHECK_SUPPORT) || (DeviceInfo.AT_LEAST_ANDROID_13 && code == SpeechRecognizer.ERROR_CANNOT_CHECK_SUPPORT)
|| (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && code == SpeechRecognizer.ERROR_SERVER_DISCONNECTED); || (DeviceInfo.AT_LEAST_ANDROID_14 && code == SpeechRecognizer.ERROR_CANNOT_LISTEN_TO_DOWNLOAD_EVENTS);
} }
@ -72,51 +72,36 @@ public class VoiceInputError {
private static String codeToDebugString(int code) { private static String codeToDebugString(int code) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE && code == SpeechRecognizer.ERROR_CANNOT_LISTEN_TO_DOWNLOAD_EVENTS) { if (DeviceInfo.AT_LEAST_ANDROID_14 && code == SpeechRecognizer.ERROR_CANNOT_LISTEN_TO_DOWNLOAD_EVENTS) {
return "Cannot listen to download events."; return "Cannot listen to download events.";
} }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && code == SpeechRecognizer.ERROR_CANNOT_CHECK_SUPPORT) { if (DeviceInfo.AT_LEAST_ANDROID_13 && code == SpeechRecognizer.ERROR_CANNOT_CHECK_SUPPORT) {
return "Cannot check voice input support."; return "Cannot check voice input support.";
} }
String message = codeToDebugString31(code); String message = codeToDebugStringCommon(code);
message = message != null ? message : codeToDebugStringCommon(code);
message = message != null ? message : "Unknown voice input error code: " + code; message = message != null ? message : "Unknown voice input error code: " + code;
return message; return message;
} }
private static String codeToDebugString31(int code) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
switch (code) {
case SpeechRecognizer.ERROR_TOO_MANY_REQUESTS:
return "Server overloaded. Try again later.";
case SpeechRecognizer.ERROR_SERVER_DISCONNECTED:
return "Lost connection to the server.";
case SpeechRecognizer.ERROR_LANGUAGE_NOT_SUPPORTED:
return "Language not supported.";
case SpeechRecognizer.ERROR_LANGUAGE_UNAVAILABLE:
return "Language missing. Try again later.";
}
}
return null;
}
private static String codeToDebugStringCommon(int code) { private static String codeToDebugStringCommon(int code) {
return switch (code) { return switch (code) {
case SpeechRecognizer.ERROR_AUDIO -> "Audio capture error."; case SpeechRecognizer.ERROR_AUDIO -> "Audio capture error.";
case SpeechRecognizer.ERROR_CLIENT -> "Speech recognition client error."; case SpeechRecognizer.ERROR_CLIENT -> "Speech recognition client error.";
case SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS -> "No microphone permissions."; case SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS -> "No microphone permissions.";
case SpeechRecognizer.ERROR_LANGUAGE_NOT_SUPPORTED -> "Language not supported.";
case SpeechRecognizer.ERROR_LANGUAGE_UNAVAILABLE -> "Language missing. Try again later.";
case SpeechRecognizer.ERROR_NETWORK -> "No network connection."; case SpeechRecognizer.ERROR_NETWORK -> "No network connection.";
case SpeechRecognizer.ERROR_NETWORK_TIMEOUT -> "Network timeout."; case SpeechRecognizer.ERROR_NETWORK_TIMEOUT -> "Network timeout.";
case SpeechRecognizer.ERROR_NO_MATCH -> "No match."; case SpeechRecognizer.ERROR_NO_MATCH -> "No match.";
case SpeechRecognizer.ERROR_RECOGNIZER_BUSY -> "Voice input service is busy."; case SpeechRecognizer.ERROR_RECOGNIZER_BUSY -> "Voice input service is busy.";
case SpeechRecognizer.ERROR_SERVER -> "Speech recognition server error."; case SpeechRecognizer.ERROR_SERVER -> "Speech recognition server error.";
case SpeechRecognizer.ERROR_SERVER_DISCONNECTED -> "Lost connection to the server.";
case SpeechRecognizer.ERROR_SPEECH_TIMEOUT -> "No speech detected."; case SpeechRecognizer.ERROR_SPEECH_TIMEOUT -> "No speech detected.";
case SpeechRecognizer.ERROR_TOO_MANY_REQUESTS -> "Server overloaded. Try again later.";
case ERROR_NOT_AVAILABLE -> "Voice input is not available."; case ERROR_NOT_AVAILABLE -> "Voice input is not available.";
case ERROR_INVALID_LANGUAGE -> "Invalid language for voice input."; case ERROR_INVALID_LANGUAGE -> "Invalid language for voice input.";
case ERROR_CANNOT_BIND_TO_VOICE_SERVICE -> "Cannot bind to the current voice input service."; case ERROR_CANNOT_BIND_TO_VOICE_SERVICE -> "Cannot bind to the current voice input service.";

View file

@ -2,7 +2,6 @@ package io.github.sspanak.tt9.ime.voice;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Build;
import android.speech.RecognizerIntent; import android.speech.RecognizerIntent;
import android.speech.SpeechRecognizer; import android.speech.SpeechRecognizer;
@ -11,6 +10,7 @@ import androidx.annotation.NonNull;
import java.util.ArrayList; import java.util.ArrayList;
import io.github.sspanak.tt9.R; import io.github.sspanak.tt9.R;
import io.github.sspanak.tt9.hacks.DeviceInfo;
import io.github.sspanak.tt9.languages.Language; import io.github.sspanak.tt9.languages.Language;
import io.github.sspanak.tt9.util.ConsumerCompat; import io.github.sspanak.tt9.util.ConsumerCompat;
import io.github.sspanak.tt9.util.Logger; import io.github.sspanak.tt9.util.Logger;
@ -35,7 +35,7 @@ public class VoiceInputOps {
ConsumerCompat<String> onStop, ConsumerCompat<String> onStop,
ConsumerCompat<VoiceInputError> onError ConsumerCompat<VoiceInputError> onError
) { ) {
isOnDeviceRecognitionAvailable = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && SpeechRecognizer.isOnDeviceRecognitionAvailable(ims); isOnDeviceRecognitionAvailable = DeviceInfo.AT_LEAST_ANDROID_12 && SpeechRecognizer.isOnDeviceRecognitionAvailable(ims);
isRecognitionAvailable = SpeechRecognizer.isRecognitionAvailable(ims); isRecognitionAvailable = SpeechRecognizer.isRecognitionAvailable(ims);
listener = new VoiceListener(ims, onStart, this::onStop, this::onError); listener = new VoiceListener(ims, onStart, this::onStop, this::onError);
@ -47,7 +47,7 @@ public class VoiceInputOps {
private void createRecognizer() { private void createRecognizer() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && isOnDeviceRecognitionAvailable) { if (DeviceInfo.AT_LEAST_ANDROID_12 && isOnDeviceRecognitionAvailable) {
speechRecognizer = SpeechRecognizer.createOnDeviceSpeechRecognizer(ims); speechRecognizer = SpeechRecognizer.createOnDeviceSpeechRecognizer(ims);
} else if (isRecognitionAvailable) { } else if (isRecognitionAvailable) {
speechRecognizer = SpeechRecognizer.createSpeechRecognizer(ims); speechRecognizer = SpeechRecognizer.createSpeechRecognizer(ims);

View file

@ -2,7 +2,6 @@ package io.github.sspanak.tt9.preferences.items;
import android.content.Context; import android.content.Context;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.AttributeSet; import android.util.AttributeSet;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@ -11,13 +10,11 @@ import androidx.appcompat.content.res.AppCompatResources;
import androidx.preference.PreferenceViewHolder; import androidx.preference.PreferenceViewHolder;
import io.github.sspanak.tt9.R; import io.github.sspanak.tt9.R;
import io.github.sspanak.tt9.hacks.DeviceInfo;
import io.github.sspanak.tt9.languages.LanguageCollection; import io.github.sspanak.tt9.languages.LanguageCollection;
import io.github.sspanak.tt9.languages.LanguageKind; import io.github.sspanak.tt9.languages.LanguageKind;
abstract public class ItemSearch extends ItemTextInput { abstract public class ItemSearch extends ItemTextInput {
private final boolean isModernDevice = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S;
public ItemSearch(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { public ItemSearch(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes); super(context, attrs, defStyleAttr, defStyleRes);
} }
@ -33,7 +30,7 @@ abstract public class ItemSearch extends ItemTextInput {
@Override protected int getLargeLayout() { @Override protected int getLargeLayout() {
return isModernDevice ? R.layout.pref_input_text : R.layout.pref_input_text_large; return DeviceInfo.AT_LEAST_ANDROID_12 ? R.layout.pref_input_text : R.layout.pref_input_text_large;
} }

View file

@ -1,10 +1,9 @@
package io.github.sspanak.tt9.preferences.items; package io.github.sspanak.tt9.preferences.items;
import android.os.Build;
import androidx.preference.Preference; import androidx.preference.Preference;
import io.github.sspanak.tt9.R; import io.github.sspanak.tt9.R;
import io.github.sspanak.tt9.hacks.DeviceInfo;
import io.github.sspanak.tt9.preferences.PreferencesActivity; import io.github.sspanak.tt9.preferences.PreferencesActivity;
import io.github.sspanak.tt9.ui.UI; import io.github.sspanak.tt9.ui.UI;
import io.github.sspanak.tt9.util.Clipboard; import io.github.sspanak.tt9.util.Clipboard;
@ -29,7 +28,7 @@ public class ItemText extends ItemClickable {
p.getSummary() p.getSummary()
); );
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2) { if (!DeviceInfo.AT_LEAST_ANDROID_13) {
UI.toast(activity, "\"" + Clipboard.getPreview(activity) + "\" copied."); UI.toast(activity, "\"" + Clipboard.getPreview(activity) + "\" copied.");
} }

View file

@ -1,12 +1,12 @@
package io.github.sspanak.tt9.preferences.screens; package io.github.sspanak.tt9.preferences.screens;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.view.MenuItem; import android.view.MenuItem;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.preference.PreferenceFragmentCompat; import androidx.preference.PreferenceFragmentCompat;
import io.github.sspanak.tt9.hacks.DeviceInfo;
import io.github.sspanak.tt9.preferences.PreferencesActivity; import io.github.sspanak.tt9.preferences.PreferencesActivity;
import io.github.sspanak.tt9.preferences.custom.ScreenPreferencesList; import io.github.sspanak.tt9.preferences.custom.ScreenPreferencesList;
import io.github.sspanak.tt9.preferences.settings.SettingsUI; import io.github.sspanak.tt9.preferences.settings.SettingsUI;
@ -97,7 +97,7 @@ 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);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { if (DeviceInfo.AT_LEAST_ANDROID_12) {
preferencesList.setFontSize(SettingsUI.FONT_SIZE_DEFAULT); preferencesList.setFontSize(SettingsUI.FONT_SIZE_DEFAULT);
} else { } else {
preferencesList.setFontSize(activity.getSettings().getSettingsFontSize()); preferencesList.setFontSize(activity.getSettings().getSettingsFontSize());

View file

@ -1,13 +1,12 @@
package io.github.sspanak.tt9.preferences.screens.appearance; package io.github.sspanak.tt9.preferences.screens.appearance;
import android.os.Build;
import androidx.preference.DropDownPreference; import androidx.preference.DropDownPreference;
import androidx.preference.Preference; import androidx.preference.Preference;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import io.github.sspanak.tt9.R; import io.github.sspanak.tt9.R;
import io.github.sspanak.tt9.hacks.DeviceInfo;
import io.github.sspanak.tt9.preferences.items.ItemDropDown; import io.github.sspanak.tt9.preferences.items.ItemDropDown;
import io.github.sspanak.tt9.preferences.settings.SettingsStore; import io.github.sspanak.tt9.preferences.settings.SettingsStore;
@ -21,7 +20,7 @@ public class ItemSelectSettingsFontSize extends ItemDropDown {
} }
public ItemDropDown populate() { public ItemDropDown populate() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { if (DeviceInfo.AT_LEAST_ANDROID_12) {
if (item != null) item.setVisible(false); if (item != null) item.setVisible(false);
return this; return this;
} }

View file

@ -1,7 +1,6 @@
package io.github.sspanak.tt9.preferences.settings; package io.github.sspanak.tt9.preferences.settings;
import android.content.Context; import android.content.Context;
import android.os.Build;
import io.github.sspanak.tt9.hacks.DeviceInfo; import io.github.sspanak.tt9.hacks.DeviceInfo;
import io.github.sspanak.tt9.preferences.screens.debug.ItemInputHandlingMode; import io.github.sspanak.tt9.preferences.screens.debug.ItemInputHandlingMode;
@ -38,7 +37,7 @@ class SettingsHacks extends BaseSettings {
/************* hack settings *************/ /************* hack settings *************/
public int getSuggestionScrollingDelay() { public int getSuggestionScrollingDelay() {
boolean defaultOn = DeviceInfo.noTouchScreen(context) && Build.VERSION.SDK_INT < Build.VERSION_CODES.Q; boolean defaultOn = DeviceInfo.noTouchScreen(context) && !DeviceInfo.AT_LEAST_ANDROID_10;
return prefs.getBoolean("pref_alternative_suggestion_scrolling", defaultOn) ? 200 : 0; return prefs.getBoolean("pref_alternative_suggestion_scrolling", defaultOn) ? 200 : 0;
} }

View file

@ -1,15 +1,16 @@
package io.github.sspanak.tt9.ui; package io.github.sspanak.tt9.ui;
import android.os.Build;
import android.view.View; import android.view.View;
import android.view.WindowInsets; import android.view.WindowInsets;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import io.github.sspanak.tt9.hacks.DeviceInfo;
public class EdgeToEdgeActivity extends AppCompatActivity { public class EdgeToEdgeActivity extends AppCompatActivity {
public void preventEdgeToEdge(View view) { public void preventEdgeToEdge(View view) {
if (view == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) { if (view == null || !DeviceInfo.AT_LEAST_ANDROID_15) {
return; return;
} }
@ -33,13 +34,13 @@ public class EdgeToEdgeActivity extends AppCompatActivity {
} }
private void applyThemeToSystemUi() { private void applyThemeToSystemUi() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { if (DeviceInfo.AT_LEAST_ANDROID_10) {
getWindow().setStatusBarContrastEnforced(true); getWindow().setStatusBarContrastEnforced(true);
} }
} }
private WindowInsets getInsets(View view) { private WindowInsets getInsets(View view) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { if (!DeviceInfo.AT_LEAST_ANDROID_6) {
return null; return null;
} }

View file

@ -5,7 +5,6 @@ import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.inputmethodservice.InputMethodService; import android.inputmethodservice.InputMethodService;
import android.os.Build;
import android.os.Looper; import android.os.Looper;
import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager;
import android.widget.Toast; import android.widget.Toast;
@ -16,6 +15,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import java.util.HashMap; import java.util.HashMap;
import io.github.sspanak.tt9.hacks.DeviceInfo;
import io.github.sspanak.tt9.preferences.PreferencesActivity; import io.github.sspanak.tt9.preferences.PreferencesActivity;
public class UI { public class UI {
@ -56,8 +56,8 @@ public class UI {
public static void confirm(Context context, String title, String message, String OKLabel, Runnable onOk, Runnable onCancel) { public static void confirm(Context context, String title, String message, String OKLabel, Runnable onOk, Runnable onCancel) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) { if (DeviceInfo.AT_LEAST_ANDROID_12) {
new AlertDialog.Builder(context) new MaterialAlertDialogBuilder(context)
.setTitle(title) .setTitle(title)
.setMessage(message) .setMessage(message)
.setPositiveButton(OKLabel, (dialog, whichButton) -> { if (onOk != null) onOk.run(); }) .setPositiveButton(OKLabel, (dialog, whichButton) -> { if (onOk != null) onOk.run(); })
@ -65,7 +65,7 @@ public class UI {
.setCancelable(false) .setCancelable(false)
.show(); .show();
} else { } else {
new MaterialAlertDialogBuilder(context) new AlertDialog.Builder(context)
.setTitle(title) .setTitle(title)
.setMessage(message) .setMessage(message)
.setPositiveButton(OKLabel, (dialog, whichButton) -> { if (onOk != null) onOk.run(); }) .setPositiveButton(OKLabel, (dialog, whichButton) -> { if (onOk != null) onOk.run(); })

View file

@ -1,11 +1,11 @@
package io.github.sspanak.tt9.ui; package io.github.sspanak.tt9.ui;
import android.os.Build;
import android.view.HapticFeedbackConstants; import android.view.HapticFeedbackConstants;
import android.view.View; import android.view.View;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import io.github.sspanak.tt9.hacks.DeviceInfo;
import io.github.sspanak.tt9.preferences.settings.SettingsStore; import io.github.sspanak.tt9.preferences.settings.SettingsStore;
import io.github.sspanak.tt9.ui.main.keys.BaseClickableKey; import io.github.sspanak.tt9.ui.main.keys.BaseClickableKey;
import io.github.sspanak.tt9.ui.main.keys.SoftKeyNumber; import io.github.sspanak.tt9.ui.main.keys.SoftKeyNumber;
@ -28,7 +28,7 @@ public class Vibration {
} }
public static int getHoldVibration() { public static int getHoldVibration() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { if (DeviceInfo.AT_LEAST_ANDROID_11) {
return HapticFeedbackConstants.CONFIRM; return HapticFeedbackConstants.CONFIRM;
} else { } else {
return HapticFeedbackConstants.VIRTUAL_KEY; return HapticFeedbackConstants.VIRTUAL_KEY;
@ -36,7 +36,7 @@ public class Vibration {
} }
public static int getReleaseVibration() { public static int getReleaseVibration() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) { if (DeviceInfo.AT_LEAST_ANDROID_8_1) {
return HapticFeedbackConstants.KEYBOARD_RELEASE; return HapticFeedbackConstants.KEYBOARD_RELEASE;
} else { } else {
return HapticFeedbackConstants.VIRTUAL_KEY; return HapticFeedbackConstants.VIRTUAL_KEY;

View file

@ -2,13 +2,13 @@ package io.github.sspanak.tt9.ui;
import android.app.Activity; import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.os.Build;
import android.webkit.WebView; import android.webkit.WebView;
import android.webkit.WebViewClient; import android.webkit.WebViewClient;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import io.github.sspanak.tt9.R; import io.github.sspanak.tt9.R;
import io.github.sspanak.tt9.hacks.DeviceInfo;
import io.github.sspanak.tt9.util.Clipboard; import io.github.sspanak.tt9.util.Clipboard;
import io.github.sspanak.tt9.util.Logger; import io.github.sspanak.tt9.util.Logger;
@ -26,9 +26,9 @@ public class WebViewSafeClient extends WebViewClient {
return super.shouldOverrideUrlLoading(view, url); return super.shouldOverrideUrlLoading(view, url);
} }
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q || !shareLink(url)) { if (!DeviceInfo.AT_LEAST_ANDROID_10 || !shareLink(url)) {
Clipboard.copy(activity, url); Clipboard.copy(activity, url);
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2) { if (!DeviceInfo.AT_LEAST_ANDROID_13) {
UI.toastShortSingle(activity, R.string.help_url_copied); UI.toastShortSingle(activity, R.string.help_url_copied);
} }
} }

View file

@ -3,13 +3,13 @@ package io.github.sspanak.tt9.ui.dialogs;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.inputmethodservice.InputMethodService; import android.inputmethodservice.InputMethodService;
import android.os.Build;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import io.github.sspanak.tt9.R; import io.github.sspanak.tt9.R;
import io.github.sspanak.tt9.db.DataStore; import io.github.sspanak.tt9.db.DataStore;
import io.github.sspanak.tt9.db.entities.AddWordResult; import io.github.sspanak.tt9.db.entities.AddWordResult;
import io.github.sspanak.tt9.hacks.DeviceInfo;
import io.github.sspanak.tt9.languages.Language; import io.github.sspanak.tt9.languages.Language;
import io.github.sspanak.tt9.languages.LanguageCollection; import io.github.sspanak.tt9.languages.LanguageCollection;
import io.github.sspanak.tt9.preferences.settings.SettingsStore; import io.github.sspanak.tt9.preferences.settings.SettingsStore;
@ -33,7 +33,7 @@ public class AddWordDialog extends PopupDialog {
.setSettings(new SettingsStore(context)) .setSettings(new SettingsStore(context))
// The main theme does not work on Android <= 11 and the _AddWord theme does not work on 12+. // The main theme does not work on Android <= 11 and the _AddWord theme does not work on 12+.
// Not sure why since they inherit from the same parent, but it is what it is. // Not sure why since they inherit from the same parent, but it is what it is.
.setTheme(Build.VERSION.SDK_INT < Build.VERSION_CODES.S ? R.style.TTheme_AddWord : R.style.TTheme) .setTheme(DeviceInfo.AT_LEAST_ANDROID_12 ? R.style.TTheme : R.style.TTheme_AddWord)
.build(), .build(),
activityFinisher activityFinisher
); );

View file

@ -64,7 +64,7 @@ abstract class BaseMainLayout {
protected WindowInsets onApplyInsets(@NonNull View v, @NonNull WindowInsets windowInsets) { protected WindowInsets onApplyInsets(@NonNull View v, @NonNull WindowInsets windowInsets) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) { if (DeviceInfo.AT_LEAST_ANDROID_15) {
return preventEdgeToEdge(v, windowInsets); return preventEdgeToEdge(v, windowInsets);
} else { } else {
return windowInsets; return windowInsets;
@ -100,7 +100,7 @@ abstract class BaseMainLayout {
* is re-created and it is not yet possible to get the new window insets. * is re-created and it is not yet possible to get the new window insets.
*/ */
public void preventEdgeToEdge() { public void preventEdgeToEdge() {
if (tt9 == null || view == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) { if (tt9 == null || view == null || !DeviceInfo.AT_LEAST_ANDROID_15) {
return; return;
} }
@ -113,7 +113,7 @@ abstract class BaseMainLayout {
void requestPreventEdgeToEdge() { void requestPreventEdgeToEdge() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM && view != null) { if (view != null && DeviceInfo.AT_LEAST_ANDROID_15) {
view.requestApplyInsets(); view.requestApplyInsets();
} }
} }
@ -248,7 +248,7 @@ abstract class BaseMainLayout {
int width = tt9.getSettings().getWidthPercent(); int width = tt9.getSettings().getWidthPercent();
return return
Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM DeviceInfo.AT_LEAST_ANDROID_15
&& ((isLandscape && width >= 75) || (!isLandscape && width >= 65)); && ((isLandscape && width >= 75) || (!isLandscape && width >= 65));
} }

View file

@ -1,11 +1,11 @@
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 androidx.annotation.NonNull; import androidx.annotation.NonNull;
import io.github.sspanak.tt9.hacks.DeviceInfo;
import io.github.sspanak.tt9.ime.TraditionalT9; import io.github.sspanak.tt9.ime.TraditionalT9;
import io.github.sspanak.tt9.preferences.settings.SettingsStore; import io.github.sspanak.tt9.preferences.settings.SettingsStore;
import io.github.sspanak.tt9.ui.Vibration; import io.github.sspanak.tt9.ui.Vibration;
@ -29,7 +29,7 @@ public class ResizableMainView extends MainView implements View.OnAttachStateCha
private void calculateSnapHeights() { private void calculateSnapHeights() {
boolean forceRecalculate = Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM; boolean forceRecalculate = DeviceInfo.AT_LEAST_ANDROID_15;
heightNumpad = new MainLayoutNumpad(tt9).getHeight(forceRecalculate); heightNumpad = new MainLayoutNumpad(tt9).getHeight(forceRecalculate);
heightSmall = new MainLayoutSmall(tt9).getHeight(forceRecalculate); heightSmall = new MainLayoutSmall(tt9).getHeight(forceRecalculate);

View file

@ -120,7 +120,7 @@ public class SoftKey extends BaseClickableKey {
if ( if (
getNoEmojiTitle() > 0 getNoEmojiTitle() > 0
&& ( && (
Characters.noEmojiSupported() Characters.NO_EMOJI_SUPPORT
|| (new Text(getText().toString()).startsWithGraphic() && !new Paint().hasGlyph(getText().toString())) || (new Text(getText().toString()).startsWithGraphic() && !new Paint().hasGlyph(getText().toString()))
) )
) { ) {

View file

@ -5,12 +5,12 @@ import android.app.NotificationManager;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.Context; import android.content.Context;
import android.content.res.Resources; import android.content.res.Resources;
import android.os.Build;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationCompat;
import io.github.sspanak.tt9.hacks.DeviceInfo;
import io.github.sspanak.tt9.languages.Language; import io.github.sspanak.tt9.languages.Language;
public abstract class DictionaryNotification { public abstract class DictionaryNotification {
@ -43,7 +43,7 @@ public abstract class DictionaryNotification {
protected abstract PendingIntent createNavigationIntent(@NonNull Context context, @Nullable Language language); protected abstract PendingIntent createNavigationIntent(@NonNull Context context, @Nullable Language language);
private NotificationCompat.Builder getNotificationBuilderCompat(Context context) { private NotificationCompat.Builder getNotificationBuilderCompat(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (DeviceInfo.AT_LEAST_ANDROID_8) {
manager.createNotificationChannel(new NotificationChannel( manager.createNotificationChannel(new NotificationChannel(
NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_ID,
"Dictionary Status", "Dictionary Status",

View file

@ -4,12 +4,13 @@ package io.github.sspanak.tt9.util;
import android.Manifest; import android.Manifest;
import android.app.Activity; import android.app.Activity;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.os.Build;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import java.util.HashMap; import java.util.HashMap;
import io.github.sspanak.tt9.hacks.DeviceInfo;
public class Permissions { public class Permissions {
private static final HashMap<String, Boolean> firstTimeAsking = new HashMap<>(); private static final HashMap<String, Boolean> firstTimeAsking = new HashMap<>();
@NonNull private final Activity activity; @NonNull private final Activity activity;
@ -20,7 +21,7 @@ public class Permissions {
public boolean noPostNotifications() { public boolean noPostNotifications() {
return return
Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU DeviceInfo.AT_LEAST_ANDROID_13
&& isRefused(Manifest.permission.POST_NOTIFICATIONS) && isRefused(Manifest.permission.POST_NOTIFICATIONS)
&& ( && (
Boolean.TRUE.equals(firstTimeAsking.getOrDefault(Manifest.permission.POST_NOTIFICATIONS, true)) Boolean.TRUE.equals(firstTimeAsking.getOrDefault(Manifest.permission.POST_NOTIFICATIONS, true))
@ -29,7 +30,7 @@ public class Permissions {
} }
public void requestPostNotifications() { public void requestPostNotifications() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { if (DeviceInfo.AT_LEAST_ANDROID_13) {
firstTimeAsking.put(Manifest.permission.POST_NOTIFICATIONS, false); firstTimeAsking.put(Manifest.permission.POST_NOTIFICATIONS, false);
requestPermission(Manifest.permission.POST_NOTIFICATIONS); requestPermission(Manifest.permission.POST_NOTIFICATIONS);
} }
@ -42,25 +43,25 @@ public class Permissions {
public boolean noWriteStorage() { public boolean noWriteStorage() {
return return
Build.VERSION.SDK_INT < Build.VERSION_CODES.R !DeviceInfo.AT_LEAST_ANDROID_11
&& isRefused(Manifest.permission.WRITE_EXTERNAL_STORAGE); && isRefused(Manifest.permission.WRITE_EXTERNAL_STORAGE);
} }
public void requestWriteStorage() { public void requestWriteStorage() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { if (!DeviceInfo.AT_LEAST_ANDROID_11) {
requestPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE); requestPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE);
} }
} }
private void requestPermission(String permission) { private void requestPermission(String permission) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (DeviceInfo.AT_LEAST_ANDROID_6) {
activity.requestPermissions(new String[] { permission }, 0); activity.requestPermissions(new String[] { permission }, 0);
} }
} }
private boolean isRefused(String permission) { private boolean isRefused(String permission) {
return return
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M DeviceInfo.AT_LEAST_ANDROID_6
&& activity.checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED; && activity.checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED;
} }
} }

View file

@ -1,7 +1,6 @@
package io.github.sspanak.tt9.util; package io.github.sspanak.tt9.util;
import android.content.Context; import android.content.Context;
import android.os.Build;
import android.os.LocaleList; import android.os.LocaleList;
import android.provider.Settings; import android.provider.Settings;
import android.view.Window; import android.view.Window;
@ -14,6 +13,8 @@ import androidx.annotation.Nullable;
import java.util.Locale; import java.util.Locale;
import io.github.sspanak.tt9.hacks.DeviceInfo;
public class SystemSettings { public class SystemSettings {
private static InputMethodManager inputManager; private static InputMethodManager inputManager;
@ -46,7 +47,7 @@ public class SystemSettings {
@NonNull @NonNull
public static String getLocale() { public static String getLocale() {
Locale locale = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ? LocaleList.getDefault().get(0) : Locale.getDefault(); Locale locale = DeviceInfo.AT_LEAST_ANDROID_7 ? LocaleList.getDefault().get(0) : Locale.getDefault();
String country = locale.getCountry(); String country = locale.getCountry();
String language = locale.getLanguage(); String language = locale.getLanguage();
@ -77,7 +78,7 @@ public class SystemSettings {
* @see : <a href="https://stackoverflow.com/a/77240330">the only working solution</a>. * @see : <a href="https://stackoverflow.com/a/77240330">the only working solution</a>.
*/ */
public static void setNavigationBarDarkTheme(@Nullable Window window, boolean dark) { public static void setNavigationBarDarkTheme(@Nullable Window window, boolean dark) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { if (!DeviceInfo.AT_LEAST_ANDROID_11) {
return; return;
} }

View file

@ -1,12 +1,15 @@
package io.github.sspanak.tt9.util.chars; package io.github.sspanak.tt9.util.chars;
import android.graphics.Paint; import android.graphics.Paint;
import android.os.Build;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import io.github.sspanak.tt9.hacks.DeviceInfo;
class Emoji extends Punctuation { class Emoji extends Punctuation {
final public static boolean NO_EMOJI_SUPPORT = !DeviceInfo.AT_LEAST_ANDROID_6;
final private static ArrayList<String> TextEmoticons = new ArrayList<>(Arrays.asList( final private static ArrayList<String> TextEmoticons = new ArrayList<>(Arrays.asList(
":)", ":D", ":P", ";)", "\\m/", ":-O", ":|", ":(" ":)", ":D", ":P", ";)", "\\m/", ":-O", ":|", ":("
)); ));
@ -34,12 +37,8 @@ class Emoji extends Punctuation {
return !(ch < 256 || Character.isLetterOrDigit(ch) || Character.isAlphabetic(ch)); return !(ch < 256 || Character.isLetterOrDigit(ch) || Character.isAlphabetic(ch));
} }
public static boolean noEmojiSupported() {
return Build.VERSION.SDK_INT < Build.VERSION_CODES.M;
}
public static ArrayList<String> getEmoji(int level) { public static ArrayList<String> getEmoji(int level) {
if (noEmojiSupported()) { if (NO_EMOJI_SUPPORT) {
return new ArrayList<>(TextEmoticons); return new ArrayList<>(TextEmoticons);
} }
@ -59,6 +58,6 @@ class Emoji extends Punctuation {
} }
public static int getMaxEmojiLevel() { public static int getMaxEmojiLevel() {
return noEmojiSupported() ? 1 : Emoji.size(); return NO_EMOJI_SUPPORT ? 1 : Emoji.size();
} }
} }

View file

@ -1,17 +1,17 @@
package io.github.sspanak.tt9.util.chars; package io.github.sspanak.tt9.util.chars;
import android.graphics.Paint; import android.graphics.Paint;
import android.os.Build;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import io.github.sspanak.tt9.hacks.DeviceInfo;
import io.github.sspanak.tt9.languages.Language; import io.github.sspanak.tt9.languages.Language;
import io.github.sspanak.tt9.languages.LanguageKind; import io.github.sspanak.tt9.languages.LanguageKind;
class Punctuation { class Punctuation {
public static final String GR_QUESTION_MARK = ";"; public static final String GR_QUESTION_MARK = ";";
public static final String NEW_LINE = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && new Paint().hasGlyph("") ? "" : "\\n"; public static final String NEW_LINE = DeviceInfo.AT_LEAST_ANDROID_6 && new Paint().hasGlyph("") ? "" : "\\n";
public static final String ZWJ = "\u200D"; public static final String ZWJ = "\u200D";
public static final String ZWJ_GRAPHIC = "ZWJ"; public static final String ZWJ_GRAPHIC = "ZWJ";
public static final String ZWNJ = "\u200C"; public static final String ZWNJ = "\u200C";