Merge pull request #1535 from kiwix/release/3.0.4

Release/3.0.4
This commit is contained in:
Seán Mac Gillicuddy 2019-09-30 11:57:53 +01:00 committed by GitHub
commit 06dee58b1a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 153 additions and 126 deletions

View File

@ -1,3 +1,9 @@
3.0.4
BUGFIX: Some users language was causing a crash due to unstable ISO codes
BUGFIX: Some unstable zim files were crashing when opened
BUGFIX: Long titles were rendering off screen on home page
BUGFIX: Issues when opening a file externally
3.0.0
NEW: Androidx support
NEW: In app error reporting

View File

@ -53,7 +53,7 @@ dependencies {
// Get kiwixlib online if it is not populated locally
if (!shouldUseLocalVersion()) {
implementation 'org.kiwix.kiwixlib:kiwixlib:8.0.1'
implementation 'org.kiwix.kiwixlib:kiwixlib:8.1.0'
} else {
implementation 'com.getkeepsafe.relinker:relinker:1.3.1'
implementation fileTree(include: ['*.aar'], dir: 'libs')
@ -193,7 +193,7 @@ def buildNumber = System.getenv('TRAVIS_BUILD_NUMBER') ?: "dev"
ext {
versionMajor = 3
versionMinor = 0
versionPatch = 3
versionPatch = 4
}
private String generateVersionName() {

View File

@ -1,13 +1,4 @@
NEW: Androidx support
NEW: In app error reporting
NEW: Improved bookmarks
NEW: Help screen
NEW: Home page
NEW: Zim history
NEW: Introductory screen
NEW: Improved language selection
NEW: Add note to articles
NEW: Tabs
NEW: Share zim files to other Kiwix users
NEW: Host zim files on your phone
+ Bugfixes & Lots More
BUGFIX: Some users language was causing a crash due to unstable ISO codes
BUGFIX: Some unstable zim files were crashing when opened
BUGFIX: Long titles were rendering off screen on home page
BUGFIX: Issues when opening a file externally

View File

@ -19,6 +19,7 @@ package org.kiwix.kiwixmobile.di.modules;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import dagger.Module;
import dagger.Provides;
import javax.inject.Singleton;
@ -38,7 +39,12 @@ import org.kiwix.kiwixlib.JNIKiwixSearcher;
@Provides
@Singleton
@Nullable
public JNIKiwixSearcher providesJNIKiwixSearcher() {
return new JNIKiwixSearcher();
try {
return new JNIKiwixSearcher();
} catch (UnsatisfiedLinkError ignore) {
return null;
}
}
}

View File

@ -99,6 +99,7 @@ import org.kiwix.kiwixmobile.R;
import org.kiwix.kiwixmobile.base.BaseActivity;
import org.kiwix.kiwixmobile.bookmark.BookmarkItem;
import org.kiwix.kiwixmobile.bookmark.BookmarksActivity;
import org.kiwix.kiwixmobile.extensions.ContextExtensionsKt;
import org.kiwix.kiwixmobile.help.HelpActivity;
import org.kiwix.kiwixmobile.history.HistoryActivity;
import org.kiwix.kiwixmobile.history.HistoryListItem;
@ -1090,24 +1091,26 @@ public class MainActivity extends BaseActivity implements WebViewCallback,
&& Build.VERSION.SDK_INT != 23)) {
if (file.exists()) {
zimReaderContainer.setZimFile(file);
if (clearHistory) {
requestClearHistoryAfterLoad = true;
}
if (menu != null) {
initAllMenuItems();
if (zimReaderContainer.getZimFileReader() != null) {
if (clearHistory) {
requestClearHistoryAfterLoad = true;
}
if (menu != null) {
initAllMenuItems();
} else {
// Menu may not be initialized yet. In this case
// signal to menu create to show
requestInitAllMenuItems = true;
}
openMainPage();
presenter.loadCurrentZimBookmarksUrl();
} else {
// Menu may not be initialized yet. In this case
// signal to menu create to show
requestInitAllMenuItems = true;
ContextExtensionsKt.toast(this, R.string.error_file_invalid, Toast.LENGTH_LONG);
showHomePage();
}
openMainPage();
presenter.loadCurrentZimBookmarksUrl();
} else {
Log.w(TAG_KIWIX, "ZIM file doesn't exist at " + file.getAbsolutePath());
Toast.makeText(this, getResources().getString(R.string.error_file_not_found),
Toast.LENGTH_LONG)
.show();
ContextExtensionsKt.toast(this, R.string.error_file_not_found, Toast.LENGTH_LONG);
showHomePage();
}
} else {
@ -1661,7 +1664,7 @@ public class MainActivity extends BaseActivity implements WebViewCallback,
toggleActionItemsConfig();
this.menu = menu;
if (tabSwitcherRoot.getVisibility() == View.VISIBLE) {
if (tabSwitcherRoot != null && tabSwitcherRoot.getVisibility() == View.VISIBLE) {
menu.findItem(R.id.menu_search).setVisible(false);
menu.findItem(R.id.menu_fullscreen).setVisible(false);
menu.findItem(R.id.menu_random_article).setVisible(false);

View File

@ -36,6 +36,7 @@ import org.kiwix.kiwixmobile.utils.Constants.TAG_KIWIX
import org.kiwix.kiwixmobile.utils.files.FileUtils
import java.text.Collator
import java.util.Locale
import java.util.MissingResourceException
class LanguageUtils(private val context: Context) {
private val localeLanguageCodes: List<String> = languageCodesFromAssets()
@ -152,8 +153,14 @@ class LanguageUtils(private val context: Context) {
companion object {
private var localeMap =
Locale.getAvailableLocales().associateBy { it.isO3Language.toUpperCase(Locale.ROOT) }
private var isO3LanguageToLocaleMap: Map<String, Locale> =
Locale.getAvailableLocales().associateBy {
try {
it.isO3Language.toUpperCase(Locale.ROOT)
} catch (ignore: MissingResourceException) {
it.language.toUpperCase(Locale.ROOT)
}
}
private var fontExceptions = mapOf(
"km" to "fonts/KhmerOS.ttf",
@ -204,7 +211,7 @@ class LanguageUtils(private val context: Context) {
*/
@JvmStatic
fun iSO3ToLocale(iso3: String?): Locale? =
iso3?.let { localeMap[it.toUpperCase(Locale.ROOT)] }
iso3?.let { isO3LanguageToLocaleMap[it.toUpperCase(Locale.ROOT)] }
@JvmStatic
fun getCurrentLocale(context: Context) = context.locale

View File

@ -17,10 +17,12 @@
*/
package org.kiwix.kiwixmobile.utils.files
import android.annotation.TargetApi
import android.content.ContentUris
import android.content.Context
import android.net.Uri
import android.os.Build
import android.os.Build.VERSION_CODES
import android.os.Environment
import android.provider.DocumentsContract
import android.util.Log
@ -39,15 +41,15 @@ object FileUtils {
"${Environment.getExternalStorageDirectory()}${File.separator}Android" +
"${File.separator}obb${File.separator}${BuildConfig.APPLICATION_ID}"
@JvmStatic fun getFileCacheDir(context: Context): File =
@JvmStatic fun getFileCacheDir(context: Context): File? =
if (Environment.MEDIA_MOUNTED == Environment.getExternalStorageState()) {
context.externalCacheDir!!
context.externalCacheDir
} else {
context.cacheDir
}
@JvmStatic @Synchronized fun deleteCachedFiles(context: Context) {
getFileCacheDir(context).deleteRecursively()
getFileCacheDir(context)?.deleteRecursively()
}
@JvmStatic @Synchronized fun deleteZimFile(path: String) {
@ -146,12 +148,12 @@ object FileUtils {
}
@JvmStatic fun getLocalFilePathByUri(
ctx: Context,
context: Context,
uri: Uri
): String? {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT &&
DocumentsContract.isDocumentUri(ctx, uri)
DocumentsContract.isDocumentUri(context, uri)
) {
if ("com.android.externalstorage.documents" == uri.authority) {
val documentId = DocumentsContract.getDocumentId(uri)
@ -160,38 +162,49 @@ object FileUtils {
if (documentId[0] == "primary") {
return "${Environment.getExternalStorageDirectory()}/${documentId[1]}"
}
} else if ("com.android.providers.downloads.documents" == uri.authority
)
return contentQuery(
ctx,
ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"),
try {
DocumentsContract.getDocumentId(uri).toLong()
} catch (ignore: NumberFormatException) {
0L
}
)
)
} else if ("com.android.providers.downloads.documents" == uri.authority)
return try {
documentProviderContentQuery(context, uri)
} catch (ignore: IllegalArgumentException) {
null
}
} else if ("content".equals(uri.scheme!!, ignoreCase = true)) {
return contentQuery(ctx, uri)
return contentQuery(context, uri)
} else if ("file".equals(uri.scheme!!, ignoreCase = true)) {
return uri.path
}
return null
}
@TargetApi(VERSION_CODES.KITKAT)
private fun documentProviderContentQuery(context: Context, uri: Uri) =
contentQuery(
context,
ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"),
try {
DocumentsContract.getDocumentId(uri).toLong()
} catch (ignore: NumberFormatException) {
0L
}
)
)
private fun contentQuery(
context: Context,
uri: Uri
): String? {
val columnName = "_data"
return context.contentResolver.query(uri, arrayOf(columnName), null, null, null)
?.use {
if (it.moveToFirst() && it.getColumnIndex(columnName) != -1) {
it[columnName]
} else null
}
return try {
context.contentResolver.query(uri, arrayOf(columnName), null, null, null)
?.use {
if (it.moveToFirst() && it.getColumnIndex(columnName) != -1) {
it[columnName]
} else null
}
} catch (ignore: SecurityException) {
null
}
}
@JvmStatic fun readLocalesFromAssets(context: Context) =

View File

@ -26,6 +26,7 @@ import android.webkit.MimeTypeMap
import androidx.core.net.toUri
import io.reactivex.Single
import io.reactivex.schedulers.Schedulers
import org.kiwix.kiwixlib.JNIKiwixException
import org.kiwix.kiwixlib.JNIKiwixInt
import org.kiwix.kiwixlib.JNIKiwixReader
import org.kiwix.kiwixlib.JNIKiwixString
@ -51,11 +52,15 @@ class ZimFileReader(
private val sharedPreferenceUtil: SharedPreferenceUtil
) {
interface Factory {
fun create(file: File): ZimFileReader
fun create(file: File): ZimFileReader?
class Impl @Inject constructor(val sharedPreferenceUtil: SharedPreferenceUtil) : Factory {
override fun create(file: File) =
ZimFileReader(file, sharedPreferenceUtil = sharedPreferenceUtil)
try {
ZimFileReader(file, sharedPreferenceUtil = sharedPreferenceUtil)
} catch (ignore: JNIKiwixException) {
null
}
}
}

View File

@ -26,7 +26,7 @@ import javax.inject.Singleton
@Singleton
class ZimReaderContainer @Inject constructor(
private val zimFileReaderFactory: ZimFileReader.Factory,
private val jniKiwixSearcher: JNIKiwixSearcher
private val jniKiwixSearcher: JNIKiwixSearcher?
) {
private val listOfAddedReaderIds = mutableListOf<String>()
var zimFileReader: ZimFileReader? = null
@ -34,7 +34,7 @@ class ZimReaderContainer @Inject constructor(
field = value
if (value != null && !listOfAddedReaderIds.contains(value.id)) {
listOfAddedReaderIds.add(value.id)
jniKiwixSearcher.addKiwixReader(value.jniKiwixReader)
jniKiwixSearcher?.addKiwixReader(value.jniKiwixReader)
}
}
@ -60,10 +60,10 @@ class ZimReaderContainer @Inject constructor(
fun getRandomArticleUrl() = zimFileReader?.getRandomArticleUrl()
fun search(query: String, count: Int) {
jniKiwixSearcher.search(query, count)
jniKiwixSearcher?.search(query, count)
}
fun getNextResult() = jniKiwixSearcher.nextResult
fun getNextResult() = jniKiwixSearcher?.nextResult
fun isRedirect(url: String): Boolean = zimFileReader?.isRedirect(url) == true
fun getRedirect(url: String): String = zimFileReader?.getRedirect(url) ?: ""

View File

@ -1,5 +1,6 @@
package org.kiwix.kiwixmobile.zim_manager.fileselect_view
import io.reactivex.Flowable
import io.reactivex.functions.BiFunction
import io.reactivex.schedulers.Schedulers
import org.kiwix.kiwixmobile.database.newdb.dao.FetchDownloadDao
@ -16,10 +17,10 @@ class StorageObserver @Inject constructor(
private val zimReaderFactory: ZimFileReader.Factory
) {
val booksOnFileSystem
val booksOnFileSystem: Flowable<List<BookOnDisk>>
get() = scanFiles()
.withLatestFrom(downloadDao.downloads(), BiFunction(::toFilesThatAreNotDownloading))
.map { it.map(::convertToBookOnDisk) }
.map { it.mapNotNull(::convertToBookOnDisk) }
private fun scanFiles() = fileSearch.scan().subscribeOn(Schedulers.io())
@ -29,5 +30,6 @@ class StorageObserver @Inject constructor(
private fun fileHasNoMatchingDownload(downloads: List<DownloadModel>, file: File) =
downloads.firstOrNull { file.absolutePath.endsWith(it.fileNameFromUrl) } == null
private fun convertToBookOnDisk(file: File) = BookOnDisk(file, zimReaderFactory.create(file))
private fun convertToBookOnDisk(file: File) =
zimReaderFactory.create(file)?.let { BookOnDisk(file, it) }
}

View File

@ -2,14 +2,12 @@
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/get_content_card"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
android:layout_height="match_parent">
<androidx.cardview.widget.CardView
android:id="@+id/content_main_card"
@ -19,13 +17,11 @@
app:cardCornerRadius="4dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
>
app:layout_constraintTop_toTopOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
android:layout_height="wrap_content">
<ImageView
android:id="@+id/content_main_card_image"
@ -37,8 +33,7 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
app:layout_constraintTop_toTopOf="parent" />
<!-- constraintRight_toRightOf is used as android:layoutDirection is supported from API 17 -->
<androidx.appcompat.widget.AppCompatButton
@ -50,21 +45,19 @@
android:textColor="@android:color/white"
app:backgroundTint="#1565c0"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
/>
app:layout_constraintRight_toRightOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintEnd_toEndOf="@id/content_main_card"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/content_main_card"
/>
app:layout_constraintTop_toBottomOf="@id/content_main_card" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>

View File

@ -4,26 +4,34 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingEnd="0dp"
android:paddingLeft="0dp"
android:paddingRight="0dp"
android:paddingStart="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:ignore="Overdraw"
>
android:animateLayoutChanges="true"
tools:ignore="Overdraw">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/horizontal_padding"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_begin="@dimen/activity_horizontal_margin" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/vertical_padding"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_begin="@dimen/activity_vertical_margin" />
<CheckBox
android:id="@+id/itemBookCheckbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
android:paddingRight="10dp"
android:paddingEnd="10dp"
android:paddingRight="10dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintStart_toStartOf="@+id/horizontal_padding"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="RtlSymmetry"
/>
tools:ignore="RtlSymmetry" />
<ImageView
android:id="@+id/item_book_icon"
@ -33,21 +41,20 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/itemBookCheckbox"
app:layout_constraintTop_toTopOf="parent"
tools:src="@mipmap/kiwix_icon_round"
/>
tools:src="@mipmap/kiwix_icon_round" />
<TextView
android:id="@+id/item_book_title"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/activity_horizontal_margin"
android:layout_marginStart="@dimen/activity_horizontal_margin"
android:layout_marginEnd="@dimen/activity_horizontal_margin"
android:textColor="@color/textDarkPrimary"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/item_book_icon"
app:layout_constraintTop_toTopOf="parent"
tools:text="Wikipedia"
/>
app:layout_constraintTop_toBottomOf="@+id/vertical_padding"
tools:text="Wikipedia" />
<TextView
android:id="@+id/item_book_date"
@ -56,75 +63,69 @@
android:textColor="@color/textDarkSecondary"
app:layout_constraintStart_toStartOf="@id/item_book_title"
app:layout_constraintTop_toBottomOf="@id/item_book_title"
tools:text="1 Jan 2018"
/>
tools:text="1 Jan 2018" />
<TextView
android:id="@+id/item_book_size"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/activity_horizontal_margin"
android:layout_marginStart="@dimen/activity_horizontal_margin"
android:layout_marginEnd="@dimen/activity_horizontal_margin"
android:textColor="@color/textDarkSecondary"
app:layout_constraintStart_toEndOf="@id/item_book_date"
app:layout_constraintTop_toTopOf="@id/item_book_date"
tools:text="20 GB"
/>
tools:text="20 GB" />
<TextView
android:id="@+id/item_book_article_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/activity_horizontal_margin"
android:layout_marginStart="@dimen/activity_horizontal_margin"
android:layout_marginEnd="@dimen/activity_horizontal_margin"
android:textColor="@color/textDarkSecondary"
app:layout_constraintStart_toEndOf="@id/item_book_size"
app:layout_constraintTop_toTopOf="@id/item_book_size"
tools:text="10.1 K articles"
/>
tools:text="10.1 K articles" />
<TextView
android:id="@+id/item_book_description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textColor="@color/textDarkSecondary"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:textColor="@color/textDarkSecondary"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/item_book_title"
app:layout_constraintTop_toBottomOf="@id/item_book_date"
tools:text="All wikipedia articles"
/>
tools:text="All wikipedia articles" />
<TextView
android:id="@+id/item_book_label_picture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/border_label_picture"
android:paddingEnd="6dp"
android:paddingStart="6dp"
android:paddingEnd="6dp"
android:text="@string/pic"
android:textAlignment="center"
android:textColor="@color/picture_label"
app:layout_constraintStart_toStartOf="@id/item_book_title"
app:layout_constraintTop_toBottomOf="@id/item_book_description"
/>
app:layout_constraintTop_toBottomOf="@id/item_book_description" />
<TextView
android:id="@+id/item_book_label_video"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:background="@drawable/border_label_video"
android:paddingEnd="6dp"
android:paddingStart="6dp"
android:paddingEnd="6dp"
android:text="@string/vid"
android:textAlignment="center"
android:textColor="@color/video_label"
app:layout_constraintStart_toEndOf="@id/item_book_label_picture"
app:layout_constraintTop_toTopOf="@id/item_book_label_picture"
/>
app:layout_constraintTop_toTopOf="@id/item_book_label_picture" />
<View
android:layout_width="0dp"
@ -132,16 +133,16 @@
android:layout_marginTop="@dimen/activity_vertical_margin"
android:background="?android:attr/dividerVertical"
app:layout_constraintStart_toStartOf="@id/item_book_title"
app:layout_constraintTop_toBottomOf="@id/item_book_label_picture"
/>
app:layout_constraintTop_toBottomOf="@id/item_book_label_picture" />
<View
android:background="?android:attr/selectableItemBackground"
android:id="@+id/item_book_clickable_area"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
android:background="?android:attr/selectableItemBackground"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
/>
app:layout_constraintTop_toTopOf="parent"
tools:visibility="gone" />
</androidx.constraintlayout.widget.ConstraintLayout>