mirror of
https://github.com/kiwix/kiwix-android.git
synced 2025-08-03 10:46:53 -04:00
Merge remote-tracking branch 'origin/develop' into feature/macgills/detekt
# Conflicts: # core/src/main/java/org/kiwix/kiwixmobile/core/main/KiwixTextToSpeech.java
This commit is contained in:
commit
f4ada8ce0b
@ -17,6 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects
|
package org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import org.kiwix.kiwixmobile.core.R
|
import org.kiwix.kiwixmobile.core.R
|
||||||
@ -37,6 +38,7 @@ data class OpenFile(private val bookOnDisk: BookOnDisk) :
|
|||||||
activity.finish()
|
activity.finish()
|
||||||
activity.start<KiwixMainActivity> {
|
activity.start<KiwixMainActivity> {
|
||||||
data = file.toUri()
|
data = file.toUri()
|
||||||
|
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,32 +18,55 @@
|
|||||||
|
|
||||||
package custom
|
package custom
|
||||||
|
|
||||||
|
import org.json.simple.JSONObject
|
||||||
import java.text.ParseException
|
import java.text.ParseException
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
|
const val dateFormat = "YYYY-MM"
|
||||||
|
|
||||||
data class CustomApp(
|
data class CustomApp(
|
||||||
val name: String,
|
val name: String,
|
||||||
val url: String,
|
val url: String,
|
||||||
val enforcedLanguage: String,
|
val enforcedLanguage: String,
|
||||||
val displayName: String,
|
val displayName: String,
|
||||||
val versionName: String = parseVersionNameFromUrlOrUsePattern(url, "YYYY-MM")
|
val versionName: String,
|
||||||
|
val disableSideBar: Boolean = false,
|
||||||
|
val disableTabs: Boolean = false,
|
||||||
|
val disableReadAloud: Boolean = false
|
||||||
) {
|
) {
|
||||||
val versionCode: Int = formatDate("YYDDD0").toInt()
|
constructor(name: String, parsedJson: JSONObject) : this(
|
||||||
|
name,
|
||||||
|
parsedJson.getAndCast("zim_url"),
|
||||||
|
parsedJson.getAndCast("enforced_lang"),
|
||||||
|
parsedJson.getAndCast("app_name"),
|
||||||
|
readVersionOrInfer(parsedJson),
|
||||||
|
parsedJson.getAndCast("disable_sidebar") ?: false,
|
||||||
|
parsedJson.getAndCast("disable_tabs") ?: false,
|
||||||
|
parsedJson.getAndCast("disable_read_aloud") ?: false
|
||||||
|
)
|
||||||
|
|
||||||
|
val versionCode: Int = formatCurrentDate("YYDDD0").toInt()
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private fun readVersionOrInfer(parsedJson: JSONObject) =
|
||||||
|
parsedJson.getAndCast("version_name")
|
||||||
|
?: versionNameFromUrl(parsedJson.getAndCast("zim_url"))
|
||||||
|
?: formatCurrentDate()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseVersionNameFromUrlOrUsePattern(url: String, pattern: String) =
|
private fun versionNameFromUrl(url: String) =
|
||||||
url.substringAfterLast("_")
|
url.substringAfterLast("_")
|
||||||
.substringBeforeLast(".")
|
.substringBeforeLast(".")
|
||||||
.takeIf {
|
.takeIf {
|
||||||
try {
|
try {
|
||||||
SimpleDateFormat(pattern, Locale.ROOT).parse(it) != null
|
SimpleDateFormat(dateFormat, Locale.ROOT).parse(it) != null
|
||||||
} catch (parseException: ParseException) {
|
} catch (parseException: ParseException) {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
?: formatDate(pattern)
|
|
||||||
|
|
||||||
private fun formatDate(pattern: String) =
|
private fun formatCurrentDate(pattern: String = dateFormat) =
|
||||||
Date().let(SimpleDateFormat(pattern, Locale.ROOT)::format)
|
Date().let(SimpleDateFormat(pattern, Locale.ROOT)::format)
|
||||||
|
@ -24,199 +24,39 @@ import org.json.simple.JSONObject
|
|||||||
import org.json.simple.parser.JSONParser
|
import org.json.simple.parser.JSONParser
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
|
typealias ProductFlavors = NamedDomainObjectContainer<ProductFlavor>
|
||||||
|
|
||||||
object CustomApps {
|
object CustomApps {
|
||||||
private val example = CustomApp(
|
|
||||||
name = "customexample",
|
|
||||||
url = "http://download.kiwix.org/zim/wikipedia_fr_test.zim",
|
|
||||||
enforcedLanguage = "en",
|
|
||||||
displayName = "Test Custom App"
|
|
||||||
)
|
|
||||||
private val phet = CustomApp(
|
|
||||||
name = "phet",
|
|
||||||
url = "http://download.kiwix.org/zim/phet/phet_mul_2018-09.zim",
|
|
||||||
enforcedLanguage = "en",
|
|
||||||
displayName = "PhET"
|
|
||||||
)
|
|
||||||
private val tunisie = CustomApp(
|
|
||||||
name = "tunisie",
|
|
||||||
url = "http://download.kiwix.org/zim/wikipedia_fr_tunisie_novid.zim",
|
|
||||||
enforcedLanguage = "fr",
|
|
||||||
versionName = "2018-07",
|
|
||||||
displayName = "Encyclopédie de la Tunisie"
|
|
||||||
)
|
|
||||||
private val venezuela = CustomApp(
|
|
||||||
name = "venezuela",
|
|
||||||
url = "http://download.kiwix.org/zim/wikipedia/wikipedia_es_venezuela_2018-07.zim",
|
|
||||||
enforcedLanguage = "es",
|
|
||||||
displayName = "Enciclopedia de Venezuela"
|
|
||||||
)
|
|
||||||
private val wikimed = CustomApp(
|
|
||||||
name = "wikimed",
|
|
||||||
url = "http://download.kiwix.org/zim/wikipedia_en_medicine_novid.zim",
|
|
||||||
enforcedLanguage = "en",
|
|
||||||
versionName = "2018-08",
|
|
||||||
displayName = "Medical Wikipedia"
|
|
||||||
)
|
|
||||||
private val wikimedar = CustomApp(
|
|
||||||
name = "wikimedar",
|
|
||||||
url = "http://download.kiwix.org/zim/wikipedia/wikipedia_ar_medicine_novid_2018-08.zim",
|
|
||||||
enforcedLanguage = "ar",
|
|
||||||
displayName = "وِيكيبيديا الطبية (بلا اتصال بالانترنت)"
|
|
||||||
)
|
|
||||||
private val wikimedde = CustomApp(
|
|
||||||
name = "wikimedde",
|
|
||||||
url = "http://download.kiwix.org/zim/wikipedia/wikipedia_de_medicine_novid_2018-10.zim",
|
|
||||||
enforcedLanguage = "de",
|
|
||||||
displayName = "Wikipedia Medizin (Offline)"
|
|
||||||
)
|
|
||||||
private val wikimedes = CustomApp(
|
|
||||||
name = "wikimedes",
|
|
||||||
url = "http://download.kiwix.org/zim/wikipedia/wikipedia_es_medicine_novid_2018-10.zim",
|
|
||||||
enforcedLanguage = "es",
|
|
||||||
displayName = "Wikipedia Médica (Offline)"
|
|
||||||
)
|
|
||||||
private val wikimedfa = CustomApp(
|
|
||||||
name = "wikimedfa",
|
|
||||||
url = "http://download.kiwix.org/zim/wikipedia/wikipedia_fa_medicine_novid_2018-07.zim",
|
|
||||||
enforcedLanguage = "fa",
|
|
||||||
displayName = "ویکیپدیای پزشکی (آفلاین)"
|
|
||||||
)
|
|
||||||
private val wikimedfr = CustomApp(
|
|
||||||
name = "wikimedfr",
|
|
||||||
url = "http://download.kiwix.org/zim/wikipedia/wikipedia_fr_medicine_novid_2018-07.zim",
|
|
||||||
enforcedLanguage = "fr",
|
|
||||||
displayName = "Wikipédia médicale"
|
|
||||||
)
|
|
||||||
private val wikimedja = CustomApp(
|
|
||||||
name = "wikimedja",
|
|
||||||
url = "http://download.kiwix.org/zim/wikipedia/wikipedia_ja_medicine_novid_2018-07.zim",
|
|
||||||
enforcedLanguage = "ja",
|
|
||||||
displayName = "医療ウィキペディア(オフライン)"
|
|
||||||
)
|
|
||||||
private val wikimedmini = CustomApp(
|
|
||||||
name = "wikimedmini",
|
|
||||||
url = "http://download.kiwix.org/zim/wikipedia/wikipedia_en_medicine_nodet_2018-07.zim",
|
|
||||||
enforcedLanguage = "en",
|
|
||||||
displayName = "Offline WikiMed mini"
|
|
||||||
)
|
|
||||||
private val wikimedor = CustomApp(
|
|
||||||
name = "wikimedor",
|
|
||||||
url = "http://download.kiwix.org/zim/wikipedia/wikipedia_or_medicine_novid_2018-07.zim",
|
|
||||||
enforcedLanguage = "or",
|
|
||||||
displayName = "ମେଡିକାଲ ଉଇକିପିଡିଆ (ଅଫଲାଇନ)"
|
|
||||||
)
|
|
||||||
private val wikimedpt = CustomApp(
|
|
||||||
name = "wikimedpt",
|
|
||||||
url = "http://download.kiwix.org/zim/wikipedia/wikipedia_pt_medicine_2018-10.zim",
|
|
||||||
enforcedLanguage = "pt",
|
|
||||||
displayName = "Wikipédia Médica (Offline)"
|
|
||||||
)
|
|
||||||
private val wikimedzh = CustomApp(
|
|
||||||
name = "wikimedzh",
|
|
||||||
url = "http://download.kiwix.org/zim/wikipedia/wikipedia_zh_medicine_novid_2018-07.zim",
|
|
||||||
enforcedLanguage = "zh",
|
|
||||||
displayName = "醫學維基百科(離線版)"
|
|
||||||
)
|
|
||||||
private val wikispecies = CustomApp(
|
|
||||||
name = "wikispecies",
|
|
||||||
url = "http://download.kiwix.org/zim/wikispecies/wikispecies_en_all_novid_2018-08.zim",
|
|
||||||
enforcedLanguage = "en",
|
|
||||||
displayName = "WikiSpecies"
|
|
||||||
)
|
|
||||||
private val wikivoyage = CustomApp(
|
|
||||||
name = "wikivoyage",
|
|
||||||
url = "http://download.kiwix.org/zim/wikivoyage/wikivoyage_en_all_novid_2018-10.zim",
|
|
||||||
enforcedLanguage = "en",
|
|
||||||
displayName = "Wikivoyage"
|
|
||||||
)
|
|
||||||
private val wikivoyageeurope = CustomApp(
|
|
||||||
name = "wikivoyageeurope",
|
|
||||||
url = "http://download.kiwix.org/zim/wikivoyage/wikivoyage_en_europe_novid_2018-10.zim",
|
|
||||||
enforcedLanguage = "en",
|
|
||||||
displayName = "Wikivoyage European Travels"
|
|
||||||
)
|
|
||||||
private val wikivoyagede = CustomApp(
|
|
||||||
name = "wikivoyagede",
|
|
||||||
url = "http://download.kiwix.org/zim/wikivoyage/wikivoyage_de_all_novid_2018-10.zim",
|
|
||||||
enforcedLanguage = "de",
|
|
||||||
displayName = "Wikivoyage auf Deutsch"
|
|
||||||
)
|
|
||||||
private val all = listOf(
|
|
||||||
example,
|
|
||||||
phet,
|
|
||||||
tunisie,
|
|
||||||
venezuela,
|
|
||||||
wikimed,
|
|
||||||
wikimedar,
|
|
||||||
wikimedde,
|
|
||||||
wikimedes,
|
|
||||||
wikimedfa,
|
|
||||||
wikimedfr,
|
|
||||||
wikimedja,
|
|
||||||
wikimedmini,
|
|
||||||
wikimedor,
|
|
||||||
wikimedpt,
|
|
||||||
wikimedzh,
|
|
||||||
wikispecies,
|
|
||||||
wikivoyage,
|
|
||||||
wikivoyageeurope,
|
|
||||||
wikivoyagede
|
|
||||||
)
|
|
||||||
|
|
||||||
fun createStatically(productFlavors: NamedDomainObjectContainer<ProductFlavor>) {
|
fun createDynamically(srcFolder: File, productFlavors: ProductFlavors) {
|
||||||
productFlavors.create(all)
|
productFlavors.create(customApps(srcFolder))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createDynamically(
|
private fun customApps(srcFolder: File) = srcFolder.walk()
|
||||||
srcFolder: File,
|
.filter { it.name == "info.json" }
|
||||||
productFlavors: NamedDomainObjectContainer<ProductFlavor>
|
.map { CustomApp(it.parentFile.name, JSONParser().parse(it.readText()) as JSONObject) }
|
||||||
) {
|
.toList()
|
||||||
productFlavors.create(
|
}
|
||||||
srcFolder.walk()
|
|
||||||
.filter { it.name == "info.json" }
|
fun ProductFlavors.create(customApps: List<CustomApp>) {
|
||||||
.map {
|
customApps.forEach { customApp ->
|
||||||
val parsedJson = JSONParser().parse(it.readText()) as JSONObject
|
this.create(customApp.name) {
|
||||||
createCustomAppFromJson(
|
versionName = customApp.versionName
|
||||||
it.parentFile.name,
|
versionCode = customApp.versionCode
|
||||||
parsedJson.getAndCast("zim_url"),
|
applicationIdSuffix = ".kiwixcustom${customApp.name}"
|
||||||
parsedJson.getAndCast("enforced_lang"),
|
buildConfigField("String", "ZIM_URL", "\"${customApp.url}\"")
|
||||||
parsedJson.getAndCast("app_name"),
|
buildConfigField("String", "ENFORCED_LANG", "\"${customApp.enforcedLanguage}\"")
|
||||||
parsedJson.getAndCast("version_name")
|
buildConfigField("Boolean", "DISABLE_SIDEBAR", "${customApp.disableSideBar}")
|
||||||
)
|
buildConfigField("Boolean", "DISABLE_TABS", "${customApp.disableTabs}")
|
||||||
}.toList()
|
buildConfigField("Boolean", "DISABLE_READ_ALOUD", "${customApp.disableReadAloud}")
|
||||||
)
|
configureStrings(customApp.displayName)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun NamedDomainObjectContainer<ProductFlavor>.create(customApps: List<CustomApp>) {
|
|
||||||
customApps.forEach(this::create)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createCustomAppFromJson(
|
|
||||||
name: String,
|
|
||||||
url: String,
|
|
||||||
enforcedLanguage: String,
|
|
||||||
displayName: String,
|
|
||||||
versionName: String?
|
|
||||||
) = if (versionName == null) CustomApp(name, url, enforcedLanguage, displayName)
|
|
||||||
else CustomApp(name, url, enforcedLanguage, displayName, versionName)
|
|
||||||
|
|
||||||
fun <T> JSONObject.getAndCast(columnName: String): T =
|
fun <T> JSONObject.getAndCast(columnName: String): T =
|
||||||
getOrDefault(columnName, null) as T
|
getOrDefault(columnName, null) as T
|
||||||
|
|
||||||
private fun NamedDomainObjectContainer<ProductFlavor>.create(
|
|
||||||
customApp: CustomApp
|
|
||||||
) {
|
|
||||||
create(customApp.name) {
|
|
||||||
versionName = customApp.versionName
|
|
||||||
versionCode = customApp.versionCode
|
|
||||||
applicationIdSuffix = ".kiwixcustom${customApp.name}"
|
|
||||||
buildConfigField("String", "ZIM_URL", "\"${customApp.url}\"")
|
|
||||||
buildConfigField("String", "ENFORCED_LANG", "\"${customApp.enforcedLanguage}\"")
|
|
||||||
configureStrings(customApp.displayName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun ProductFlavor.configureStrings(appName: String) {
|
fun ProductFlavor.configureStrings(appName: String) {
|
||||||
resValue("string", "app_name", appName)
|
resValue("string", "app_name", appName)
|
||||||
resValue("string", "app_search_string", "Search $appName")
|
resValue("string", "app_search_string", "Search $appName")
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
<Whitelist>
|
<Whitelist>
|
||||||
<ID>EmptyFunctionBlock:BooksOnDiskViewHolder.kt$BookOnDiskViewHolder.BookViewHolder${ }</ID>
|
<ID>EmptyFunctionBlock:BooksOnDiskViewHolder.kt$BookOnDiskViewHolder.BookViewHolder${ }</ID>
|
||||||
<ID>EmptyFunctionBlock:FetchDownloadMonitor.kt$FetchDownloadMonitor.<no name provided>${}</ID>
|
<ID>EmptyFunctionBlock:FetchDownloadMonitor.kt$FetchDownloadMonitor.<no name provided>${}</ID>
|
||||||
|
<ID>LongParameterList:MainMenu.kt$MainMenu.Factory$( menu: Menu, webViews: MutableList<KiwixWebView>, urlIsValid: Boolean, menuClickListener: MenuClickListener, disableReadAloud: Boolean, disableTabs: Boolean )</ID>
|
||||||
<ID>MagicNumber:ArticleCount.kt$ArticleCount$1000.0</ID>
|
<ID>MagicNumber:ArticleCount.kt$ArticleCount$1000.0</ID>
|
||||||
<ID>MagicNumber:ArticleCount.kt$ArticleCount$3</ID>
|
<ID>MagicNumber:ArticleCount.kt$ArticleCount$3</ID>
|
||||||
<ID>MagicNumber:CoreSplashActivity.kt$CoreSplashActivity$10</ID>
|
<ID>MagicNumber:CoreSplashActivity.kt$CoreSplashActivity$10</ID>
|
||||||
|
@ -57,13 +57,17 @@ abstract class ActivityModule {
|
|||||||
menu: Menu,
|
menu: Menu,
|
||||||
webViews: MutableList<KiwixWebView>,
|
webViews: MutableList<KiwixWebView>,
|
||||||
urlIsValid: Boolean,
|
urlIsValid: Boolean,
|
||||||
menuClickListener: MenuClickListener
|
menuClickListener: MenuClickListener,
|
||||||
|
disableReadAloud: Boolean,
|
||||||
|
disableTabs: Boolean
|
||||||
): MainMenu = MainMenu(
|
): MainMenu = MainMenu(
|
||||||
activity,
|
activity,
|
||||||
zimReaderContainer.zimFileReader,
|
zimReaderContainer.zimFileReader,
|
||||||
menu,
|
menu,
|
||||||
webViews,
|
webViews,
|
||||||
urlIsValid,
|
urlIsValid,
|
||||||
|
disableReadAloud,
|
||||||
|
disableTabs,
|
||||||
menuClickListener
|
menuClickListener
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -132,12 +132,12 @@ public class CompatFindActionModeCallback
|
|||||||
throw new AssertionError("No WebView for CompatFindActionModeCallback::findAll");
|
throw new AssertionError("No WebView for CompatFindActionModeCallback::findAll");
|
||||||
}
|
}
|
||||||
CharSequence find = editText.getText();
|
CharSequence find = editText.getText();
|
||||||
if (find.length() == 0) {
|
if (find == null || find.length() == 0) {
|
||||||
webView.clearMatches();
|
webView.clearMatches();
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||||
webView.findAllAsync(null);
|
webView.findAllAsync("");
|
||||||
} else {
|
} else {
|
||||||
webView.findAll(null);
|
webView.findAll("");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||||
|
@ -175,7 +175,7 @@ public abstract class CoreMainActivity extends BaseActivity
|
|||||||
@BindView(R2.id.activity_main_fullscreen_button)
|
@BindView(R2.id.activity_main_fullscreen_button)
|
||||||
ImageButton exitFullscreenButton;
|
ImageButton exitFullscreenButton;
|
||||||
@BindView(R2.id.activity_main_drawer_layout)
|
@BindView(R2.id.activity_main_drawer_layout)
|
||||||
DrawerLayout drawerLayout;
|
protected DrawerLayout drawerLayout;
|
||||||
@BindView(R2.id.activity_main_nav_view)
|
@BindView(R2.id.activity_main_nav_view)
|
||||||
NavigationView tableDrawerRightContainer;
|
NavigationView tableDrawerRightContainer;
|
||||||
@BindView(R2.id.activity_main_content_frame)
|
@BindView(R2.id.activity_main_content_frame)
|
||||||
@ -285,7 +285,7 @@ public abstract class CoreMainActivity extends BaseActivity
|
|||||||
Menu menu = mode.getMenu();
|
Menu menu = mode.getMenu();
|
||||||
// Inflate custom menu icon.
|
// Inflate custom menu icon.
|
||||||
getMenuInflater().inflate(R.menu.menu_webview_action, menu);
|
getMenuInflater().inflate(R.menu.menu_webview_action, menu);
|
||||||
readAloudSelection(menu);
|
configureWebViewSelectionHandler(menu);
|
||||||
}
|
}
|
||||||
super.onActionModeStarted(mode);
|
super.onActionModeStarted(mode);
|
||||||
}
|
}
|
||||||
@ -296,7 +296,7 @@ public abstract class CoreMainActivity extends BaseActivity
|
|||||||
super.onActionModeFinished(mode);
|
super.onActionModeFinished(mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void readAloudSelection(Menu menu) {
|
protected void configureWebViewSelectionHandler(Menu menu) {
|
||||||
if (menu != null) {
|
if (menu != null) {
|
||||||
menu.findItem(R.id.menu_speak_text)
|
menu.findItem(R.id.menu_speak_text)
|
||||||
.setOnMenuItemClickListener(item -> {
|
.setOnMenuItemClickListener(item -> {
|
||||||
@ -525,7 +525,7 @@ public abstract class CoreMainActivity extends BaseActivity
|
|||||||
ContextCompat.getDrawable(this, R.drawable.ic_round_add_white_36dp));
|
ContextCompat.getDrawable(this, R.drawable.ic_round_add_white_36dp));
|
||||||
actionBar.setDisplayShowTitleEnabled(false);
|
actionBar.setDisplayShowTitleEnabled(false);
|
||||||
|
|
||||||
drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
|
setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
|
||||||
bottomToolbar.setVisibility(View.GONE);
|
bottomToolbar.setVisibility(View.GONE);
|
||||||
contentFrame.setVisibility(View.GONE);
|
contentFrame.setVisibility(View.GONE);
|
||||||
progressBar.setVisibility(View.GONE);
|
progressBar.setVisibility(View.GONE);
|
||||||
@ -550,7 +550,7 @@ public abstract class CoreMainActivity extends BaseActivity
|
|||||||
actionBar.setDisplayHomeAsUpEnabled(false);
|
actionBar.setDisplayHomeAsUpEnabled(false);
|
||||||
actionBar.setDisplayShowTitleEnabled(true);
|
actionBar.setDisplayShowTitleEnabled(true);
|
||||||
|
|
||||||
drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
|
setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
|
||||||
closeAllTabsButton.setImageDrawable(
|
closeAllTabsButton.setImageDrawable(
|
||||||
ContextCompat.getDrawable(this, R.drawable.ic_close_black_24dp));
|
ContextCompat.getDrawable(this, R.drawable.ic_close_black_24dp));
|
||||||
startAnimation(tabSwitcherRoot, R.anim.slide_up);
|
startAnimation(tabSwitcherRoot, R.anim.slide_up);
|
||||||
@ -563,6 +563,10 @@ public abstract class CoreMainActivity extends BaseActivity
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void setDrawerLockMode(int lockMode) {
|
||||||
|
drawerLayout.setDrawerLockMode(lockMode);
|
||||||
|
}
|
||||||
|
|
||||||
@OnClick(R2.id.bottom_toolbar_arrow_back)
|
@OnClick(R2.id.bottom_toolbar_arrow_back)
|
||||||
void goBack() {
|
void goBack() {
|
||||||
if (getCurrentWebView().canGoBack()) {
|
if (getCurrentWebView().canGoBack()) {
|
||||||
@ -1506,10 +1510,14 @@ public abstract class CoreMainActivity extends BaseActivity
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
mainMenu = menuFactory.create(menu, webViewList, !urlIsInvalid(), this);
|
mainMenu = createMainMenu(menu);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull protected MainMenu createMainMenu(Menu menu) {
|
||||||
|
return menuFactory.create(menu, webViewList, !urlIsInvalid(), this, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
protected boolean urlIsInvalid() {
|
protected boolean urlIsInvalid() {
|
||||||
return getCurrentWebView().getUrl() == null;
|
return getCurrentWebView().getUrl() == null;
|
||||||
}
|
}
|
||||||
@ -1675,28 +1683,32 @@ public abstract class CoreMainActivity extends BaseActivity
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (handleEvent) {
|
if (handleEvent) {
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
showOpenInNewTabDialog(url);
|
||||||
|
|
||||||
builder.setPositiveButton(android.R.string.yes, (dialog, id) -> {
|
|
||||||
if (isOpenNewTabInBackground) {
|
|
||||||
newTabInBackground(url);
|
|
||||||
Snackbar.make(snackbarRoot, R.string.new_tab_snack_bar, Snackbar.LENGTH_LONG)
|
|
||||||
.setAction(getString(R.string.open), v -> {
|
|
||||||
if (webViewList.size() > 1) selectTab(webViewList.size() - 1);
|
|
||||||
})
|
|
||||||
.setActionTextColor(getResources().getColor(R.color.white))
|
|
||||||
.show();
|
|
||||||
} else {
|
|
||||||
newTab(url);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
builder.setNegativeButton(android.R.string.no, null);
|
|
||||||
builder.setMessage(getString(R.string.open_in_new_tab));
|
|
||||||
AlertDialog dialog = builder.create();
|
|
||||||
dialog.show();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void showOpenInNewTabDialog(String url) {
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||||
|
|
||||||
|
builder.setPositiveButton(android.R.string.yes, (dialog, id) -> {
|
||||||
|
if (isOpenNewTabInBackground) {
|
||||||
|
newTabInBackground(url);
|
||||||
|
Snackbar.make(snackbarRoot, R.string.new_tab_snack_bar, Snackbar.LENGTH_LONG)
|
||||||
|
.setAction(getString(R.string.open), v -> {
|
||||||
|
if (webViewList.size() > 1) selectTab(webViewList.size() - 1);
|
||||||
|
})
|
||||||
|
.setActionTextColor(getResources().getColor(R.color.white))
|
||||||
|
.show();
|
||||||
|
} else {
|
||||||
|
newTab(url);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
builder.setNegativeButton(android.R.string.no, null);
|
||||||
|
builder.setMessage(getString(R.string.open_in_new_tab));
|
||||||
|
AlertDialog dialog = builder.create();
|
||||||
|
dialog.show();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setHomePage(View view) {
|
public void setHomePage(View view) {
|
||||||
getCurrentWebView().deactivateNightMode();
|
getCurrentWebView().deactivateNightMode();
|
||||||
|
@ -34,9 +34,11 @@ import java.util.ArrayList;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import org.kiwix.kiwixmobile.core.CoreApp;
|
import org.kiwix.kiwixmobile.core.CoreApp;
|
||||||
import org.kiwix.kiwixmobile.core.R;
|
import org.kiwix.kiwixmobile.core.R;
|
||||||
|
import org.kiwix.kiwixmobile.core.extensions.ContextExtensionsKt;
|
||||||
import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer;
|
import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer;
|
||||||
import org.kiwix.kiwixmobile.core.utils.LanguageUtils;
|
import org.kiwix.kiwixmobile.core.utils.LanguageUtils;
|
||||||
|
|
||||||
@ -133,6 +135,12 @@ public class KiwixTextToSpeech {
|
|||||||
} else {
|
} else {
|
||||||
tts.setLanguage(locale);
|
tts.setLanguage(locale);
|
||||||
|
|
||||||
|
if (getFeatures(tts, locale).contains(TextToSpeech.Engine.KEY_FEATURE_NOT_INSTALLED)) {
|
||||||
|
ContextExtensionsKt.toast(context, R.string.tts_lang_not_supported,
|
||||||
|
Toast.LENGTH_LONG);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (requestAudioFocus()) {
|
if (requestAudioFocus()) {
|
||||||
loadURL(webView);
|
loadURL(webView);
|
||||||
}
|
}
|
||||||
@ -140,6 +148,12 @@ public class KiwixTextToSpeech {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("NewApi")
|
||||||
|
private Set<String> getFeatures(TextToSpeech tts, Locale locale) {
|
||||||
|
return VERSION.SDK_INT < VERSION_CODES.LOLLIPOP ? tts.getFeatures(locale)
|
||||||
|
: tts.getVoice().getFeatures();
|
||||||
|
}
|
||||||
|
|
||||||
private void loadURL(WebView webView) {
|
private void loadURL(WebView webView) {
|
||||||
// We use JavaScript to get the content of the page conveniently, earlier making some
|
// We use JavaScript to get the content of the page conveniently, earlier making some
|
||||||
// changes in the page
|
// changes in the page
|
||||||
|
@ -22,6 +22,7 @@ import android.content.res.Configuration
|
|||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import androidx.core.view.isVisible
|
||||||
import org.kiwix.kiwixmobile.core.Intents.internal
|
import org.kiwix.kiwixmobile.core.Intents.internal
|
||||||
import org.kiwix.kiwixmobile.core.R
|
import org.kiwix.kiwixmobile.core.R
|
||||||
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.intent
|
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.intent
|
||||||
@ -42,6 +43,8 @@ class MainMenu(
|
|||||||
menu: Menu,
|
menu: Menu,
|
||||||
webViews: MutableList<KiwixWebView>,
|
webViews: MutableList<KiwixWebView>,
|
||||||
urlIsValid: Boolean,
|
urlIsValid: Boolean,
|
||||||
|
disableReadAloud: Boolean = false,
|
||||||
|
disableTabs: Boolean = false,
|
||||||
private val menuClickListener: MenuClickListener
|
private val menuClickListener: MenuClickListener
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@ -50,7 +53,9 @@ class MainMenu(
|
|||||||
menu: Menu,
|
menu: Menu,
|
||||||
webViews: MutableList<KiwixWebView>,
|
webViews: MutableList<KiwixWebView>,
|
||||||
urlIsValid: Boolean,
|
urlIsValid: Boolean,
|
||||||
menuClickListener: MenuClickListener
|
menuClickListener: MenuClickListener,
|
||||||
|
disableReadAloud: Boolean,
|
||||||
|
disableTabs: Boolean
|
||||||
): MainMenu
|
): MainMenu
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,29 +77,39 @@ class MainMenu(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val search = menu.findItem(R.id.menu_search)
|
private val search = menu.findItem(R.id.menu_search)
|
||||||
private val tabSwitcher = menu.findItem(R.id.menu_tab_switcher)
|
private var tabSwitcher: MenuItem? = menu.findItem(R.id.menu_tab_switcher)
|
||||||
private val tabSwitcherTextView =
|
private var tabSwitcherTextView: TextView? =
|
||||||
tabSwitcher.actionView.findViewById<TextView>(R.id.ic_tab_switcher_text)
|
tabSwitcher?.actionView?.findViewById(R.id.ic_tab_switcher_text)
|
||||||
private val bookmarks = menu.findItem(R.id.menu_bookmarks_list)
|
private val bookmarks = menu.findItem(R.id.menu_bookmarks_list)
|
||||||
private val history = menu.findItem(R.id.menu_history)
|
private val history = menu.findItem(R.id.menu_history)
|
||||||
private val library = menu.findItem(R.id.menu_openfile)
|
private val library = menu.findItem(R.id.menu_openfile)
|
||||||
private val addNote = menu.findItem(R.id.menu_add_note)
|
private val addNote = menu.findItem(R.id.menu_add_note)
|
||||||
private val randomArticle = menu.findItem(R.id.menu_random_article)
|
private val randomArticle = menu.findItem(R.id.menu_random_article)
|
||||||
private val fullscreen = menu.findItem(R.id.menu_fullscreen)
|
private val fullscreen = menu.findItem(R.id.menu_fullscreen)
|
||||||
private val readAloud = menu.findItem(R.id.menu_read_aloud)
|
private var readAloud: MenuItem? = menu.findItem(R.id.menu_read_aloud)
|
||||||
private val hostBooks = menu.findItem(R.id.menu_host_books)
|
private val hostBooks = menu.findItem(R.id.menu_host_books)
|
||||||
private val help = menu.findItem(R.id.menu_help)
|
private val help = menu.findItem(R.id.menu_help)
|
||||||
private val settings = menu.findItem(R.id.menu_settings)
|
private val settings = menu.findItem(R.id.menu_settings)
|
||||||
private val supportKiwix = menu.findItem(R.id.menu_support_kiwix)
|
private val supportKiwix = menu.findItem(R.id.menu_support_kiwix)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
if (disableReadAloud) {
|
||||||
|
readAloud?.isVisible = false
|
||||||
|
readAloud = null
|
||||||
|
}
|
||||||
|
if (disableTabs) {
|
||||||
|
tabSwitcher?.isVisible = false
|
||||||
|
tabSwitcherTextView?.isVisible = false
|
||||||
|
tabSwitcher = null
|
||||||
|
tabSwitcherTextView = null
|
||||||
|
}
|
||||||
randomArticle.setShowAsAction(
|
randomArticle.setShowAsAction(
|
||||||
if (activity.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE)
|
if (activity.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE)
|
||||||
MenuItem.SHOW_AS_ACTION_IF_ROOM
|
MenuItem.SHOW_AS_ACTION_IF_ROOM
|
||||||
else
|
else
|
||||||
MenuItem.SHOW_AS_ACTION_NEVER
|
MenuItem.SHOW_AS_ACTION_NEVER
|
||||||
)
|
)
|
||||||
tabSwitcher.actionView.setOnClickListener { menuClickListener.onTabMenuClicked() }
|
tabSwitcher?.actionView?.setOnClickListener { menuClickListener.onTabMenuClicked() }
|
||||||
help.menuItemClickListener { activity.start<HelpActivity>() }
|
help.menuItemClickListener { activity.start<HelpActivity>() }
|
||||||
settings.menuItemClickListener {
|
settings.menuItemClickListener {
|
||||||
activity.startActivityForResult(
|
activity.startActivityForResult(
|
||||||
@ -149,7 +164,7 @@ class MainMenu(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun updateTabIcon(tabs: Int) {
|
fun updateTabIcon(tabs: Int) {
|
||||||
tabSwitcherTextView.text = if (tabs > 99) ":D" else "$tabs"
|
tabSwitcherTextView?.text = if (tabs > 99) ":D" else "$tabs"
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun navigateToSearch(zimFileReader: ZimFileReader): Boolean {
|
private fun navigateToSearch(zimFileReader: ZimFileReader): Boolean {
|
||||||
@ -164,15 +179,15 @@ class MainMenu(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun onTextToSpeechStartedTalking() {
|
fun onTextToSpeechStartedTalking() {
|
||||||
readAloud.setTitle(R.string.menu_read_aloud_stop)
|
readAloud?.setTitle(R.string.menu_read_aloud_stop)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onTextToSpeechStoppedTalking() {
|
fun onTextToSpeechStoppedTalking() {
|
||||||
readAloud.setTitle(R.string.menu_read_aloud)
|
readAloud?.setTitle(R.string.menu_read_aloud)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setVisibility(visibility: Boolean, vararg menuItems: MenuItem) {
|
private fun setVisibility(visibility: Boolean, vararg menuItems: MenuItem?) {
|
||||||
menuItems.forEach { it.isVisible = visibility }
|
menuItems.forEach { it?.isVisible = visibility }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun tryExpandSearch(zimFileReader: ZimFileReader?) {
|
fun tryExpandSearch(zimFileReader: ZimFileReader?) {
|
||||||
@ -182,8 +197,8 @@ class MainMenu(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun MenuItem.menuItemClickListener(function: (MenuItem) -> Unit) {
|
private fun MenuItem?.menuItemClickListener(function: (MenuItem) -> Unit) {
|
||||||
setOnMenuItemClickListener {
|
this?.setOnMenuItemClickListener {
|
||||||
function.invoke(it)
|
function.invoke(it)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -52,8 +52,7 @@ import org.kiwix.kiwixmobile.core.search.viewmodel.Action.OnItemClick
|
|||||||
import org.kiwix.kiwixmobile.core.search.viewmodel.Action.OnItemLongClick
|
import org.kiwix.kiwixmobile.core.search.viewmodel.Action.OnItemLongClick
|
||||||
import org.kiwix.kiwixmobile.core.search.viewmodel.SearchViewModel
|
import org.kiwix.kiwixmobile.core.search.viewmodel.SearchViewModel
|
||||||
import org.kiwix.kiwixmobile.core.search.viewmodel.State
|
import org.kiwix.kiwixmobile.core.search.viewmodel.State
|
||||||
import org.kiwix.kiwixmobile.core.search.viewmodel.State.Empty
|
import org.kiwix.kiwixmobile.core.search.viewmodel.State.NoResults
|
||||||
import org.kiwix.kiwixmobile.core.search.viewmodel.State.Initialising
|
|
||||||
import org.kiwix.kiwixmobile.core.search.viewmodel.State.Results
|
import org.kiwix.kiwixmobile.core.search.viewmodel.State.Results
|
||||||
import org.kiwix.kiwixmobile.core.utils.SimpleTextListener
|
import org.kiwix.kiwixmobile.core.utils.SimpleTextListener
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -136,15 +135,19 @@ class SearchActivity : BaseActivity() {
|
|||||||
is Results -> {
|
is Results -> {
|
||||||
searchViewAnimator.setDistinctDisplayedChild(0)
|
searchViewAnimator.setDistinctDisplayedChild(0)
|
||||||
searchAdapter.items = state.values
|
searchAdapter.items = state.values
|
||||||
searchView.setQuery(state.searchString, false)
|
render(state.searchString)
|
||||||
searchInTextMenuItem.isVisible = state.searchString.isNotBlank()
|
|
||||||
}
|
}
|
||||||
Empty -> searchViewAnimator.setDistinctDisplayedChild(1)
|
is NoResults -> {
|
||||||
Initialising -> {
|
searchViewAnimator.setDistinctDisplayedChild(1)
|
||||||
// do nothing
|
render(state.searchString)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun render(searchString: String) {
|
||||||
|
searchView.setQuery(searchString, false)
|
||||||
|
searchInTextMenuItem.isEnabled = searchString.isNotBlank()
|
||||||
|
}
|
||||||
|
|
||||||
private fun onItemClick(it: SearchListItem) {
|
private fun onItemClick(it: SearchListItem) {
|
||||||
searchViewModel.actions.offer(OnItemClick(it))
|
searchViewModel.actions.offer(OnItemClick(it))
|
||||||
}
|
}
|
||||||
|
@ -42,8 +42,7 @@ import org.kiwix.kiwixmobile.core.search.viewmodel.Action.OnItemClick
|
|||||||
import org.kiwix.kiwixmobile.core.search.viewmodel.Action.OnItemLongClick
|
import org.kiwix.kiwixmobile.core.search.viewmodel.Action.OnItemLongClick
|
||||||
import org.kiwix.kiwixmobile.core.search.viewmodel.Action.ReceivedPromptForSpeechInput
|
import org.kiwix.kiwixmobile.core.search.viewmodel.Action.ReceivedPromptForSpeechInput
|
||||||
import org.kiwix.kiwixmobile.core.search.viewmodel.Action.StartSpeechInputFailed
|
import org.kiwix.kiwixmobile.core.search.viewmodel.Action.StartSpeechInputFailed
|
||||||
import org.kiwix.kiwixmobile.core.search.viewmodel.State.Empty
|
import org.kiwix.kiwixmobile.core.search.viewmodel.State.NoResults
|
||||||
import org.kiwix.kiwixmobile.core.search.viewmodel.State.Initialising
|
|
||||||
import org.kiwix.kiwixmobile.core.search.viewmodel.State.Results
|
import org.kiwix.kiwixmobile.core.search.viewmodel.State.Results
|
||||||
import org.kiwix.kiwixmobile.core.search.viewmodel.effects.DeleteRecentSearch
|
import org.kiwix.kiwixmobile.core.search.viewmodel.effects.DeleteRecentSearch
|
||||||
import org.kiwix.kiwixmobile.core.search.viewmodel.effects.Finish
|
import org.kiwix.kiwixmobile.core.search.viewmodel.effects.Finish
|
||||||
@ -64,7 +63,7 @@ class SearchViewModel @Inject constructor(
|
|||||||
private val searchResultGenerator: SearchResultGenerator
|
private val searchResultGenerator: SearchResultGenerator
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
val state = MutableLiveData<State>().apply { value = Initialising }
|
val state = MutableLiveData<State>().apply { value = NoResults("") }
|
||||||
val effects = PublishProcessor.create<SideEffect<*>>()
|
val effects = PublishProcessor.create<SideEffect<*>>()
|
||||||
val actions = PublishProcessor.create<Action>()
|
val actions = PublishProcessor.create<Action>()
|
||||||
private val filter = BehaviorProcessor.createDefault("")
|
private val filter = BehaviorProcessor.createDefault("")
|
||||||
@ -108,10 +107,7 @@ class SearchViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun searchPreviousScreenWhenStateIsValid(): Any =
|
private fun searchPreviousScreenWhenStateIsValid(): Any =
|
||||||
when (val currentState = state.value) {
|
effects.offer(SearchInPreviousScreen(state.value!!.searchString))
|
||||||
is Results -> effects.offer(SearchInPreviousScreen(currentState.searchString))
|
|
||||||
else -> Unit
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun showDeleteDialog(longClick: OnItemLongClick) {
|
private fun showDeleteDialog(longClick: OnItemLongClick) {
|
||||||
effects.offer(ShowDeleteSearchDialog(longClick.searchListItem, actions))
|
effects.offer(ShowDeleteSearchDialog(longClick.searchListItem, actions))
|
||||||
@ -144,7 +140,7 @@ class SearchViewModel @Inject constructor(
|
|||||||
Results(searchString, zimSearchResults)
|
Results(searchString, zimSearchResults)
|
||||||
searchString.isEmpty() && recentSearchResults.isNotEmpty() ->
|
searchString.isEmpty() && recentSearchResults.isNotEmpty() ->
|
||||||
Results(searchString, recentSearchResults)
|
Results(searchString, recentSearchResults)
|
||||||
else -> Empty
|
else -> NoResults(searchString)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun searchResultsFromZimReader() = filter
|
private fun searchResultsFromZimReader() = filter
|
||||||
|
@ -21,7 +21,8 @@ package org.kiwix.kiwixmobile.core.search.viewmodel
|
|||||||
import org.kiwix.kiwixmobile.core.search.adapter.SearchListItem
|
import org.kiwix.kiwixmobile.core.search.adapter.SearchListItem
|
||||||
|
|
||||||
sealed class State {
|
sealed class State {
|
||||||
data class Results(val searchString: String, val values: List<SearchListItem>) : State()
|
abstract val searchString: String
|
||||||
object Empty : State()
|
|
||||||
object Initialising : State()
|
data class Results(override val searchString: String, val values: List<SearchListItem>) : State()
|
||||||
|
data class NoResults(override val searchString: String) : State()
|
||||||
}
|
}
|
||||||
|
@ -43,15 +43,20 @@ class TagsView(context: Context, attrs: AttributeSet) : ChipGroup(context, attrs
|
|||||||
fun render(tags: List<KiwixTag>) {
|
fun render(tags: List<KiwixTag>) {
|
||||||
tag_picture.selectBy(tags.isYesOrNotDefined<PicturesTag>())
|
tag_picture.selectBy(tags.isYesOrNotDefined<PicturesTag>())
|
||||||
tag_video.selectBy(tags.isYesOrNotDefined<VideoTag>())
|
tag_video.selectBy(tags.isYesOrNotDefined<VideoTag>())
|
||||||
tag_text_only.selectBy(tags.isDefinedAndNo<PicturesTag>() && tags.isDefinedAndNo<VideoTag>())
|
val shortTextIsSelected = tags.isDefinedAndNo<DetailsTag>()
|
||||||
tag_short_text.selectBy(tags.isDefinedAndNo<DetailsTag>())
|
tag_text_only.selectBy(
|
||||||
|
tags.isDefinedAndNo<PicturesTag>() &&
|
||||||
|
tags.isDefinedAndNo<VideoTag>() &&
|
||||||
|
!shortTextIsSelected
|
||||||
|
)
|
||||||
|
tag_short_text.selectBy(shortTextIsSelected)
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline fun <reified T : YesNoValueTag> List<KiwixTag>.isYesOrNotDefined() =
|
private inline fun <reified T : YesNoValueTag> List<KiwixTag>.isYesOrNotDefined() =
|
||||||
isYes<T>() || !isDefined<T>()
|
isYes<T>() || !isDefined<T>()
|
||||||
|
|
||||||
private inline fun <reified T : YesNoValueTag> List<KiwixTag>.isDefinedAndNo() =
|
private inline fun <reified T : YesNoValueTag> List<KiwixTag>.isDefinedAndNo() =
|
||||||
!isDefined<T>() && !isYes<T>()
|
isDefined<T>() && !isYes<T>()
|
||||||
|
|
||||||
private inline fun <reified T : YesNoValueTag> List<KiwixTag>.isYes() =
|
private inline fun <reified T : YesNoValueTag> List<KiwixTag>.isYes() =
|
||||||
filterIsInstance<T>().getOrNull(0)?.value == YES
|
filterIsInstance<T>().getOrNull(0)?.value == YES
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="wrap_content">
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/file_name"
|
android:id="@+id/file_name"
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
app:backgroundColor="?colorOnSurface"
|
app:backgroundColor="?colorOnSurface"
|
||||||
app:bidirectionalAnimate="false"
|
app:bidirectionalAnimate="false"
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/toolbar"
|
app:layout_constraintBottom_toBottomOf="@+id/toolbar"
|
||||||
app:progressColor="@color/accent" />
|
app:progressColor="@color/blue400" />
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
@ -47,8 +47,7 @@ import org.kiwix.kiwixmobile.core.search.viewmodel.Action.OnItemClick
|
|||||||
import org.kiwix.kiwixmobile.core.search.viewmodel.Action.OnItemLongClick
|
import org.kiwix.kiwixmobile.core.search.viewmodel.Action.OnItemLongClick
|
||||||
import org.kiwix.kiwixmobile.core.search.viewmodel.Action.ReceivedPromptForSpeechInput
|
import org.kiwix.kiwixmobile.core.search.viewmodel.Action.ReceivedPromptForSpeechInput
|
||||||
import org.kiwix.kiwixmobile.core.search.viewmodel.Action.StartSpeechInputFailed
|
import org.kiwix.kiwixmobile.core.search.viewmodel.Action.StartSpeechInputFailed
|
||||||
import org.kiwix.kiwixmobile.core.search.viewmodel.State.Empty
|
import org.kiwix.kiwixmobile.core.search.viewmodel.State.NoResults
|
||||||
import org.kiwix.kiwixmobile.core.search.viewmodel.State.Initialising
|
|
||||||
import org.kiwix.kiwixmobile.core.search.viewmodel.State.Results
|
import org.kiwix.kiwixmobile.core.search.viewmodel.State.Results
|
||||||
import org.kiwix.kiwixmobile.core.search.viewmodel.effects.DeleteRecentSearch
|
import org.kiwix.kiwixmobile.core.search.viewmodel.effects.DeleteRecentSearch
|
||||||
import org.kiwix.kiwixmobile.core.search.viewmodel.effects.Finish
|
import org.kiwix.kiwixmobile.core.search.viewmodel.effects.Finish
|
||||||
@ -99,7 +98,7 @@ internal class SearchViewModelTest {
|
|||||||
inner class StateTests {
|
inner class StateTests {
|
||||||
@Test
|
@Test
|
||||||
fun `initial state is Initialising`() {
|
fun `initial state is Initialising`() {
|
||||||
viewModel.state.test().assertValue(Initialising)
|
viewModel.state.test().assertValue(NoResults(""))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -115,13 +114,13 @@ internal class SearchViewModelTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `non empty search string with no search results is Empty`() {
|
fun `non empty search string with no search results is NoResults`() {
|
||||||
emissionOf(
|
emissionOf(
|
||||||
searchTerm = "a",
|
searchTerm = "a",
|
||||||
searchResults = emptyList(),
|
searchResults = emptyList(),
|
||||||
databaseResults = listOf(RecentSearchListItem(""))
|
databaseResults = listOf(RecentSearchListItem(""))
|
||||||
)
|
)
|
||||||
resultsIn(Empty)
|
resultsIn(NoResults("a"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -136,13 +135,13 @@ internal class SearchViewModelTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `empty search string with no database results is Empty`() {
|
fun `empty search string with no database results is NoResults`() {
|
||||||
emissionOf(
|
emissionOf(
|
||||||
searchTerm = "",
|
searchTerm = "",
|
||||||
searchResults = listOf(ZimSearchResultListItem("")),
|
searchResults = listOf(ZimSearchResultListItem("")),
|
||||||
databaseResults = emptyList()
|
databaseResults = emptyList()
|
||||||
)
|
)
|
||||||
resultsIn(Empty)
|
resultsIn(NoResults(""))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -157,7 +156,7 @@ internal class SearchViewModelTest {
|
|||||||
viewModel.actions.offer(Filter(searchString))
|
viewModel.actions.offer(Filter(searchString))
|
||||||
viewModel.state.test()
|
viewModel.state.test()
|
||||||
.also { testScheduler.advanceTimeBy(100, MILLISECONDS) }
|
.also { testScheduler.advanceTimeBy(100, MILLISECONDS) }
|
||||||
.assertValueHistory(Initialising, Results(searchString, listOf(item)))
|
.assertValueHistory(NoResults(""), Results(searchString, listOf(item)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -175,7 +174,7 @@ internal class SearchViewModelTest {
|
|||||||
)
|
)
|
||||||
viewModel.state.test()
|
viewModel.state.test()
|
||||||
.also { testScheduler.advanceTimeBy(100, MILLISECONDS) }
|
.also { testScheduler.advanceTimeBy(100, MILLISECONDS) }
|
||||||
.assertValueHistory(Initialising, Results("b", listOf(item)))
|
.assertValueHistory(NoResults(""), Results("b", listOf(item)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,21 +205,8 @@ internal class SearchViewModelTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `ClickedSearchInText in invalid state does nothing`() {
|
fun `ClickedSearchInText offers SearchInPreviousScreen`() {
|
||||||
actionResultsInEffects(ClickedSearchInText)
|
actionResultsInEffects(ClickedSearchInText, SearchInPreviousScreen(""))
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `ClickedSearchInText in Result state offers SearchInPreviousScreen`() {
|
|
||||||
val item = ZimSearchResultListItem("")
|
|
||||||
val searchTerm = "searchTerm"
|
|
||||||
emissionOf(
|
|
||||||
searchTerm = searchTerm,
|
|
||||||
searchResults = listOf(item),
|
|
||||||
databaseResults = listOf(RecentSearchListItem(""))
|
|
||||||
)
|
|
||||||
resultsIn(Results(searchTerm, listOf(item)))
|
|
||||||
actionResultsInEffects(ClickedSearchInText, SearchInPreviousScreen(searchTerm))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -288,8 +274,7 @@ internal class SearchViewModelTest {
|
|||||||
searchResults: List<ZimSearchResultListItem>,
|
searchResults: List<ZimSearchResultListItem>,
|
||||||
databaseResults: List<RecentSearchListItem>
|
databaseResults: List<RecentSearchListItem>
|
||||||
) {
|
) {
|
||||||
val item =
|
every { searchResultGenerator.generateSearchResults(searchTerm) } returns searchResults
|
||||||
every { searchResultGenerator.generateSearchResults(searchTerm) } returns searchResults
|
|
||||||
viewModel.actions.offer(Filter(searchTerm))
|
viewModel.actions.offer(Filter(searchTerm))
|
||||||
recentsFromDb.offer(databaseResults)
|
recentsFromDb.offer(databaseResults)
|
||||||
}
|
}
|
||||||
|
@ -21,9 +21,6 @@ android {
|
|||||||
|
|
||||||
flavorDimensions("default")
|
flavorDimensions("default")
|
||||||
productFlavors {
|
productFlavors {
|
||||||
|
|
||||||
// Uncomment for static productFlavors
|
|
||||||
// CustomApps.createStatically(this)
|
|
||||||
CustomApps.createDynamically(project.file("src"), this)
|
CustomApps.createDynamically(project.file("src"), this)
|
||||||
all {
|
all {
|
||||||
File("$projectDir/src", "$name/$name.zim").let {
|
File("$projectDir/src", "$name/$name.zim").let {
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
<Whitelist>
|
<Whitelist>
|
||||||
<ID>LongParameterList:DownloadCustom.kt$DownloadCustom$( id: String = "", title: String = "", description: String = "", language: String = "", creator: String = "", publisher: String = "", date: String = "", url: String = "", articleCount: String = "", mediaCount: String = "", size: String = "", name: String = "", favIcon: String = "" )</ID>
|
<ID>LongParameterList:DownloadCustom.kt$DownloadCustom$( id: String = "", title: String = "", description: String = "", language: String = "", creator: String = "", publisher: String = "", date: String = "", url: String = "", articleCount: String = "", mediaCount: String = "", size: String = "", name: String = "", favIcon: String = "" )</ID>
|
||||||
<ID>MagicNumber:CustomDownloadActivity.kt$CustomDownloadActivity$3</ID>
|
<ID>MagicNumber:CustomDownloadActivity.kt$CustomDownloadActivity$3</ID>
|
||||||
|
<ID>MagicNumber:CustomMainActivity.kt$CustomMainActivity$.25f</ID>
|
||||||
<ID>TooGenericExceptionThrown:ActivityExtensions.kt$throw RuntimeException( """ applicationContext is ${applicationContext::class.java.simpleName} application is ${application::class.java.simpleName} """.trimIndent() )</ID>
|
<ID>TooGenericExceptionThrown:ActivityExtensions.kt$throw RuntimeException( """ applicationContext is ${applicationContext::class.java.simpleName} application is ${application::class.java.simpleName} """.trimIndent() )</ID>
|
||||||
<ID>TooManyFunctions:CustomMainActivity.kt$CustomMainActivity$CustomMainActivity</ID>
|
<ID>TooManyFunctions:CustomMainActivity.kt$CustomMainActivity$CustomMainActivity</ID>
|
||||||
</Whitelist>
|
</Whitelist>
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
{
|
{
|
||||||
"app_name": "Test Custom App",
|
"app_name": "Test Custom App",
|
||||||
"zim_url": "http://download.kiwix.org/zim/wikipedia_fr_test.zim",
|
"zim_url": "http://download.kiwix.org/zim/wikipedia_fr_test.zim",
|
||||||
"enforced_lang": "en"
|
"enforced_lang": "en",
|
||||||
|
"disable_sidebar": false,
|
||||||
|
"disable_tabs": false,
|
||||||
|
"disable_read_aloud": false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,11 +28,14 @@ import android.os.Bundle
|
|||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
|
import android.widget.ImageView
|
||||||
import androidx.core.app.ActivityCompat
|
import androidx.core.app.ActivityCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.drawerlayout.widget.DrawerLayout
|
||||||
import org.kiwix.kiwixmobile.core.di.components.CoreComponent
|
import org.kiwix.kiwixmobile.core.di.components.CoreComponent
|
||||||
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.start
|
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.start
|
||||||
import org.kiwix.kiwixmobile.core.main.CoreMainActivity
|
import org.kiwix.kiwixmobile.core.main.CoreMainActivity
|
||||||
|
import org.kiwix.kiwixmobile.core.main.MainMenu
|
||||||
import org.kiwix.kiwixmobile.core.main.WebViewCallback
|
import org.kiwix.kiwixmobile.core.main.WebViewCallback
|
||||||
import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer
|
import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer
|
||||||
import org.kiwix.kiwixmobile.core.utils.DialogShower
|
import org.kiwix.kiwixmobile.core.utils.DialogShower
|
||||||
@ -72,6 +75,19 @@ class CustomMainActivity : CoreMainActivity() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
openObbOrZim()
|
openObbOrZim()
|
||||||
|
setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
|
||||||
|
if (BuildConfig.DISABLE_SIDEBAR) {
|
||||||
|
val toolbarToc = findViewById<ImageView>(R.id.bottom_toolbar_toc)
|
||||||
|
toolbarToc.isEnabled = false
|
||||||
|
toolbarToc.alpha = .25f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setDrawerLockMode(lockMode: Int) {
|
||||||
|
super.setDrawerLockMode(
|
||||||
|
if (BuildConfig.DISABLE_SIDEBAR) DrawerLayout.LOCK_MODE_LOCKED_CLOSED
|
||||||
|
else lockMode
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(VERSION_CODES.M)
|
@TargetApi(VERSION_CODES.M)
|
||||||
@ -152,4 +168,27 @@ class CustomMainActivity : CoreMainActivity() {
|
|||||||
override fun manageZimFiles(tab: Int) {
|
override fun manageZimFiles(tab: Int) {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun createMainMenu(menu: Menu?): MainMenu {
|
||||||
|
return menuFactory.create(
|
||||||
|
menu!!,
|
||||||
|
webViewList,
|
||||||
|
!urlIsInvalid(),
|
||||||
|
this,
|
||||||
|
BuildConfig.DISABLE_READ_ALOUD,
|
||||||
|
BuildConfig.DISABLE_TABS
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showOpenInNewTabDialog(url: String?) {
|
||||||
|
if (BuildConfig.DISABLE_TABS) return
|
||||||
|
super.showOpenInNewTabDialog(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun configureWebViewSelectionHandler(menu: Menu?) {
|
||||||
|
if (BuildConfig.DISABLE_READ_ALOUD) {
|
||||||
|
menu?.findItem(org.kiwix.kiwixmobile.core.R.id.menu_speak_text)?.isVisible = false
|
||||||
|
}
|
||||||
|
super.configureWebViewSelectionHandler(menu)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user