1
0
Fork 0

added Android 13 support

This commit is contained in:
sspanak 2024-04-19 19:09:06 +03:00 committed by Dimo Karaivanov
parent ee63ca384e
commit 7e41800eac
20 changed files with 160 additions and 16 deletions

View file

@ -59,7 +59,7 @@ android {
defaultConfig { defaultConfig {
applicationId PACKAGE_NAME applicationId PACKAGE_NAME
minSdk 19 minSdk 19
targetSdk 32 targetSdk 33
versionCode getVerCode() versionCode getVerCode()
versionName getVerName() versionName getVerName()
} }

View file

@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:tools="http://schemas.android.com/tools" <manifest xmlns:tools="http://schemas.android.com/tools"
android:versionCode="474" android:versionCode="493"
android:versionName="30.0" android:versionName="30.19"
xmlns:android="http://schemas.android.com/apk/res/android"> xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/> <!-- allows displaying notifications on Android >= 13 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage" /> <!-- required for words exporting on Android < 10 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage" /> <!-- allows words exporting on Android < 10 -->
<application <application
android:allowBackup="false" android:allowBackup="false"

View file

@ -1,10 +1,8 @@
package io.github.sspanak.tt9.db.exporter; package io.github.sspanak.tt9.db.exporter;
import android.Manifest;
import android.app.Activity; import android.app.Activity;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.ContentValues; import android.content.ContentValues;
import android.content.pm.PackageManager;
import android.media.MediaScannerConnection; import android.media.MediaScannerConnection;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
@ -19,6 +17,7 @@ import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import io.github.sspanak.tt9.util.ConsumerCompat; import io.github.sspanak.tt9.util.ConsumerCompat;
import io.github.sspanak.tt9.util.Permissions;
public abstract class AbstractExporter { public abstract class AbstractExporter {
protected static String FILE_EXTENSION = ".csv"; protected static String FILE_EXTENSION = ".csv";
@ -67,11 +66,9 @@ public abstract class AbstractExporter {
protected void writeLegacy(Activity activity) throws Exception { protected void writeLegacy(Activity activity) throws Exception {
if ( Permissions permissions = new Permissions(activity);
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M if (permissions.noWriteStorage()) {
&& activity.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED permissions.requestWriteStorage();
) {
activity.requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0);
} }
final String exportDir = Environment.getExternalStoragePublicDirectory(getOutputDir()).getAbsolutePath(); final String exportDir = Environment.getExternalStoragePublicDirectory(getOutputDir()).getAbsolutePath();

View file

@ -0,0 +1,53 @@
package io.github.sspanak.tt9.preferences.screens.languages;
import androidx.preference.Preference;
import androidx.preference.SwitchPreferenceCompat;
import io.github.sspanak.tt9.preferences.PreferencesActivity;
import io.github.sspanak.tt9.util.Permissions;
public class ItemDictionaryNotifications {
public static final String NAME = "dictionary_notifications";
private final SwitchPreferenceCompat item;
private final Permissions permissions;
public ItemDictionaryNotifications(SwitchPreferenceCompat preference, PreferencesActivity activity) {
this.item = preference;
this.permissions = new Permissions(activity);
}
public ItemDictionaryNotifications populate() {
if (item == null) {
return this;
}
boolean noPermission = permissions.noPostNotifications();
item.setVisible(noPermission);
((SwitchPreferenceCompat) item).setChecked(!noPermission);
return this;
}
public ItemDictionaryNotifications enableClickHandler() {
if (item != null) {
item.setOnPreferenceChangeListener(this::onClick);
}
return this;
}
protected boolean onClick(Preference p, Object value) {
if (value == Boolean.TRUE || permissions.noPostNotifications()) {
permissions.requestPostNotifications();
}
// Switch off the component on user refusal. Android will not allow permission request again.
item.setEnabled(false);
return !permissions.noPostNotifications();
}
}

View file

@ -26,6 +26,7 @@ public class LanguagesScreen extends BaseScreenFragment {
@Override protected int getTitle() { return R.string.pref_choose_languages; } @Override protected int getTitle() { return R.string.pref_choose_languages; }
@Override protected int getXml() { return R.xml.prefs_screen_languages; } @Override protected int getXml() { return R.xml.prefs_screen_languages; }
@Override @Override
protected void onCreate() { protected void onCreate() {
ItemSelectLanguage multiSelect = new ItemSelectLanguage( ItemSelectLanguage multiSelect = new ItemSelectLanguage(
@ -34,6 +35,10 @@ public class LanguagesScreen extends BaseScreenFragment {
); );
multiSelect.populate().enableValidation(); multiSelect.populate().enableValidation();
new ItemDictionaryNotifications(findPreference(ItemDictionaryNotifications.NAME), activity)
.populate()
.enableClickHandler();
loadItem = new ItemLoadDictionary(findPreference(ItemLoadDictionary.NAME), loadItem = new ItemLoadDictionary(findPreference(ItemLoadDictionary.NAME),
activity, activity,
() -> ItemClickable.disableOthers(clickables, loadItem), () -> ItemClickable.disableOthers(clickables, loadItem),
@ -77,6 +82,7 @@ public class LanguagesScreen extends BaseScreenFragment {
refreshItems(); refreshItems();
} }
@Override @Override
public void onResume() { public void onResume() {
super.onResume(); super.onResume();

View file

@ -0,0 +1,51 @@
package io.github.sspanak.tt9.util;
import android.Manifest;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.os.Build;
public class Permissions {
private final Activity activity;
public Permissions(Activity activity) {
this.activity = activity;
}
public boolean noPostNotifications() {
return
Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
&& activity.shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS);
}
public void requestPostNotifications() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
requestPermission(Manifest.permission.POST_NOTIFICATIONS);
}
}
public boolean noWriteStorage() {
return
Build.VERSION.SDK_INT < Build.VERSION_CODES.R
&& isRefused(Manifest.permission.WRITE_EXTERNAL_STORAGE);
}
public void requestWriteStorage() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
requestPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE);
}
}
private void requestPermission(String permission) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
activity.requestPermissions(new String[] { permission }, 0);
}
}
private boolean isRefused(String permission) {
return
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
&& activity.checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED;
}
}

View file

@ -64,6 +64,8 @@
<string name="function_add_word_key">Добавяне на нова дума</string> <string name="function_add_word_key">Добавяне на нова дума</string>
<string name="function_backspace_key">Триене на текст</string> <string name="function_backspace_key">Триене на текст</string>
<string name="function_change_keyboard_key">Избор на клавиатура</string> <string name="function_change_keyboard_key">Избор на клавиатура</string>
<string name="dictionary_no_notifications">Речникови известия</string>
<string name="dictionary_no_notifications_summary">Получавайте известия за обновления на речника и за прогреса при зареждане.</string>
<string name="function_filter_clear_key">Изчистване на филтър</string> <string name="function_filter_clear_key">Изчистване на филтър</string>
<string name="function_filter_suggestions_key">Филтриране на думи</string> <string name="function_filter_suggestions_key">Филтриране на думи</string>
<string name="function_previous_suggestion_key">Предишна дума</string> <string name="function_previous_suggestion_key">Предишна дума</string>

View file

@ -101,4 +101,6 @@
<string name="dictionary_export_generating_csv">CSV wird exportiert…</string> <string name="dictionary_export_generating_csv">CSV wird exportiert…</string>
<string name="dictionary_export_generating_csv_for_language">CSV wird exportiert (%1$s)…</string> <string name="dictionary_export_generating_csv_for_language">CSV wird exportiert (%1$s)…</string>
<string name="pref_layout">Layout auf dem Bildschirm</string> <string name="pref_layout">Layout auf dem Bildschirm</string>
<string name="dictionary_no_notifications">Wörterbuchbenachrichtigungen</string>
<string name="dictionary_no_notifications_summary">Benachrichtigen über Wörterbuchaktualisierungen und den Ladevorgang.</string>
</resources> </resources>

View file

@ -71,6 +71,8 @@
<string name="pref_category_setup">Configuración inicial</string> <string name="pref_category_setup">Configuración inicial</string>
<string name="error">Error</string> <string name="error">Error</string>
<string name="function_change_keyboard_key">Cambiar el teclado</string> <string name="function_change_keyboard_key">Cambiar el teclado</string>
<string name="dictionary_no_notifications">Notificaciones del diccionario</string>
<string name="dictionary_no_notifications_summary">Recibir notificaciones sobre actualizaciones del diccionario y sobre el progreso de la carga.</string>
<string name="function_filter_clear_key">Limpiar el filtro</string> <string name="function_filter_clear_key">Limpiar el filtro</string>
<string name="function_filter_suggestions_key">Filtrar sugerencias</string> <string name="function_filter_suggestions_key">Filtrar sugerencias</string>
<string name="function_previous_suggestion_key">Sugerencia previa</string> <string name="function_previous_suggestion_key">Sugerencia previa</string>

View file

@ -62,6 +62,8 @@
<string name="function_add_word_key">Ajouter un mot</string> <string name="function_add_word_key">Ajouter un mot</string>
<string name="function_backspace_key">Retour arrière</string> <string name="function_backspace_key">Retour arrière</string>
<string name="function_change_keyboard_key">Changer le clavier</string> <string name="function_change_keyboard_key">Changer le clavier</string>
<string name="dictionary_no_notifications">Notifications du dictionnaire</string>
<string name="dictionary_no_notifications_summary">Recevoir des notifications sur les mises à jour du dictionnaire et sur la progression du chargement.</string>
<string name="function_filter_clear_key">Supprimer le filtre</string> <string name="function_filter_clear_key">Supprimer le filtre</string>
<string name="function_filter_suggestions_key">Filtrer les mots</string> <string name="function_filter_suggestions_key">Filtrer les mots</string>
<string name="function_previous_suggestion_key">Mot précédent</string> <string name="function_previous_suggestion_key">Mot précédent</string>

View file

@ -103,5 +103,7 @@
<string name="dictionary_export_generating_csv">CSV in corso…</string> <string name="dictionary_export_generating_csv">CSV in corso…</string>
<string name="dictionary_export_generating_csv_for_language">CSV in corso (%1$s)…</string> <string name="dictionary_export_generating_csv_for_language">CSV in corso (%1$s)…</string>
<string name="pref_layout">Layout sullo schermo</string> <string name="pref_layout">Layout sullo schermo</string>
<string name="dictionary_no_notifications">Notifiche del dizionario</string>
<string name="dictionary_no_notifications_summary">Ricevere notifiche sugli aggiornamenti del dizionario e sul progresso del caricamento.</string>
</resources> </resources>

View file

@ -118,4 +118,6 @@
<string name="dictionary_export_generating_csv">מייצא CSV…</string> <string name="dictionary_export_generating_csv">מייצא CSV…</string>
<string name="dictionary_export_generating_csv_for_language">מייצא CSV (%1$s)…</string> <string name="dictionary_export_generating_csv_for_language">מייצא CSV (%1$s)…</string>
<string name="pref_layout">תצורת המקלדת על המסך</string> <string name="pref_layout">תצורת המקלדת על המסך</string>
<string name="dictionary_no_notifications">התראות מילון</string>
<string name="dictionary_no_notifications_summary">לקבל התראות על עדכוני המילון ועל התקדמות הטעינה.</string>
</resources> </resources>

View file

@ -74,6 +74,8 @@
<string name="function_add_word_key">Pridėti žodį</string> <string name="function_add_word_key">Pridėti žodį</string>
<string name="function_backspace_key">Trinti</string> <string name="function_backspace_key">Trinti</string>
<string name="function_change_keyboard_key">Keisti klaviatūrą</string> <string name="function_change_keyboard_key">Keisti klaviatūrą</string>
<string name="dictionary_no_notifications">Žodyno pranešimai</string>
<string name="dictionary_no_notifications_summary">Gaukite pranešimus apie žodynų atnaujinimus ir įkėlimo progresą.</string>
<string name="function_filter_clear_key">Panaikinti filtrą</string> <string name="function_filter_clear_key">Panaikinti filtrą</string>
<string name="function_filter_suggestions_key">Filtruoti pasiūlymus</string> <string name="function_filter_suggestions_key">Filtruoti pasiūlymus</string>
<string name="function_previous_suggestion_key">Ankstesnis pasiūlytas žodis</string> <string name="function_previous_suggestion_key">Ankstesnis pasiūlytas žodis</string>

View file

@ -99,4 +99,6 @@
<string name="dictionary_export_generating_csv">CSV exporteren…</string> <string name="dictionary_export_generating_csv">CSV exporteren…</string>
<string name="dictionary_export_generating_csv_for_language">CSV exporteren (%1$s)…</string> <string name="dictionary_export_generating_csv_for_language">CSV exporteren (%1$s)…</string>
<string name="pref_layout">Indeling op het scherm</string> <string name="pref_layout">Indeling op het scherm</string>
<string name="dictionary_no_notifications">Woordenboekmeldingen</string>
<string name="dictionary_no_notifications_summary">Ontvang meldingen over woordenboekupdates en de voortgang van het laden.</string>
</resources> </resources>

View file

@ -120,4 +120,6 @@
<string name="dictionary_export_generating_csv">Exportando CSV…</string> <string name="dictionary_export_generating_csv">Exportando CSV…</string>
<string name="dictionary_export_generating_csv_for_language">Exportando CSV (%1$s)…</string> <string name="dictionary_export_generating_csv_for_language">Exportando CSV (%1$s)…</string>
<string name="pref_layout">Layout na tela</string> <string name="pref_layout">Layout na tela</string>
<string name="dictionary_no_notifications">Notificações do dicionário</string>
<string name="dictionary_no_notifications_summary">Receber notificações sobre atualizações do dicionário e sobre o progresso do carregamento.</string>
</resources> </resources>

View file

@ -63,6 +63,8 @@
<string name="function_add_word_key">Добавить новое слово</string> <string name="function_add_word_key">Добавить новое слово</string>
<string name="function_backspace_key">Стереть</string> <string name="function_backspace_key">Стереть</string>
<string name="function_change_keyboard_key">Выбор клавиатуры</string> <string name="function_change_keyboard_key">Выбор клавиатуры</string>
<string name="dictionary_no_notifications">Уведомления словаря</string>
<string name="dictionary_no_notifications_summary">Получать уведомления о обновлениях словаря и о процессе загрузки.</string>
<string name="function_filter_clear_key">Удалить фильтр</string> <string name="function_filter_clear_key">Удалить фильтр</string>
<string name="function_filter_suggestions_key">Фильтровать слова</string> <string name="function_filter_suggestions_key">Фильтровать слова</string>
<string name="function_previous_suggestion_key">Предыдущее слово</string> <string name="function_previous_suggestion_key">Предыдущее слово</string>

View file

@ -99,6 +99,8 @@
<string name="function_add_word_key">Додати слово</string> <string name="function_add_word_key">Додати слово</string>
<string name="function_backspace_key">Стерти</string> <string name="function_backspace_key">Стерти</string>
<string name="function_change_keyboard_key">Змінити клавіатуру</string> <string name="function_change_keyboard_key">Змінити клавіатуру</string>
<string name="dictionary_no_notifications">Сповіщення словника</string>
<string name="dictionary_no_notifications_summary">Отримувати повідомлення про оновлення словника та процес завантаження.</string>
<string name="function_filter_clear_key">Очистити фільтр</string> <string name="function_filter_clear_key">Очистити фільтр</string>
<string name="function_filter_suggestions_key">Фільтрувати пропозиції</string> <string name="function_filter_suggestions_key">Фільтрувати пропозиції</string>
<string name="function_previous_suggestion_key">Попередня пропозиція</string> <string name="function_previous_suggestion_key">Попередня пропозиція</string>

View file

@ -79,6 +79,8 @@
<string name="dictionary_loading_please_wait">Please wait for the dictionary to load.</string> <string name="dictionary_loading_please_wait">Please wait for the dictionary to load.</string>
<string name="dictionary_load_title">Load Selected</string> <string name="dictionary_load_title">Load Selected</string>
<string name="dictionary_not_found">Loading failed. Dictionary for \"%1$s\" not found.</string> <string name="dictionary_not_found">Loading failed. Dictionary for \"%1$s\" not found.</string>
<string name="dictionary_no_notifications">Dictionary Notifications</string>
<string name="dictionary_no_notifications_summary">Get notified about dictionary updates and see the loading progress.</string>
<string name="dictionary_truncate_title">Delete All</string> <string name="dictionary_truncate_title">Delete All</string>
<string name="dictionary_truncate_unselected">Delete Unselected</string> <string name="dictionary_truncate_unselected">Delete Unselected</string>
<string name="dictionary_truncated">Dictionary deleted successfully.</string> <string name="dictionary_truncated">Dictionary deleted successfully.</string>

View file

@ -6,6 +6,13 @@
app:layout="@layout/pref_text" app:layout="@layout/pref_text"
app:title="@string/pref_choose_languages" /> app:title="@string/pref_choose_languages" />
<SwitchPreferenceCompat
app:isPreferenceVisible="false"
app:key="dictionary_notifications"
app:layout="@layout/pref_switch"
app:title="@string/dictionary_no_notifications"
app:summary="@string/dictionary_no_notifications_summary"/>
<Preference <Preference
app:key="dictionary_load" app:key="dictionary_load"
app:layout="@layout/pref_text" app:layout="@layout/pref_text"

View file

@ -2,18 +2,24 @@
This manual explains how to configure and use Traditional T9 in different scenarios. For installation instructions, please consult the [Installation Guide](https://github.com/sspanak/tt9/blob/master/docs/installation.md) on GitHub. Finally, you may want to check out the [main repository page](https://github.com/sspanak/tt9), which includes all source code, a developer's guide, the privacy policy, and supplementary documentation. This manual explains how to configure and use Traditional T9 in different scenarios. For installation instructions, please consult the [Installation Guide](https://github.com/sspanak/tt9/blob/master/docs/installation.md) on GitHub. Finally, you may want to check out the [main repository page](https://github.com/sspanak/tt9), which includes all source code, a developer's guide, the privacy policy, and supplementary documentation.
## Initial Setup ## Initial Setup
After installing, first, you need to enable Traditional T9 as an Android keyboard. To do so, click on the launcher icon. If you need to take any action, all options besides Initial Setup would be disabled and there would be a label saying TT9 is disabled. Go to Initial Setup and enable it. After installing, first, you need to enable Traditional T9 as an Android keyboard. To do so, click on the launcher icon. If you need to take any action, all options besides Initial Setup will be disabled, and there will be a label: "TT9 is disabled". Go to Initial Setup and enable it.
_If you don't see the icon right after installing, restart your phone and it should appear. Android is trying to save some battery life by not refreshing the newly installed apps list in some cases._ _If you don't see the icon right after installing, restart your phone, and it should appear. It is due to Android trying to save some battery life by not refreshing the newly installed apps list._
### Using on a touchscreen-only phone ### Using on a touchscreen-only phone
If your phone does not have a hardware keypad, check out the [On-screen Keypad section](#on-screen-keypad). If your phone does not have a hardware keypad, check out the [On-screen Keypad section](#on-screen-keypad).
### Enabling Predictive Mode ### Enabling Predictive Mode
Predictive Mode requires a language dictionary to be loaded to provide word suggestions. You can toggle the enabled languages and load their dictionaries from: Settings Screen → [Languages](#language-options). In case you have forgotten to load some dictionary, Traditional T9 will do it for you automatically, when you start typing. Predictive Mode requires a language dictionary to be loaded to provide word suggestions. You can toggle the enabled languages and load their dictionaries from Settings Screen → [Languages](#language-options). In case, you have forgotten to load some dictionary, Traditional T9 will do it for you automatically when you start typing.
For more information, [see below](#language-options). For more information, [see below](#language-options).
#### Notes for Android 13 or higher
By default, the notifications for newly installed apps are disabled. If you enable them, TT9 will let you know when there are dictionary updates, and once you choose to install them, it will show the loading progress.
You can enable the notifications by going to Settings → Languages and toggling Dictionary Notifications.
_If you decide to keep them off, TT9 will continue to work without problems, but you will have to manage the dictionaries manually._
## Hotkeys ## Hotkeys
All hotkeys can be reconfigured or disabled from Settings → Keypad → Select Hotkeys. All hotkeys can be reconfigured or disabled from Settings → Keypad → Select Hotkeys.