Skip to content
Merged
2 changes: 0 additions & 2 deletions app/src/keyboards/java/be/scri/services/GeneralKeyboardIME.kt
Original file line number Diff line number Diff line change
Expand Up @@ -594,8 +594,6 @@ abstract class GeneralKeyboardIME(
return isActionSearch || isUriType || hasSearchHint
}

override fun isClipboardKeyEnabled(): Boolean = PreferencesHelper.getIsClipboardKeyEnabled(this, language)

private fun loadLanguageData() {
val languageAlias = getLanguageAlias(language)
dataContract = dbManagers.getLanguageContract(languageAlias)
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/assets/i18n
556 changes: 295 additions & 261 deletions app/src/main/java/be/scri/App.kt

Large diffs are not rendered by default.

2 changes: 0 additions & 2 deletions app/src/main/java/be/scri/helpers/KeyboardBase.kt
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@ class KeyboardBase {
val keyboardLetters: Int

fun isSearchBar(): Boolean

fun isClipboardKeyEnabled(): Boolean
}

/** Horizontal gap default for all rows */
Expand Down
29 changes: 29 additions & 0 deletions app/src/main/java/be/scri/helpers/NetworkMonitor.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// SPDX-License-Identifier: GPL-3.0-or-later
package be.scri.helpers

import android.content.Context
import android.net.ConnectivityManager
import android.net.NetworkCapabilities

/**
* Utility singleton to check network connectivity status.
*/
object NetworkMonitor {
/**
* Checks if the device is currently connected to the internet.
*
* @param context The application context.
* @return true if connected via WiFi, Cellular, or Ethernet, false otherwise.
*/
fun isOnline(context: Context): Boolean {
val connectivityManager =
context.getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager
?: return false

val activeNetwork = connectivityManager.activeNetwork ?: return false
val capabilities = connectivityManager.getNetworkCapabilities(activeNetwork) ?: return false

return capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) &&
capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
}
}
26 changes: 0 additions & 26 deletions app/src/main/java/be/scri/helpers/PreferencesHelper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ object PreferencesHelper {
private const val DEFAULT_CURRENCY = "default_currency"
private const val HOLD_FOR_ALT_KEYS = "hold_for_alt_keys"
private const val INCREASE_TEXT_SIZE = "increase_text_size"
private const val CLIPBOARD_KEY_ON_KEYBOARD = "clipboard_key_on_keyboard"

/**
* Sets the translation source language for a given language.
Expand Down Expand Up @@ -535,31 +534,6 @@ object PreferencesHelper {
return sharedPref.getBoolean(getLanguageSpecificPreferenceKey(HOLD_FOR_ALT_KEYS, language), true)
}

/**
* Sets the preference for showing or hiding the clipboard key on the keyboard.
*/
fun setClipboardKeyPreference(
context: Context,
language: String,
enabled: Boolean,
) {
val sharedPref = context.getSharedPreferences(SCRIBE_PREFS, Context.MODE_PRIVATE)
sharedPref.edit {
putBoolean(getLanguageSpecificPreferenceKey(CLIPBOARD_KEY_ON_KEYBOARD, language), enabled)
}
}

/**
* Retrieves whether the clipboard key is enabled on the keyboard for a given language.
*/
fun getIsClipboardKeyEnabled(
context: Context,
language: String,
): Boolean {
val sharedPref = context.getSharedPreferences(SCRIBE_PREFS, Context.MODE_PRIVATE)
return sharedPref.getBoolean(getLanguageSpecificPreferenceKey(CLIPBOARD_KEY_ON_KEYBOARD, language), true)
}

/**
* Resets the application hints, marking them as not shown in the shared preferences.
*
Expand Down
27 changes: 1 addition & 26 deletions app/src/main/java/be/scri/ui/screens/LanguageSettingsScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -109,13 +109,6 @@ fun LanguageSettingsScreen(
)
}

val clipboardKeyOnKeyboardState =
remember {
mutableStateOf(
PreferencesHelper.getIsClipboardKeyEnabled(context, language),
)
}

val wordByWordDeletionState =
remember {
mutableStateOf(
Expand Down Expand Up @@ -161,15 +154,6 @@ fun LanguageSettingsScreen(
shouldDisableAccentCharacter,
)
},
toggleClipboardKeyOnKeyboard = clipboardKeyOnKeyboardState.value,
onToggleClipboardKeyOnKeyboard = { isEnabled ->
clipboardKeyOnKeyboardState.value = isEnabled
PreferencesHelper.setClipboardKeyPreference(
context,
language,
isEnabled,
)
},
onCurrencySelect = onCurrencySelect,
),
)
Expand Down Expand Up @@ -366,8 +350,6 @@ private fun getLayoutListData(
onTogglePeriodAndComma: (Boolean) -> Unit,
toggleDisableAccentCharacter: Boolean,
onToggleDisableAccentCharacter: (Boolean) -> Unit,
toggleClipboardKeyOnKeyboard: Boolean,
onToggleClipboardKeyOnKeyboard: (Boolean) -> Unit,
onCurrencySelect: () -> Unit,
): List<ScribeItem> {
val list: MutableList<ScribeItem> = mutableListOf()
Expand All @@ -393,14 +375,7 @@ private fun getLayoutListData(
onToggle = onTogglePeriodAndComma,
),
)
list.add(
ScribeItem.SwitchItem(
title = R.string.i18n_app_settings_keyboard_layout_clipboard_on_keyboard,
desc = R.string.i18n_app_settings_keyboard_layout_clipboard_on_keyboard_description,
state = toggleClipboardKeyOnKeyboard,
onToggle = onToggleClipboardKeyOnKeyboard,
),
)

list.add(
ScribeItem.ClickableItem(
title = R.string.i18n_app_settings_keyboard_layout_default_currency,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ import android.app.Application
import android.content.Context
import android.database.sqlite.SQLiteException
import android.util.Log
import android.widget.Toast
import androidx.compose.runtime.mutableStateMapOf
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import be.scri.R
import be.scri.data.remote.ConjugateDynamicDbHelper
import be.scri.data.remote.RetrofitClient
import be.scri.helpers.LanguageMappingConstants
import be.scri.helpers.NetworkMonitor
import be.scri.helpers.StringUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
Expand All @@ -29,6 +29,7 @@ import java.io.IOException
import java.time.LocalDate

/** ViewModel to manage conjugate data download states and actions. */
@Suppress("TooManyFunctions")
class ConjugateDataDownloadViewModel(
application: Application,
) : AndroidViewModel(application) {
Expand All @@ -39,6 +40,9 @@ class ConjugateDataDownloadViewModel(
private val _checkUpdateState = MutableStateFlow(CheckUpdateState.Idle)
val checkUpdateState = _checkUpdateState.asStateFlow()

private val _toastMessage = MutableStateFlow<String?>(null)
val toastMessage = _toastMessage.asStateFlow()

private var checkUpdateJob: Job? = null

/**
Expand Down Expand Up @@ -98,6 +102,11 @@ class ConjugateDataDownloadViewModel(
key: String,
forceDownload: Boolean = false,
) {
if (!NetworkMonitor.isOnline(getApplication())) {
showToast(getApplication<Application>().getString(R.string.i18n_app_download_error_no_network))
return
}

val currentState = downloadStates[key] ?: DownloadState.Ready
val displayLang = key.replaceFirstChar { it.uppercase() }
if (forceDownload) {
Expand All @@ -113,7 +122,7 @@ class ConjugateDataDownloadViewModel(
R.string.i18n_app_download_menu_ui_conjugate_data_already_up_to_date,
)
val msg = StringUtils.formatStringWithParams(template, displayLang)
Toast.makeText(getApplication(), msg, Toast.LENGTH_SHORT).show()
showToast(msg)
return
}
}
Expand Down Expand Up @@ -156,7 +165,7 @@ class ConjugateDataDownloadViewModel(
R.string.i18n_app_download_menu_ui_conjugate_data_download_success,
)
val msg = StringUtils.formatStringWithParams(template, displayLang)
Toast.makeText(getApplication(), msg, Toast.LENGTH_SHORT).show()
showToast(msg)
}
} else {
// Already up to date: Skip the DB work.
Expand All @@ -166,7 +175,7 @@ class ConjugateDataDownloadViewModel(
getApplication<Application>().getString(
R.string.i18n_app_download_menu_ui_download_data_generic_already_up_to_date,
)
Toast.makeText(getApplication(), msg, Toast.LENGTH_SHORT).show()
showToast(msg)
}
}
} catch (e: IOException) {
Expand Down Expand Up @@ -209,6 +218,11 @@ class ConjugateDataDownloadViewModel(
* Handles the "All languages" download action by initiating downloads for all languages that are not already completed or downloading.
*/
fun handleDownloadAllLanguages() {
if (!NetworkMonitor.isOnline(getApplication())) {
showToast(getApplication<Application>().getString(R.string.i18n_app_download_error_no_network))
return
}

val toDownload =
downloadStates.keys.filter { key ->
downloadStates[key] != DownloadState.Completed && downloadStates[key] != DownloadState.Downloading
Expand Down Expand Up @@ -272,6 +286,11 @@ class ConjugateDataDownloadViewModel(
* Checks for new data updates for all completed languages.
*/
fun checkForNewData() {
if (!NetworkMonitor.isOnline(getApplication())) {
showToast(getApplication<Application>().getString(R.string.i18n_app_download_error_no_network))
return
}

checkUpdateJob?.cancel()

val keysToCheck = downloadStates.keys.filter { downloadStates[it] == DownloadState.Completed }
Expand Down Expand Up @@ -314,7 +333,7 @@ class ConjugateDataDownloadViewModel(
withContext(Dispatchers.Main) {
// Reset status so user can retry.
downloadStates[key] = DownloadState.Ready
Toast.makeText(getApplication(), message, Toast.LENGTH_LONG).show()
showToast(message)
}
}

Expand All @@ -326,4 +345,12 @@ class ConjugateDataDownloadViewModel(
downloadJobs.values.forEach { it.cancel() }
downloadJobs.clear()
}

private fun showToast(msg: String) {
viewModelScope.launch {
_toastMessage.value = msg
kotlinx.coroutines.delay(3000)
_toastMessage.value = null
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ import android.app.Application
import android.content.Context
import android.database.sqlite.SQLiteException
import android.util.Log
import android.widget.Toast
import androidx.compose.runtime.mutableStateMapOf
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import be.scri.R
import be.scri.data.remote.DynamicDbHelper
import be.scri.data.remote.RetrofitClient
import be.scri.helpers.LanguageMappingConstants
import be.scri.helpers.NetworkMonitor
import be.scri.helpers.StringUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
Expand All @@ -29,6 +29,7 @@ import java.io.IOException
import java.time.LocalDate

/** ViewModel to manage data download states and actions. */
@Suppress("TooManyFunctions")
class DataDownloadViewModel(
application: Application,
) : AndroidViewModel(application) {
Expand All @@ -39,6 +40,9 @@ class DataDownloadViewModel(
private val _checkUpdateState = MutableStateFlow(CheckUpdateState.Idle)
val checkUpdateState = _checkUpdateState.asStateFlow()

private val _toastMessage = MutableStateFlow<String?>(null)
val toastMessage = _toastMessage.asStateFlow()

private var checkUpdateJob: Job? = null

/**
Expand Down Expand Up @@ -98,6 +102,11 @@ class DataDownloadViewModel(
key: String,
forceDownload: Boolean = false,
) {
if (!NetworkMonitor.isOnline(getApplication())) {
showToast(getApplication<Application>().getString(R.string.i18n_app_download_error_no_network))
return
}

val currentState = downloadStates[key] ?: DownloadState.Ready
val displayLang = key.replaceFirstChar { it.uppercase() }
if (forceDownload) {
Expand All @@ -113,7 +122,7 @@ class DataDownloadViewModel(
R.string.i18n_app_download_menu_ui_download_data_already_up_to_date,
)
val msg = StringUtils.formatStringWithParams(template, displayLang)
Toast.makeText(getApplication(), msg, Toast.LENGTH_SHORT).show()
showToast(msg)
return
}
}
Expand Down Expand Up @@ -156,7 +165,7 @@ class DataDownloadViewModel(
R.string.i18n_app_download_menu_ui_download_data_download_success,
)
val msg = StringUtils.formatStringWithParams(template, displayLang)
Toast.makeText(getApplication(), msg, Toast.LENGTH_SHORT).show()
showToast(msg)
}
} else {
// Already up to date: Skip the DB work.
Expand All @@ -166,7 +175,7 @@ class DataDownloadViewModel(
getApplication<Application>().getString(
R.string.i18n_app_download_menu_ui_download_data_generic_already_up_to_date,
)
Toast.makeText(getApplication(), msg, Toast.LENGTH_SHORT).show()
showToast(msg)
}
}
} catch (e: IOException) {
Expand Down Expand Up @@ -209,6 +218,11 @@ class DataDownloadViewModel(
* Handles the "All languages" download action by initiating downloads for all languages that are not already completed or downloading.
*/
fun handleDownloadAllLanguages() {
if (!NetworkMonitor.isOnline(getApplication())) {
showToast(getApplication<Application>().getString(R.string.i18n_app_download_error_no_network))
return
}

val toDownload =
downloadStates.keys.filter { key ->
downloadStates[key] != DownloadState.Completed && downloadStates[key] != DownloadState.Downloading
Expand Down Expand Up @@ -272,6 +286,11 @@ class DataDownloadViewModel(
* Checks for new data updates for all completed languages.
*/
fun checkForNewData() {
if (!NetworkMonitor.isOnline(getApplication())) {
showToast(getApplication<Application>().getString(R.string.i18n_app_download_error_no_network))
return
}

checkUpdateJob?.cancel()

val keysToCheck = downloadStates.keys.filter { downloadStates[it] == DownloadState.Completed }
Expand Down Expand Up @@ -314,7 +333,7 @@ class DataDownloadViewModel(
withContext(Dispatchers.Main) {
// Reset status so user can retry.
downloadStates[key] = DownloadState.Ready
Toast.makeText(getApplication(), message, Toast.LENGTH_LONG).show()
showToast(message)
}
}

Expand All @@ -326,6 +345,14 @@ class DataDownloadViewModel(
downloadJobs.values.forEach { it.cancel() }
downloadJobs.clear()
}

private fun showToast(msg: String) {
viewModelScope.launch {
_toastMessage.value = msg
kotlinx.coroutines.delay(3000)
_toastMessage.value = null
}
}
}

/**
Expand Down
Loading
Loading