#1399 rewrite to kotlin

This commit is contained in:
Sean Mac Gillicuddy 2019-09-05 14:57:43 +01:00
parent 3acdf2bd59
commit 0f360abc4c
7 changed files with 111 additions and 176 deletions

View File

@ -34,20 +34,18 @@ import org.kiwix.kiwixmobile.di.qualifiers.IO;
import org.kiwix.kiwixmobile.di.qualifiers.MainThread; import org.kiwix.kiwixmobile.di.qualifiers.MainThread;
import org.kiwix.kiwixmobile.downloader.model.UriToFileConverter; import org.kiwix.kiwixmobile.downloader.model.UriToFileConverter;
import org.kiwix.kiwixmobile.utils.BookUtils; import org.kiwix.kiwixmobile.utils.BookUtils;
import org.kiwix.kiwixmobile.utils.LanguageUtils;
@Module(includes = { @Module(includes = {
ActivityBindingModule.class, ActivityBindingModule.class,
AndroidInjectionModule.class, AndroidInjectionModule.class,
DownloaderModule.class, DownloaderModule.class,
ViewModelModule.class, ViewModelModule.class,
DatabaseModule.class DatabaseModule.class
}) })
public class ApplicationModule { public class ApplicationModule {
@Provides @Singleton Application provideApplication(Context context) { @Provides @Singleton Application provideApplication(Context context) {
return (Application) context; return (Application) context;
} }
@Provides @Provides
@ -62,14 +60,8 @@ public class ApplicationModule {
@Provides @Provides
@Singleton @Singleton
BookUtils provideBookUtils(LanguageUtils.LanguageContainer container) { BookUtils provideBookUtils() {
return new BookUtils(container); return new BookUtils();
}
@Provides
@Singleton
LanguageUtils.LanguageContainer provideLanguageContainer() {
return new LanguageUtils.LanguageContainer();
} }
@IO @IO

View File

@ -3,8 +3,11 @@ package org.kiwix.kiwixmobile.extensions
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import android.os.Build.VERSION
import android.os.Build.VERSION_CODES
import android.widget.Toast import android.widget.Toast
import org.kiwix.kiwixmobile.zim_manager.BaseBroadcastReceiver import org.kiwix.kiwixmobile.zim_manager.BaseBroadcastReceiver
import java.util.Locale
fun Context?.toast( fun Context?.toast(
stringId: Int, stringId: Int,
@ -28,3 +31,8 @@ fun Context?.toast(
fun Context.registerReceiver(baseBroadcastReceiver: BaseBroadcastReceiver): Intent? = fun Context.registerReceiver(baseBroadcastReceiver: BaseBroadcastReceiver): Intent? =
registerReceiver(baseBroadcastReceiver, IntentFilter(baseBroadcastReceiver.action)) registerReceiver(baseBroadcastReceiver, IntentFilter(baseBroadcastReceiver.action))
val Context.locale: Locale
get() =
if (VERSION.SDK_INT >= VERSION_CODES.N) resources.configuration.locales.get(0)
else resources.configuration.locale

View File

@ -110,7 +110,7 @@ public class KiwixTextToSpeech {
onSpeakingListener.onSpeakingEnded(); onSpeakingListener.onSpeakingEnded();
} }
} else { } else {
Locale locale = LanguageUtils.ISO3ToLocale(ZimContentProvider.getLanguage()); Locale locale = LanguageUtils.iSO3ToLocale(ZimContentProvider.getLanguage());
int result; int result;
if ("mul".equals(ZimContentProvider.getLanguage())) { if ("mul".equals(ZimContentProvider.getLanguage())) {
Log.d(TAG_KIWIX, "TextToSpeech: disabled " + Log.d(TAG_KIWIX, "TextToSpeech: disabled " +

View File

@ -28,11 +28,9 @@ import java.util.Map;
public class BookUtils { public class BookUtils {
public final Map<String, Locale> localeMap; public final Map<String, Locale> localeMap;
public LanguageUtils.LanguageContainer container;
// Create a map of ISO 369-2 language codes // Create a map of ISO 369-2 language codes
public BookUtils(LanguageUtils.LanguageContainer container) { public BookUtils() {
this.container = container;
String[] languages = Locale.getISOLanguages(); String[] languages = Locale.getISOLanguages();
localeMap = new HashMap<>(languages.length); localeMap = new HashMap<>(languages.length);
for (String language : languages) { for (String language : languages) {
@ -49,7 +47,7 @@ public class BookUtils {
} }
if (languageCode.length() == 2) { if (languageCode.length() == 2) {
return container.findLanguageName(languageCode).getLanguageName(); return new LanguageContainer(languageCode).getLanguageName();
} else if (languageCode.length() == 3) { } else if (languageCode.length() == 3) {
try { try {
return localeMap.get(languageCode).getDisplayLanguage(); return localeMap.get(languageCode).getDisplayLanguage();

View File

@ -0,0 +1,19 @@
package org.kiwix.kiwixmobile.utils
import java.util.Locale
class LanguageContainer private constructor(val languageCode: String, val languageName: String) {
constructor(languageCode: String) : this(languageCode, chooseLanguageName(languageCode))
companion object {
private fun chooseLanguageName(
languageCode: String
): String {
val displayLanguage = Locale(languageCode).displayLanguage
return if (displayLanguage.length == 2 || displayLanguage.isEmpty())
Locale.ENGLISH.displayLanguage
else
displayLanguage
}
}
}

View File

@ -31,70 +31,46 @@ import android.view.InflateException
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.widget.TextView import android.widget.TextView
import org.kiwix.kiwixmobile.extensions.locale
import org.kiwix.kiwixmobile.utils.Constants.TAG_KIWIX import org.kiwix.kiwixmobile.utils.Constants.TAG_KIWIX
import org.kiwix.kiwixmobile.utils.files.FileUtils import org.kiwix.kiwixmobile.utils.files.FileUtils
import java.text.Collator import java.text.Collator
import java.util.Collections
import java.util.HashMap
import java.util.Locale import java.util.Locale
import java.util.MissingResourceException
class LanguageUtils(private val mContext: Context) { class LanguageUtils(private val context: Context) {
private val mLanguageList: List<LanguageContainer> = sortWithCollator(setupLanguageList()) private val localeLanguageCodes: List<String> = languageCodesFromAssets()
private val mLocaleLanguageCodes: List<String> = getLanguageCodesFromAssets()) private val languageList: List<LanguageContainer> = languageContainersFrom(localeLanguageCodes)
val keys = languageList.map(LanguageContainer::languageCode)
private fun sortWithCollator(languageCodesFromAssets: List<LanguageContainer>): List<String> { private fun languageContainersFrom(languageCodes: List<String>) =
val localeCollator = Collator.getInstance(mContext.resources.configuration.locale) sortWithCollator(languageCodes.map(::LanguageContainer).toMutableList())
localeCollator.strength = Collator.SECONDARY
Collections.sort(languageCodesFromAssets) { o1, o2 -> localeCollator.compare(o1.languageName, o2) } private fun languageCodesFromAssets(): List<String> {
return FileUtils.readLocalesFromAssets(context)
.filter(String::isNotEmpty)
.map { locale -> locale.trim { it <= ' ' } }
} }
// Get a list of all the language names private fun sortWithCollator(languageCodesFromAssets: MutableList<LanguageContainer>):
val values: List<String> MutableList<LanguageContainer> {
get() = mLanguageList.map(LanguageContainer::languageName) val localeCollator =
Collator.getInstance(context.locale).apply { strength = Collator.SECONDARY }
// Get a list of all the language codes languageCodesFromAssets.sortWith(Comparator { o1, o2 ->
val keys: List<String> localeCollator.compare(
get() = mLanguageList.map(LanguageContainer::languageCode) o1.languageName,
o2.languageName
init { )
})
sortLanguageList() return languageCodesFromAssets
} }
// Read the language codes, that are supported in this app from the locales.txt file
private fun getLanguageCodesFromAssets() = FileUtils.readLocalesFromAssets(mContext)
.filterNot(String::isEmpty)
.map { locale -> locale.trim { it <= ' ' } }
// Create a list containing the language code and the corresponding (english) langauge name
private fun setupLanguageList() = mLocaleLanguageCodes.map(::LanguageContainer)
// Sort the language list by the language name
private fun sortLanguageList(locale: Locale) {
Collections.sort(
mLanguageList
) { a, b -> localeCollator.compare(a.languageName, b.languageName) }
}
// Check, if the selected Locale is supported and weather we actually need to change our font.
// We do this by checking, if our Locale is available in the List, that Locale.getAvailableLocales() returns.
private fun haveToChangeFont(sharedPreferenceUtil: SharedPreferenceUtil): Boolean { private fun haveToChangeFont(sharedPreferenceUtil: SharedPreferenceUtil): Boolean {
if (sharedPreferenceUtil.getPrefLanguage("").isEmpty()) {
for (s in Locale.getAvailableLocales()) { return false
if (s.language == Locale.getDefault().toString()) {
return false
}
// Don't change the language, if the options hasn't been set
val language = sharedPreferenceUtil.getPrefLanguage("")
if (language.isEmpty()) {
return false
}
} }
return true return Locale.getAvailableLocales().firstOrNull { locale ->
locale.language == Locale.getDefault().toString()
} == null
} }
// Change the font of all the TextViews and its subclasses in our whole app by attaching a custom // Change the font of all the TextViews and its subclasses in our whole app by attaching a custom
@ -115,10 +91,11 @@ class LanguageUtils(private val mContext: Context) {
val field = LayoutInflater::class.java.getDeclaredField("mFactorySet") val field = LayoutInflater::class.java.getDeclaredField("mFactorySet")
field.isAccessible = true field.isAccessible = true
field.setBoolean(layoutInflater, false) field.setBoolean(layoutInflater, false)
layoutInflater.factory = LayoutInflaterFactory(mContext, layoutInflater) layoutInflater.factory = LayoutInflaterFactory(context, layoutInflater)
} catch (e: NoSuchFieldException) { } catch (e: NoSuchFieldException) {
Log.w( Log.w(
TAG_KIWIX, "Font Change Failed: Could not access private field of the LayoutInflater", TAG_KIWIX,
"Font Change Failed: Could not access private field of the LayoutInflater",
e e
) )
} catch (e: IllegalAccessException) { } catch (e: IllegalAccessException) {
@ -143,34 +120,25 @@ class LanguageUtils(private val mContext: Context) {
private val mLayoutInflater: LayoutInflater private val mLayoutInflater: LayoutInflater
) : LayoutInflater.Factory { ) : LayoutInflater.Factory {
@SuppressWarnings("ImplicitSamInstance")
override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? { override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? {
// Apply the custom font, if the xml tag equals "TextView", "EditText" or "AutoCompleteTextView" // Apply the custom font, if the xml equals "TextView", "EditText" or "AutoCompleteTextView"
if (name.equals("TextView", ignoreCase = true) if (name.equals("TextView", ignoreCase = true) ||
|| name.equals("EditText", ignoreCase = true) name.equals("EditText", ignoreCase = true) ||
|| name.equals("AutoCompleteTextView", ignoreCase = true) name.equals("AutoCompleteTextView", ignoreCase = true)
) { ) {
try { try {
val inflater = mLayoutInflater val view = mLayoutInflater.createView(name, null, attrs)
val view = inflater.createView(name, null, attrs)
Handler().post { Handler().post {
val textView = view as TextView (view as TextView).apply {
typeface = Typeface.createFromAsset(
// Set the custom typeface mContext.assets,
textView.typeface = Typeface.createFromAsset( getTypeface(Locale.getDefault().language)
mContext.assets, )
getTypeface(Locale.getDefault().language) setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize - 2f)
) }
Log.d(TAG_KIWIX, "Applying custom font")
// Reduce the text size
textView.setTextSize(
TypedValue.COMPLEX_UNIT_PX,
textView.textSize - 2f
)
} }
return view return view
} catch (e: InflateException) { } catch (e: InflateException) {
Log.w(TAG_KIWIX, "Could not apply the custom font to $name", e) Log.w(TAG_KIWIX, "Could not apply the custom font to $name", e)
@ -178,51 +146,47 @@ class LanguageUtils(private val mContext: Context) {
Log.w(TAG_KIWIX, "Could not apply the custom font to $name", e) Log.w(TAG_KIWIX, "Could not apply the custom font to $name", e)
} }
} }
return null return null
} }
} }
class LanguageContainer(val languageCode: String, val languageName: String) {
constructor(languageCode: String) : this(languageCode, chooseLanguageName(languageCode))
companion object {
private fun chooseLanguageName(
languageCode: String
): String {
val displayLanguage = Locale(languageCode).displayLanguage
return if (displayLanguage.length == 2 || displayLanguage.isEmpty()) {
Locale.ENGLISH.displayLanguage
} else {
displayLanguage
}
}
}
}
companion object { companion object {
private var mLocaleMap: HashMap<String, Locale>? = null private var mLocaleMap =
Locale.getAvailableLocales().associateBy { it.isO3Language.toUpperCase(Locale.ROOT) }
private var fontExceptions = mapOf(
"km" to "fonts/KhmerOS.ttf",
"my" to "fonts/Parabaik.ttf",
"guj" to "fonts/Lohit-Gujarati.ttf",
"ori" to "fonts/Lohit-Odia.ttf",
"pan" to "fonts/Lohit-Punjabi.ttf",
"dzo" to "fonts/DDC_Uchen.ttf",
"bod" to "fonts/DDC_Uchen.ttf",
"sin" to "fonts/Kaputa-Regular.ttf",
// http://scriptsource.org/cms/scripts/page.php?item_id=entry_detail&uid=kstzk8hbg4
// Link above shows that we are allowed to distribute this font
"chr" to "fonts/Digohweli.ttf"
)
@JvmStatic
fun handleLocaleChange( fun handleLocaleChange(
context: Context, context: Context,
sharedPreferenceUtil: SharedPreferenceUtil sharedPreferenceUtil: SharedPreferenceUtil
) { ) {
val language = sharedPreferenceUtil.getPrefLanguage("") val language = sharedPreferenceUtil.getPrefLanguage("")
if (language.isEmpty()) { if (language.isEmpty()) {
return return
} }
handleLocaleChange(context, language) handleLocaleChange(context, language)
} }
@JvmStatic
fun handleLocaleChange(context: Context, language: String) { fun handleLocaleChange(context: Context, language: String) {
val locale = Locale(language) val locale = Locale(language)
Locale.setDefault(locale) Locale.setDefault(locale)
val config = Configuration() val config = Configuration()
if (Build.VERSION.SDK_INT >= 17) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
config.setLocale(locale) config.setLocale(locale)
config.setLayoutDirection(locale) config.setLayoutDirection(locale)
} else { } else {
@ -238,64 +202,18 @@ class LanguageUtils(private val mContext: Context) {
* @param iso3 ISO3 language code * @param iso3 ISO3 language code
* @return [java.util.Locale] that represents the language of the provided code * @return [java.util.Locale] that represents the language of the provided code
*/ */
fun ISO3ToLocale(iso3: String): Locale? { @JvmStatic
if (mLocaleMap == null) { fun iSO3ToLocale(iso3: String?): Locale? =
val locales = Locale.getAvailableLocales() iso3?.let { mLocaleMap[it.toUpperCase(Locale.ROOT)] }
mLocaleMap = HashMap()
for (locale in locales) {
try {
mLocaleMap!![locale.isO3Language.toUpperCase()] = locale
} catch (e: MissingResourceException) {
// Do nothing
}
}
}
return mLocaleMap!![iso3.toUpperCase()]
}
fun getCurrentLocale(context: Context): Locale { @JvmStatic
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { fun getCurrentLocale(context: Context) = context.locale
context.resources.configuration.locales.get(0)
} else {
context.resources.configuration.locale @JvmStatic
} fun getTypeface(languageCode: String) =
} fontExceptions[languageCode] ?: "fonts/DejaVuSansCondensed.ttf"
// This method will determine which font will be applied to the not-supported-locale.
// You can define exceptions to the default DejaVu font in the 'exceptions' Hashmap:
fun getTypeface(languageCode: String): String? {
// Define the exceptions to the rule. The font has to be placed in the assets folder.
// Key: the language code; Value: the name of the font.
val exceptions = HashMap<String, String>()
exceptions["km"] = "fonts/KhmerOS.ttf"
exceptions["my"] = "fonts/Parabaik.ttf"
exceptions["guj"] = "fonts/Lohit-Gujarati.ttf"
exceptions["ori"] = "fonts/Lohit-Odia.ttf"
exceptions["pan"] = "fonts/Lohit-Punjabi.ttf"
exceptions["dzo"] = "fonts/DDC_Uchen.ttf"
exceptions["bod"] = "fonts/DDC_Uchen.ttf"
exceptions["sin"] = "fonts/Kaputa-Regular.ttf"
// http://scriptsource.org/cms/scripts/page.php?item_id=entry_detail&uid=kstzk8hbg4
// Link above shows that we are allowed to distribute this font
exceptions["chr"] = "fonts/Digohweli.ttf"
// These scripts could be supported via more Lohit fonts if DejaVu doesn't
// support them. That is untested now as they aren't even in the language
// menu:
// * (no ISO code?) (Devanagari/Nagari) -- at 0% in translatewiki
// * mr (Marathi) -- at 21% in translatewiki
// Check, if an exception applies to our current locale
return if (exceptions.containsKey(languageCode)) {
exceptions[languageCode]
} else "fonts/DejaVuSansCondensed.ttf"
// Return the default font
}
@JvmStatic
fun getResourceString(appContext: Context, str: String): String { fun getResourceString(appContext: Context, str: String): String {
var resourceName = str var resourceName = str
if (resourceName.contains("REPLACE_")) { if (resourceName.contains("REPLACE_")) {

View File

@ -24,12 +24,12 @@ import org.junit.Assert.assertEquals
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
class BookUtilsTest { class BookUtilsTest {
private val container: LanguageUtils.LanguageContainer = mockk() private val container: LanguageContainer = mockk()
// Test that the language returned for the given language code is correct // Test that the language returned for the given language code is correct
@Test @Test
fun testLanguageFromCode() { fun testLanguageFromCode() {
val t = BookUtils(container) val t = BookUtils()
// testing trivial cases // testing trivial cases
assertEquals("null is passed", "", t.getLanguage(null)) assertEquals("null is passed", "", t.getLanguage(null))