Merge pull request #2946 from kiwix/develop

Release 3.6.0
This commit is contained in:
Kelson 2022-08-11 12:34:49 +02:00 committed by GitHub
commit 96b032c57e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
300 changed files with 3574 additions and 1468 deletions

18
.codecov.yml Normal file
View File

@ -0,0 +1,18 @@
codecov:
notify:
require_ci_to_pass: yes
coverage:
status:
project:
default:
threshold: 1%
patch:
default:
target: 90%
threshold: 0%
ignore:
- "docs"
- "fastlane"
- "core/src/test/java/"

27
.github/move.yml vendored
View File

@ -1,27 +0,0 @@
# Configuration for Move Issues - https://github.com/dessant/move-issues
# Delete the command comment when it contains no other content
deleteCommand: true
# Close the source issue after moving
closeSourceIssue: true
# Lock the source issue after moving
lockSourceIssue: false
# Mention issue and comment authors
mentionAuthors: true
# Preserve mentions in the issue content
keepContentMentions: true
# Move labels that also exist on the target repository
moveLabels: true
# Set custom aliases for targets
# aliases:
# r: repo
# or: owner/repo
# Repository to extend settings from
# _extends: repo

View File

@ -24,6 +24,11 @@ jobs:
with:
fetch-depth: 1
- name: Set up JDK 11
uses: actions/setup-java@v1
with:
java-version: 11
- name: create instrumentation coverage
uses: ReactiveCircus/android-emulator-runner@v2.23.0
env:
@ -32,9 +37,10 @@ jobs:
api-level: ${{ matrix.api-level }}
arch: x86_64
ndk: 21.4.7075529
sdcard-path-or-size: '1000M'
disable-animations: false
script: bash contrib/instrumentation.sh
- name: Upload screenshot result
uses: actions/upload-artifact@v1
if: failure()
@ -48,8 +54,7 @@ jobs:
- name: Upload coverage to Codecov
if: ${{ matrix.api-level==21 }}
run: |
bash <(curl -s https://codecov.io/bash)
uses: codecov/codecov-action@v2
- name: Upload Coverage to GH-Actions
uses: actions/upload-artifact@v2.2.0

View File

@ -20,6 +20,11 @@ jobs:
with:
fetch-depth: 1
- name: Set up JDK 11
uses: actions/setup-java@v1
with:
java-version: 11
- name: run instrumentation tests
uses: ReactiveCircus/android-emulator-runner@v2.19.1
with:
@ -44,6 +49,11 @@ jobs:
with:
fetch-depth: 1
- name: Set up JDK 11
uses: actions/setup-java@v1
with:
java-version: 11
- name: Install NDK
run: echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;22.0.7026061" --sdk_root=${ANDROID_SDK_ROOT}
@ -71,3 +81,10 @@ jobs:
cp $UNIVERSAL_DEBUG_APK $DATE
scp -P 30022 -vrp -i ssh_key -o StrictHostKeyChecking=no $DATE ci@master.download.kiwix.org:/data/download/nightly/
- name: fdroid nightly
run: |
sudo add-apt-repository ppa:fdroid/fdroidserver
sudo apt-get update
sudo apt-get install apksigner fdroidserver --no-install-recommends
export DEBUG_KEYSTORE=$\{\{ secrets.DEBUG_KEYSTORE \}\}
fdroid nightly --archive-older 10

View File

@ -13,6 +13,11 @@ jobs:
with:
fetch-depth: 1
- name: Set up JDK 11
uses: actions/setup-java@v1
with:
java-version: 11
- name: Static Analysis
run: ./gradlew ktlintCheck detekt app:lintDebug custom:lintCustomexampleDebug
@ -34,6 +39,11 @@ jobs:
with:
fetch-depth: 1
- name: Set up JDK 11
uses: actions/setup-java@v1
with:
java-version: 11
- name: Install NDK
run: echo "y" | sudo ${ANDROID_HOME}/tools/bin/sdkmanager --install "ndk;22.0.7026061" --sdk_root=${ANDROID_SDK_ROOT}

View File

@ -17,6 +17,11 @@ jobs:
steps:
- uses: actions/checkout@v1
- name: Set up JDK 11
uses: actions/setup-java@v1
with:
java-version: 11
- name: Decrypt files
env:
keystore: ${{ secrets.keystore }}
@ -42,13 +47,18 @@ jobs:
cp $UNIVERSAL_RELEASE_APK kiwix-${TAG}.apk
scp -P 30022 -vrp -i ssh_key -o StrictHostKeyChecking=no kiwix-${TAG}.apk ci@master.download.kiwix.org:/data/download/release/kiwix-android/
- name: Publish to github releases
uses: ncipollo/release-action@v1
with:
artifacts: "app/build/outputs/apk/release/**"
token: ${{ secrets.GITHUB_TOKEN }}
draft: true
prerelease: true
- name: Publish versionInfo to download.kiwix.org
run: |
./gradlew generateVersionCodeAndName
scp -P 30022 -vrp -i ssh_key -o StrictHostKeyChecking=no VERSION_INFO ci@master.download.kiwix.org:/data/download/release/kiwix-android/
- name: Publish to github releases
uses: ncipollo/release-action@v1
with:
artifacts: "app/build/outputs/apk/release/**"
token: ${{ secrets.GITHUB_TOKEN }}
draft: true
prerelease: true
- name: Publish app to play store
@ -56,4 +66,4 @@ jobs:
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
KEY_STORE_PASSWORD: ${{ secrets.KEY_STORE_PASSWORD }}
run: ./gradlew publishReleaseBundle
run: ./gradlew publishPlayStoreBundle

View File

@ -1,3 +1,19 @@
3.6.0
* FIX: Upgrade to Kotlin 1.7.0.
* FIX: Introduce google play build variant.
* FIX: Upgrade project environment from java 8 to java 11.
* FIX: Upgrade detekt lib to 1.20.0.
* FIX: Upgrade klint lib to 10.3.0.
* FIX: Google play publisher upgrade to 3.7.0.
* FIX: Pdf/Epub files opening issue fixes.
* FIX: Same pictures are saved multiple times.
* FIX: Introductory screen hangup issue fixed.
* FIX: Search Widget navigation issue fixed.
* FIX: Search screen were missing while pressing back fixed.
* FIX: Unsatisfied Link error fixed.
* FIX: IllegalStateException report in core reader fragment fixed.
3.5.0
* FIX: Storing Zim/Book files inside the public Kiwix Directory on internal storage.
* FIX: Storing Zim/Book files inside the public Kiwix Directory on external storage in android 11 and above.

View File

@ -8,10 +8,10 @@
# Kiwix Android
Kiwix is an offline reader for Web content. One of its main purpose
Kiwix is an offline reader for Web content. One of its main purposes
is to make Wikipedia available offline. This is done by reading the
content of a file in the ZIM format, a highly compressed open format
with additional meta-data. This is the version for Android.
with additional metadata. This is the version for Android.
Kiwix Android is written in [Kotlin](https://kotlinlang.org/) (with a few old
pieces in Java).

2
VERSION_INFO Normal file
View File

@ -0,0 +1,2 @@
3.6.0
7230600

View File

@ -10,7 +10,7 @@ apply(from = rootProject.file("jacoco.gradle"))
ext {
set("versionMajor", 3)
set("versionMinor", 5)
set("versionMinor", 6)
set("versionPatch", 0)
}
@ -30,8 +30,8 @@ fun generateVersionName() = "${ext["versionMajor"]}.${ext["versionMinor"]}.${ext
fun generateVersionCode() =
20 * 10000 +
((ext["versionMajor"] as Int) * 10000) +
((ext["versionMinor"] as Int) * 100) +
ext["versionMajor"] as Int * 10000 +
ext["versionMinor"] as Int * 100 +
ext["versionPatch"] as Int
val apkPrefix get() = System.getenv("TAG") ?: "dev"
@ -44,8 +44,8 @@ android {
resValue("string", "app_search_string", "Search Kiwix")
versionCode = generateVersionCode()
versionName = generateVersionName()
manifestPlaceholders["permission"] = "android.permission.MANAGE_EXTERNAL_STORAGE"
}
lintOptions {
isCheckDependencies = true
}
@ -54,14 +54,22 @@ android {
getByName("debug") {
multiDexKeepProguard = file("multidex-instrumentation-config.pro")
buildConfigField("boolean", "KIWIX_ERROR_ACTIVITY", "false")
buildConfigField("boolean", "IS_PLAYSTORE", "false")
}
getByName("release") {
buildConfigField("boolean", "KIWIX_ERROR_ACTIVITY", "true")
buildConfigField("boolean", "IS_PLAYSTORE", "false")
if (properties.containsKey("disableSigning")) {
signingConfig = null
}
}
create("playStore") {
initWith(getByName("release"))
setMatchingFallbacks("release")
buildConfigField("boolean", "IS_PLAYSTORE", "true")
manifestPlaceholders["permission"] = "android.permission.placeholder"
}
}
bundle {
language {
@ -78,13 +86,23 @@ android {
}
play {
isEnabled = true
serviceAccountCredentials = file("../google.json")
track = "alpha"
releaseStatus = "draft"
resolutionStrategy = "fail"
enabled.set(true)
serviceAccountCredentials.set(file("../google.json"))
track.set("alpha")
releaseStatus.set(com.github.triplet.gradle.androidpublisher.ReleaseStatus.DRAFT)
resolutionStrategy.set(com.github.triplet.gradle.androidpublisher.ResolutionStrategy.FAIL)
}
dependencies {
implementation(Libs.squidb)
}
task("generateVersionCodeAndName") {
val file = File("VERSION_INFO")
if (!file.exists()) file.createNewFile()
file.printWriter().use {
it.print(
"${generateVersionName()}\n" +
"7${generateVersionCode()}"
)
}
}

View File

@ -1,42 +1,52 @@
<?xml version="1.0" ?>
<?xml version='1.0' encoding='UTF-8'?>
<SmellBaseline>
<Blacklist></Blacklist>
<Whitelist>
<ManuallySuppressedIssues/>
<CurrentIssues>
<ID>EmptyFunctionBlock:None.kt$None${ }</ID>
<ID>EmptyFunctionBlock:SimplePageChangeListener.kt$SimplePageChangeListener${ }</ID>
<ID>LongParameterList:ZimManageViewModel.kt$ZimManageViewModel$( booksOnFileSystem: List&lt;BookOnDisk&gt;, activeDownloads: List&lt;DownloadModel&gt;, allLanguages: List&lt;Language&gt;, libraryNetworkEntity: LibraryNetworkEntity, filter: String, fileSystemState: FileSystemState )</ID>
<ID>LongParameterList:ZimManageViewModel.kt$ZimManageViewModel$( booksOnFileSystem: List&lt;BookOnDisk>, activeDownloads: List&lt;DownloadModel>, allLanguages: List&lt;Language>, libraryNetworkEntity: LibraryNetworkEntity, filter: String, fileSystemState: FileSystemState )</ID>
<ID>LongParameterList:ZimManageViewModel.kt$ZimManageViewModel$( private val downloadDao: FetchDownloadDao, private val bookDao: NewBookDao, private val languageDao: NewLanguagesDao, private val storageObserver: StorageObserver, private val kiwixService: KiwixService, private val context: Application, private val connectivityBroadcastReceiver: ConnectivityBroadcastReceiver, private val bookUtils: BookUtils, private val fat32Checker: Fat32Checker, private val defaultLanguageProvider: DefaultLanguageProvider, private val dataSource: DataSource, private val connectivityManager: ConnectivityManager, private val sharedPreferenceUtil: SharedPreferenceUtil )</ID>
<ID>MagicNumber:LibraryListItem.kt$LibraryListItem.LibraryDownloadItem$1000L</ID>
<ID>MagicNumber:PeerGroupHandshake.kt$PeerGroupHandshake$15000</ID>
<ID>MagicNumber:ShareFiles.kt$ShareFiles$24</ID>
<ID>MagicNumber:ZimManageViewModel.kt$ZimManageViewModel$5</ID>
<ID>MagicNumber:ZimManageViewModel.kt$ZimManageViewModel$500</ID>
<ID>NestedBlockDepth:LocalLibraryFragment.kt$LocalLibraryFragment$checkPermissions</ID>
<ID>NestedBlockDepth:PeerGroupHandshake.kt$PeerGroupHandshake$readHandshakeAndExchangeMetaData</ID>
<ID>NestedBlockDepth:ReceiverHandShake.kt$ReceiverHandShake$exchangeFileTransferMetadata</ID>
<ID>PackageNaming:AvailableSpaceCalculator.kt$package org.kiwix.kiwixmobile.zim_manager.library_view</ID>
<ID>PackageNaming:ConnectivityBroadcastReceiver.kt$package org.kiwix.kiwixmobile.zim_manager</ID>
<ID>PackageNaming:DefaultLanguageProvider.kt$package org.kiwix.kiwixmobile.zim_manager</ID>
<ID>PackageNaming:DeleteFiles.kt$package org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects</ID>
<ID>PackageNaming:Fat32Checker.kt$package org.kiwix.kiwixmobile.zim_manager</ID>
<ID>PackageNaming:FileSelectListState.kt$package org.kiwix.kiwixmobile.zim_manager.fileselect_view</ID>
<ID>PackageNaming:FileSystemChecker.kt$package org.kiwix.kiwixmobile.zim_manager</ID>
<ID>PackageNaming:FileWritingFileSystemChecker.kt$package org.kiwix.kiwixmobile.zim_manager</ID>
<ID>NestedBlockDepth:LocalLibraryFragment.kt$LocalLibraryFragment$private fun checkPermissions()</ID>
<ID>NestedBlockDepth:PeerGroupHandshake.kt$PeerGroupHandshake$private fun readHandshakeAndExchangeMetaData(): InetAddress?</ID>
<ID>NestedBlockDepth:ReceiverHandShake.kt$ReceiverHandShake$override fun exchangeFileTransferMetadata(inputStream: InputStream, outputStream: OutputStream)</ID>
<ID>PackageNaming:AvailableSpaceCalculator.kt$package
org.kiwix.kiwixmobile.zimManager.libraryView</ID>
<ID>PackageNaming:ConnectivityBroadcastReceiver.kt$package org.kiwix.kiwixmobile.zimManager</ID>
<ID>PackageNaming:DefaultLanguageProvider.kt$package org.kiwix.kiwixmobile.zimManager</ID>
<ID>PackageNaming:DeleteFiles.kt$package
org.kiwix.kiwixmobile.zimManager.fileselectView.effects</ID>
<ID>PackageNaming:Fat32Checker.kt$package org.kiwix.kiwixmobile.zimManager</ID>
<ID>PackageNaming:FileSelectListState.kt$package
org.kiwix.kiwixmobile.zimManager.fileselectView</ID>
<ID>PackageNaming:FileSystemChecker.kt$package org.kiwix.kiwixmobile.zimManager</ID>
<ID>PackageNaming:FileWritingFileSystemChecker.kt$package org.kiwix.kiwixmobile.zimManager</ID>
<ID>PackageNaming:HotspotNotificationManager.kt$package org.kiwix.kiwixmobile.webserver.wifi_hotspot</ID>
<ID>PackageNaming:HotspotStateReceiver.kt$package org.kiwix.kiwixmobile.webserver.wifi_hotspot</ID>
<ID>PackageNaming:LibraryAdapter.kt$package org.kiwix.kiwixmobile.zim_manager.library_view.adapter</ID>
<ID>PackageNaming:LibraryDelegate.kt$package org.kiwix.kiwixmobile.zim_manager.library_view.adapter</ID>
<ID>PackageNaming:LibraryListItem.kt$package org.kiwix.kiwixmobile.zim_manager.library_view.adapter</ID>
<ID>PackageNaming:LibraryViewHolder.kt$package org.kiwix.kiwixmobile.zim_manager.library_view.adapter</ID>
<ID>PackageNaming:MountFileSystemChecker.kt$package org.kiwix.kiwixmobile.zim_manager</ID>
<ID>PackageNaming:NavigateToDownloads.kt$package org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects</ID>
<ID>PackageNaming:NetworkState.kt$package org.kiwix.kiwixmobile.zim_manager</ID>
<ID>PackageNaming:None.kt$package org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects</ID>
<ID>PackageNaming:OpenFileWithNavigation.kt$package org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects</ID>
<ID>PackageNaming:ShareFiles.kt$package org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects</ID>
<ID>PackageNaming:SimplePageChangeListener.kt$package org.kiwix.kiwixmobile.zim_manager</ID>
<ID>PackageNaming:StartMultiSelection.kt$package org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects</ID>
<ID>PackageNaming:ZimManageViewModel.kt$package org.kiwix.kiwixmobile.zim_manager</ID>
<ID>PackageNaming:LibraryAdapter.kt$package
org.kiwix.kiwixmobile.zimManager.libraryView.adapter</ID>
<ID>PackageNaming:LibraryDelegate.kt$package
org.kiwix.kiwixmobile.zimManager.libraryView.adapter</ID>
<ID>PackageNaming:LibraryListItem.kt$package
org.kiwix.kiwixmobile.zimManager.libraryView.adapter</ID>
<ID>PackageNaming:LibraryViewHolder.kt$package
org.kiwix.kiwixmobile.zimManager.libraryView.adapter</ID>
<ID>PackageNaming:MountFileSystemChecker.kt$package org.kiwix.kiwixmobile.zimManager</ID>
<ID>PackageNaming:NavigateToDownloads.kt$package
org.kiwix.kiwixmobile.zimManager.fileselectView.effects</ID>
<ID>PackageNaming:NetworkState.kt$package org.kiwix.kiwixmobile.zimManager</ID>
<ID>PackageNaming:None.kt$package org.kiwix.kiwixmobile.zimManager.fileselectView.effects</ID>
<ID>PackageNaming:OpenFileWithNavigation.kt$package
org.kiwix.kiwixmobile.zimManager.fileselectView.effects</ID>
<ID>PackageNaming:ShareFiles.kt$package org.kiwix.kiwixmobile.zimManager.fileselectView.effects</ID>
<ID>PackageNaming:SimplePageChangeListener.kt$package org.kiwix.kiwixmobile.zimManager</ID>
<ID>PackageNaming:StartMultiSelection.kt$package
org.kiwix.kiwixmobile.zimManager.fileselectView.effects</ID>
<ID>PackageNaming:ZimManageViewModel.kt$package org.kiwix.kiwixmobile.zimManager</ID>
<ID>ReturnCount:Fat32Checker.kt$Fat32Checker$private fun canCreate4GbFile(storage: String): Boolean</ID>
<ID>TooGenericExceptionCaught:FileWritingFileSystemChecker.kt$FileWritingFileSystemChecker$e: Exception</ID>
<ID>TooGenericExceptionCaught:PeerGroupHandshake.kt$PeerGroupHandshake$ex: Exception</ID>
@ -46,5 +56,5 @@
<ID>TooGenericExceptionThrown:LibraryViewHolder.kt$LibraryViewHolder.LibraryBookViewHolder$throw RuntimeException("impossible invalid state: ${item.fileSystemState}")</ID>
<ID>TooGenericExceptionThrown:ZimManageViewModel.kt$ZimManageViewModel$throw RuntimeException("Impossible state")</ID>
<ID>VariableNaming:PeerGroupHandshake.kt$PeerGroupHandshake$private val HANDSHAKE_MESSAGE = "Request Kiwix File Sharing"</ID>
</Whitelist>
</CurrentIssues>
</SmellBaseline>

View File

@ -108,4 +108,4 @@ private fun resourceId(view: View) =
if (view.id > 0 && view.resources != null) " id:${view.resources.getResourceName(view.id)}"
else ""
private fun numSpaces(marginOffset: Int) = (0..marginOffset).fold("", { acc, _ -> "$acc-" })
private fun numSpaces(marginOffset: Int) = (0..marginOffset).fold("") { acc, _ -> "$acc-" }

View File

@ -48,7 +48,7 @@ abstract class BaseActivityTest {
getInstrumentation().targetContext.applicationContext
}
inline fun <reified T : Activity> activityTestRule(
protected inline fun <reified T : Activity> activityTestRule(
noinline beforeActivityAction: (() -> Unit)? = null
) =
object : ActivityTestRule<T>(T::class.java) {

View File

@ -70,14 +70,17 @@ public class KiwixDatabaseTest {
kiwixDatabase.migrateBookmarksVersion6();
ArrayList<String> bookmarkTitles = new ArrayList<>();
try (SquidCursor<Bookmark> bookmarkCursor = kiwixDatabase.query(Bookmark.class,
Query.selectDistinct(Bookmark.BOOKMARK_TITLE)
.where(Bookmark.ZIM_ID.eq(testId)
.or(Bookmark.ZIM_NAME.eq("")))
.orderBy(Bookmark.BOOKMARK_TITLE.asc()))) {
try {
SquidCursor<Bookmark> bookmarkCursor = kiwixDatabase.query(Bookmark.class,
Query.selectDistinct(Bookmark.BOOKMARK_TITLE)
.where(Bookmark.ZIM_ID.eq(testId)
.or(Bookmark.ZIM_NAME.eq("")))
.orderBy(Bookmark.BOOKMARK_TITLE.asc()));
while (bookmarkCursor.moveToNext()) {
bookmarkTitles.add(bookmarkCursor.get(Bookmark.BOOKMARK_TITLE));
}
} catch (Exception exception) {
exception.printStackTrace();
}
assertArrayEquals(testBookmarks, bookmarkTitles.toArray());

View File

@ -20,7 +20,6 @@ package org.kiwix.kiwixmobile.data.local.dao
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.yahoo.squidb.data.AbstractModel
import com.yahoo.squidb.data.SquidCursor
import com.yahoo.squidb.sql.Query
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
@ -37,7 +36,7 @@ class RecentSearchDaoTest {
every {
kiwixDatabase.query(any<Class<AbstractModel>>(), any())
} returns mockk<SquidCursor<AbstractModel>>(relaxed = true)
RecentSearchDao(kiwixDatabase).recentSearches
verify { kiwixDatabase.query(any<Class<AbstractModel>>(), Query.select()) }
RecentSearchDao(kiwixDatabase).getRecentSearches()
verify { kiwixDatabase.query(any<Class<AbstractModel>>(), any()) }
}
}

View File

@ -17,19 +17,75 @@
*/
package org.kiwix.kiwixmobile.search
import androidx.core.content.edit
import androidx.core.net.toUri
import androidx.preference.PreferenceManager
import androidx.test.internal.runner.junit4.statement.UiThreadStatement
import com.adevinta.android.barista.assertion.BaristaVisibilityAssertions.assertDisplayed
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.rule.ActivityTestRule
import androidx.test.uiautomator.UiDevice
import org.junit.Before
import org.junit.Test
import org.kiwix.kiwixmobile.BaseActivityTest
import org.kiwix.kiwixmobile.R
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
import org.kiwix.kiwixmobile.main.KiwixMainActivity
import org.kiwix.kiwixmobile.nav.destination.library.LocalLibraryFragmentDirections.actionNavigationLibraryToNavigationReader
import java.io.File
import java.io.FileOutputStream
import java.io.OutputStream
class SearchFragmentTest : BaseActivityTest() {
@Before fun setUp() {
UiThreadStatement.runOnUiThread { activityRule.activity.navigate(R.id.searchFragment) }
override var activityRule: ActivityTestRule<KiwixMainActivity> = activityTestRule {
PreferenceManager.getDefaultSharedPreferences(context).edit {
putBoolean(SharedPreferenceUtil.PREF_SHOW_INTRO, false)
putBoolean(SharedPreferenceUtil.PREF_WIFI_ONLY, false)
}
}
@Before
fun waitForIdle() {
UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()).waitForIdle()
}
@Test fun searchFragmentSimple() {
assertDisplayed(R.string.menu_search_in_text)
UiThreadStatement.runOnUiThread { activityRule.activity.navigate(R.id.libraryFragment) }
val loadFileStream =
SearchFragmentTest::class.java.classLoader.getResourceAsStream("testzim.zim")
val zimFile = File(context.cacheDir, "testzim.zim")
if (zimFile.exists()) zimFile.delete()
zimFile.createNewFile()
loadFileStream.use { inputStream ->
val outputStream: OutputStream = FileOutputStream(zimFile)
outputStream.use { it ->
val buffer = ByteArray(inputStream.available())
var length: Int
while (inputStream.read(buffer).also { length = it } > 0) {
it.write(buffer, 0, length)
}
}
}
UiThreadStatement.runOnUiThread {
activityRule.activity.navigate(
actionNavigationLibraryToNavigationReader()
.apply { zimFileUri = zimFile.toUri().toString() }
)
}
search { checkZimFileSearchSuccessful(R.id.readerFragment) }
UiThreadStatement.runOnUiThread {
if (zimFile.canRead()) {
activityRule.activity.openSearch(searchString = "Android")
} else {
throw RuntimeException(
"File $zimFile is not readable." +
" Original File $zimFile is readable = ${zimFile.canRead()}" +
" Size ${zimFile.length()}"
)
}
}
search {
clickOnSearchItemInSearchList()
checkZimFileSearchSuccessful(R.id.readerFragment)
}
}
}

View File

@ -0,0 +1,42 @@
/*
* Kiwix Android
* Copyright (c) 2022 Kiwix <android.kiwix.org>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.kiwix.kiwixmobile.search
import applyWithViewHierarchyPrinting
import com.adevinta.android.barista.interaction.BaristaSleepInteractions
import org.kiwix.kiwixmobile.BaseRobot
import org.kiwix.kiwixmobile.Findable.ViewId
import org.kiwix.kiwixmobile.core.R
import org.kiwix.kiwixmobile.testutils.TestUtils
fun search(func: SearchRobot.() -> Unit) = SearchRobot().applyWithViewHierarchyPrinting(func)
class SearchRobot : BaseRobot() {
fun clickOnSearchItemInSearchList() {
BaristaSleepInteractions.sleep(TestUtils.TEST_PAUSE_MS_FOR_SEARCH_TEST.toLong())
isVisible(ViewId(R.id.search_list))
clickOn(ViewId(R.id.list_item_search_text))
}
fun checkZimFileSearchSuccessful(readerFragment: Int) {
BaristaSleepInteractions.sleep(TestUtils.TEST_PAUSE_MS_FOR_SEARCH_TEST.toLong())
isVisible(ViewId(readerFragment))
}
}

View File

@ -40,8 +40,8 @@ class Matcher {
public override fun matchesSafely(view: View): Boolean {
val parent = view.parent
return parent is ViewGroup && parentMatcher.matches(parent) && view == parent.getChildAt(
position
)
position
)
}
}
}

View File

@ -49,6 +49,7 @@ import org.kiwix.kiwixmobile.core.entity.LibraryNetworkEntity.Book;
public class TestUtils {
private static final String TAG = "TESTUTILS";
public static int TEST_PAUSE_MS = 250;
public static int TEST_PAUSE_MS_FOR_SEARCH_TEST = 1000;
/*
TEST_PAUSE_MS is used as such:
BaristaSleepInteractions.sleep(TEST_PAUSE_MS);
@ -143,4 +144,3 @@ public class TestUtils {
return targetContext.getResources().getString(id);
}
}

View File

@ -1,70 +0,0 @@
/*
* Kiwix Android
* Copyright (c) 2019 Kiwix <android.kiwix.org>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.kiwix.kiwixmobile.utils;
import androidx.test.espresso.IdlingResource;
import org.kiwix.kiwixmobile.core.utils.TestingUtils;
import org.kiwix.kiwixmobile.core.utils.TestingUtils.IdleListener;
/**
* Created by mhutti1 on 19/04/17.
*/
public class KiwixIdlingResource implements IdlingResource, IdleListener {
private static KiwixIdlingResource kiwixIdlingResource;
private boolean idle = true;
private ResourceCallback resourceCallback;
public static KiwixIdlingResource getInstance() {
if (kiwixIdlingResource == null) {
kiwixIdlingResource = new KiwixIdlingResource();
}
kiwixIdlingResource.idle = true;
TestingUtils.registerIdleCallback(kiwixIdlingResource);
return kiwixIdlingResource;
}
@Override
public String getName() {
return "Standard Kiwix Idling Resource";
}
@Override
public boolean isIdleNow() {
return idle;
}
@Override
public void registerIdleTransitionCallback(ResourceCallback callback) {
this.resourceCallback = callback;
}
@Override
public void startTask() {
idle = false;
}
@Override
public void finishTask() {
idle = true;
if (resourceCallback != null) {
resourceCallback.onTransitionToIdle();
}
}
}

View File

@ -0,0 +1,67 @@
/*
* Kiwix Android
* Copyright (c) 2019 Kiwix <android.kiwix.org>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.kiwix.kiwixmobile.utils
import androidx.test.espresso.IdlingResource
import androidx.test.espresso.IdlingResource.ResourceCallback
import org.kiwix.kiwixmobile.core.utils.TestingUtils
import org.kiwix.kiwixmobile.core.utils.TestingUtils.IdleListener
/**
* Created by mhutti1 on 19/04/17.
*/
class KiwixIdlingResource : IdlingResource, IdleListener {
private var idle = true
private var resourceCallback: ResourceCallback? = null
override fun getName(): String = "Standard Kiwix Idling Resource"
override fun isIdleNow(): Boolean = idle
override fun registerIdleTransitionCallback(callback: ResourceCallback) {
resourceCallback = callback
}
override fun startTask() {
idle = false
}
override fun finishTask() {
idle = true
if (resourceCallback != null) {
resourceCallback?.onTransitionToIdle()
}
}
companion object {
private var kiwixIdlingResource: KiwixIdlingResource? = null
@JvmStatic
fun getInstance(): KiwixIdlingResource? {
if (kiwixIdlingResource == null) {
kiwixIdlingResource = KiwixIdlingResource()
}
if (kiwixIdlingResource != null) {
kiwixIdlingResource!!.idle = true
}
TestingUtils.registerIdleCallback(kiwixIdlingResource)
return kiwixIdlingResource
}
}
}

View File

@ -16,7 +16,7 @@
// *
// */
//
package org.kiwix.kiwixmobile.zim_manager
package org.kiwix.kiwixmobile.zimManager
//
// import androidx.test.filters.SdkSuppress
// import attempt

View File

@ -15,7 +15,7 @@
// * along with this program. If not, see <http://www.gnu.org/licenses/>.
// *
// */
package org.kiwix.kiwixmobile.zim_manager
package org.kiwix.kiwixmobile.zimManager
//
// import applyWithViewHierarchyPrinting
// import org.kiwix.kiwixmobile.BaseRobot

View File

@ -4,10 +4,15 @@
package="org.kiwix.kiwixmobile">
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission
android:name="android.permission.ACCESS_FINE_LOCATION"
android:maxSdkVersion="32" />
<uses-permission
android:name="android.permission.NEARBY_WIFI_DEVICES"
android:usesPermissionFlags="neverForLocation"
tools:targetApi="s" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" />
<uses-permission android:name="${permission}" />
<queries>
<intent>
@ -29,6 +34,7 @@
<activity
android:name=".main.KiwixMainActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:exported="true"
android:label="@string/app_name"
android:launchMode="singleTop"
android:theme="@style/KiwixTheme.Launcher"
@ -145,7 +151,9 @@
<service android:name=".webserver.wifi_hotspot.HotspotService" />
<receiver android:name=".main.KiwixSearchWidget">
<receiver
android:name=".main.KiwixSearchWidget"
android:exported="true">
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/kiwix_widget_provider_info" />

View File

@ -19,9 +19,9 @@
package org.kiwix.kiwixmobile
import android.net.ConnectivityManager
import org.kiwix.kiwixmobile.zim_manager.NetworkState
import org.kiwix.kiwixmobile.zim_manager.NetworkState.CONNECTED
import org.kiwix.kiwixmobile.zim_manager.NetworkState.NOT_CONNECTED
import org.kiwix.kiwixmobile.zimManager.NetworkState
import org.kiwix.kiwixmobile.zimManager.NetworkState.CONNECTED
import org.kiwix.kiwixmobile.zimManager.NetworkState.NOT_CONNECTED
val ConnectivityManager.networkState: NetworkState
get() = if (activeNetworkInfo?.isConnected == true)

View File

@ -34,7 +34,7 @@ import org.kiwix.kiwixmobile.nav.destination.reader.KiwixReaderFragment
import org.kiwix.kiwixmobile.settings.KiwixSettingsFragment
import org.kiwix.kiwixmobile.webserver.ZimHostFragment
import org.kiwix.kiwixmobile.webserver.ZimHostModule
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects.DeleteFiles
import org.kiwix.kiwixmobile.zimManager.fileselectView.effects.DeleteFiles
@ActivityScope
@Subcomponent(

View File

@ -27,9 +27,9 @@ import dagger.Provides
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
import org.kiwix.kiwixmobile.core.zim_manager.MountPointProducer
import org.kiwix.kiwixmobile.di.KiwixScope
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker
import org.kiwix.kiwixmobile.zim_manager.FileWritingFileSystemChecker
import org.kiwix.kiwixmobile.zim_manager.MountFileSystemChecker
import org.kiwix.kiwixmobile.zimManager.Fat32Checker
import org.kiwix.kiwixmobile.zimManager.FileWritingFileSystemChecker
import org.kiwix.kiwixmobile.zimManager.MountFileSystemChecker
@Module
object KiwixModule {

View File

@ -25,7 +25,7 @@ import dagger.multibindings.IntoMap
import org.kiwix.kiwixmobile.core.di.ViewModelKey
import org.kiwix.kiwixmobile.core.di.modules.CoreViewModelModule
import org.kiwix.kiwixmobile.language.viewmodel.LanguageViewModel
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel
import org.kiwix.kiwixmobile.zimManager.ZimManageViewModel
@Module(includes = [CoreViewModelModule::class])
abstract class KiwixViewModelModule {

View File

@ -35,7 +35,7 @@ import org.kiwix.kiwixmobile.cachedComponent
import org.kiwix.kiwixmobile.core.base.BaseActivity
import org.kiwix.kiwixmobile.core.base.BaseFragment
import org.kiwix.kiwixmobile.core.base.FragmentActivityExtensions
import org.kiwix.kiwixmobile.zim_manager.SimplePageChangeListener
import org.kiwix.kiwixmobile.zimManager.SimplePageChangeListener
import java.util.Timer
import java.util.TimerTask
import javax.inject.Inject
@ -72,14 +72,18 @@ class IntroFragment : BaseFragment(), IntroContract.View, FragmentActivityExtens
addOnPageChangeListener(SimplePageChangeListener(::updateView, ::handleDraggingState))
}
tab_indicator.setViewPager(view_pager)
timer?.schedule(object : TimerTask() {
override fun run() {
handler.post {
if (currentPage == views.size) currentPage = 0
view_pager.setCurrentItem(currentPage++, true)
timer?.schedule(
object : TimerTask() {
override fun run() {
handler.post {
if (currentPage == views.size) currentPage = 0
view_pager.setCurrentItem(currentPage++, true)
}
}
}
}, timerDelay, timerPeriod)
},
timerDelay,
timerPeriod
)
views.forEach {
it.setOnClickListener { dismissAutoRotate() }
}

View File

@ -125,9 +125,11 @@ class LanguageFragment : BaseFragment() {
super.onCreateOptionsMenu(menu, inflater)
inflater.inflate(R.menu.menu_language, menu)
val search = menu.findItem(R.id.menu_language_search)
(search.actionView as SearchView).setOnQueryTextListener(SimpleTextListener {
languageViewModel.actions.offer(Filter(it))
})
(search.actionView as SearchView).setOnQueryTextListener(
SimpleTextListener {
languageViewModel.actions.offer(Filter(it))
}
)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {

View File

@ -19,6 +19,7 @@ package org.kiwix.kiwixmobile.localFileTransfer
import android.Manifest
import android.Manifest.permission.ACCESS_FINE_LOCATION
import android.Manifest.permission.NEARBY_WIFI_DEVICES
import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
import android.annotation.SuppressLint
import android.content.Intent
@ -40,6 +41,7 @@ import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.annotation.RequiresApi
import androidx.appcompat.widget.Toolbar
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
@ -57,12 +59,13 @@ import org.kiwix.kiwixmobile.core.base.BaseFragment
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.popNavigationBackstack
import org.kiwix.kiwixmobile.core.extensions.toast
import org.kiwix.kiwixmobile.core.main.CoreMainActivity
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
import org.kiwix.kiwixmobile.core.utils.dialog.AlertDialogShower
import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog
import org.kiwix.kiwixmobile.localFileTransfer.WifiDirectManager.Companion.getDeviceStatus
import org.kiwix.kiwixmobile.localFileTransfer.adapter.WifiP2pDelegate
import org.kiwix.kiwixmobile.localFileTransfer.adapter.WifiPeerListAdapter
import java.util.ArrayList
import org.kiwix.kiwixmobile.webserver.ZimHostFragment.Companion.PERMISSION_REQUEST_CODE_COARSE_LOCATION
import javax.inject.Inject
/**
@ -82,7 +85,8 @@ import javax.inject.Inject
const val URIS_KEY = "uris"
@SuppressLint("GoogleAppIndexingApiWarning", "Registered")
class LocalFileTransferFragment : BaseFragment(),
class LocalFileTransferFragment :
BaseFragment(),
WifiDirectManager.Callbacks {
@Inject
lateinit var alertDialogShower: AlertDialogShower
@ -93,6 +97,9 @@ class LocalFileTransferFragment : BaseFragment(),
@Inject
lateinit var locationManager: LocationManager
@Inject
lateinit var sharedPreferenceUtil: SharedPreferenceUtil
private var fileListAdapter: FileListAdapter? = null
private var wifiPeerListAdapter: WifiPeerListAdapter? = null
@ -223,6 +230,20 @@ class LocalFileTransferFragment : BaseFragment(),
/* Helper methods used for checking permissions and states of services */
private fun checkFineLocationAccessPermission(): Boolean {
// Required by Android to detect wifi-p2p peers
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
return permissionIsGranted(NEARBY_WIFI_DEVICES).also { permissionGranted ->
if (!permissionGranted) {
if (shouldShowRationale(NEARBY_WIFI_DEVICES)) {
alertDialogShower.show(
KiwixDialog.NearbyWifiPermissionRationaleOnHostZimFile,
::askNearbyWifiDevicesPermission
)
} else {
askNearbyWifiDevicesPermission()
}
}
}
}
return permissionIsGranted(ACCESS_FINE_LOCATION).also { permissionGranted ->
if (!permissionGranted) {
if (shouldShowRationale(ACCESS_FINE_LOCATION)) {
@ -237,23 +258,36 @@ class LocalFileTransferFragment : BaseFragment(),
}
}
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
private fun askNearbyWifiDevicesPermission() {
ActivityCompat.requestPermissions(
requireActivity(), arrayOf(Manifest.permission.NEARBY_WIFI_DEVICES),
PERMISSION_REQUEST_CODE_COARSE_LOCATION
)
}
private fun requestLocationPermission() {
requestPermission(Manifest.permission.ACCESS_FINE_LOCATION, PERMISSION_REQUEST_FINE_LOCATION)
}
private fun checkExternalStorageWritePermission(): Boolean { // To access and store the zims
return permissionIsGranted(WRITE_EXTERNAL_STORAGE).also { permissionGranted ->
if (!permissionGranted) {
if (shouldShowRationale(WRITE_EXTERNAL_STORAGE)) {
alertDialogShower.show(
KiwixDialog.StoragePermissionRationale,
::requestStoragePermissionPermission
)
} else {
requestStoragePermissionPermission()
if (!sharedPreferenceUtil.isPlayStoreBuildWithAndroid11OrAbove() &&
Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU
) {
return permissionIsGranted(WRITE_EXTERNAL_STORAGE).also { permissionGranted ->
if (!permissionGranted) {
if (shouldShowRationale(WRITE_EXTERNAL_STORAGE)) {
alertDialogShower.show(
KiwixDialog.StoragePermissionRationale,
::requestStoragePermissionPermission
)
} else {
requestStoragePermissionPermission()
}
}
}
}
return true
}
private fun shouldShowRationale(writeExternalStorage: String) =
@ -294,7 +328,11 @@ class LocalFileTransferFragment : BaseFragment(),
}
private val isLocationServiceEnabled: Boolean
get() = isProviderEnabled(GPS_PROVIDER) || isProviderEnabled(NETWORK_PROVIDER)
get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
true
} else {
isProviderEnabled(GPS_PROVIDER) || isProviderEnabled(NETWORK_PROVIDER)
}
private fun isProviderEnabled(locationProvider: String): Boolean {
return try {

View File

@ -66,7 +66,8 @@ abstract class PeerGroupHandshake(private var groupInfo: WifiP2pInfo) {
InetSocketAddress(
groupInfo.groupOwnerAddress.hostAddress,
PEER_HANDSHAKE_PORT
), 15000
),
15000
)
val objectOutputStream = ObjectOutputStream(client.getOutputStream())
// Send message for the peer device to verify

View File

@ -33,7 +33,7 @@ class SenderHandShake(private val wifiDirectManager: WifiDirectManager, groupInf
// Send total number of files which will be transferred
objectOutputStream.writeObject(wifiDirectManager.totalFilesForTransfer)
// Send the names of each of those files, in order
wifiDirectManager.getFilesForTransfer().forEach { fileItem ->
wifiDirectManager.getFilesForTransfer().iterator().forEach { fileItem ->
objectOutputStream.writeObject(fileItem.fileName)
Log.d(TAG, "Sending " + fileItem.fileUri.toString())
}

View File

@ -112,16 +112,19 @@ class WifiDirectManager @Inject constructor(
private fun unregisterWifiDirectBroadcastReceiver() = context.unregisterReceiver(receiver)
fun discoverPeerDevices() {
manager?.discoverPeers(channel, object : ActionListener {
override fun onSuccess() {
context.toast(R.string.discovery_initiated, Toast.LENGTH_SHORT)
}
manager?.discoverPeers(
channel,
object : ActionListener {
override fun onSuccess() {
context.toast(R.string.discovery_initiated, Toast.LENGTH_SHORT)
}
override fun onFailure(reason: Int) {
Log.d(TAG, "${context.getString(R.string.discovery_failed)}: ${getErrorMessage(reason)}")
context.toast(R.string.discovery_failed, Toast.LENGTH_SHORT)
override fun onFailure(reason: Int) {
Log.d(TAG, "${context.getString(R.string.discovery_failed)}: ${getErrorMessage(reason)}")
context.toast(R.string.discovery_failed, Toast.LENGTH_SHORT)
}
}
})
)
}
/* From KiwixWifiP2pBroadcastReceiver.P2pEventListener callback-interface*/
@ -188,7 +191,8 @@ class WifiDirectManager @Inject constructor(
hasSenderStartedConnection = true
connect(senderSelectedPeerDevice)
context.toast(R.string.performing_handshake, Toast.LENGTH_LONG)
})
}
)
}
}
@ -197,17 +201,21 @@ class WifiDirectManager @Inject constructor(
deviceAddress = senderSelectedPeerDevice.deviceAddress
wps.setup = WpsInfo.PBC
}
manager?.connect(channel, config, object : ActionListener {
override fun onSuccess() {
// UI updated from broadcast receiver
}
manager?.connect(
channel,
config,
object : ActionListener {
override fun onSuccess() {
// UI updated from broadcast receiver
}
override fun onFailure(reason: Int) {
val errorMessage = getErrorMessage(reason)
Log.d(TAG, context.getString(R.string.connection_failed) + ": " + errorMessage)
context.toast(R.string.connection_failed, Toast.LENGTH_LONG)
override fun onFailure(reason: Int) {
val errorMessage = getErrorMessage(reason)
Log.d(TAG, context.getString(R.string.connection_failed) + ": " + errorMessage)
context.toast(R.string.connection_failed, Toast.LENGTH_LONG)
}
}
})
)
}
private fun performHandshakeWith(groupInfo: WifiP2pInfo) {
@ -291,17 +299,20 @@ class WifiDirectManager @Inject constructor(
}
private fun disconnect() {
manager?.removeGroup(channel, object : ActionListener {
override fun onFailure(reasonCode: Int) {
Log.d(TAG, "Disconnect failed. Reason: $reasonCode")
closeChannel()
}
manager?.removeGroup(
channel,
object : ActionListener {
override fun onFailure(reasonCode: Int) {
Log.d(TAG, "Disconnect failed. Reason: $reasonCode")
closeChannel()
}
override fun onSuccess() {
Log.d(TAG, "Disconnect successful")
closeChannel()
override fun onSuccess() {
Log.d(TAG, "Disconnect successful")
closeChannel()
}
}
})
)
}
private fun closeChannel() {

View File

@ -33,6 +33,7 @@ import kotlinx.android.synthetic.main.activity_kiwix_main.bottom_nav_view
import kotlinx.android.synthetic.main.activity_kiwix_main.drawer_nav_view
import kotlinx.android.synthetic.main.activity_kiwix_main.navigation_container
import kotlinx.android.synthetic.main.activity_kiwix_main.reader_drawer_nav_view
import org.kiwix.kiwixmobile.BuildConfig
import org.kiwix.kiwixmobile.R
import org.kiwix.kiwixmobile.core.base.FragmentActivityExtensions
import org.kiwix.kiwixmobile.core.di.components.CoreComponent
@ -54,11 +55,13 @@ class KiwixMainActivity : CoreMainActivity() {
override val bookmarksFragmentResId: Int = R.id.bookmarksFragment
override val settingsFragmentResId: Int = R.id.kiwixSettingsFragment
override val historyFragmentResId: Int = R.id.historyFragment
override val notesFragmentResId: Int = R.id.notesFragment
override val readerFragmentResId: Int = R.id.readerFragment
override val helpFragmentResId: Int = R.id.helpFragment
override val topLevelDestinations =
setOf(R.id.downloadsFragment, R.id.libraryFragment, R.id.readerFragment)
private var isIntroScreenVisible: Boolean = false
override fun injection(coreComponent: CoreComponent) {
cachedComponent.inject(this)
}
@ -97,11 +100,15 @@ class KiwixMainActivity : CoreMainActivity() {
handleDrawerOnNavigation()
}
}
if (sharedPreferenceUtil.showIntro()) {
if (sharedPreferenceUtil.showIntro() && !isIntroScreenNotVisible()) {
navigate(KiwixReaderFragmentDirections.actionReaderFragmentToIntroFragment())
}
sharedPreferenceUtil.setIsPlayStoreBuildType(BuildConfig.IS_PLAYSTORE)
}
private fun isIntroScreenNotVisible(): Boolean =
isIntroScreenVisible.also { isIntroScreenVisible = true }
override fun onSupportActionModeStarted(mode: ActionMode) {
super.onSupportActionModeStarted(mode)
actionMode = mode

View File

@ -26,6 +26,7 @@ import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.provider.Settings
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
@ -69,12 +70,12 @@ import org.kiwix.kiwixmobile.core.utils.files.FileUtils
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BookOnDiskDelegate
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskAdapter
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RequestMultiSelection
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RequestNavigateTo
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RequestSelect
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.FileSelectListState
import org.kiwix.kiwixmobile.zimManager.ZimManageViewModel
import org.kiwix.kiwixmobile.zimManager.ZimManageViewModel.FileSelectActions
import org.kiwix.kiwixmobile.zimManager.ZimManageViewModel.FileSelectActions.RequestMultiSelection
import org.kiwix.kiwixmobile.zimManager.ZimManageViewModel.FileSelectActions.RequestNavigateTo
import org.kiwix.kiwixmobile.zimManager.ZimManageViewModel.FileSelectActions.RequestSelect
import org.kiwix.kiwixmobile.zimManager.fileselectView.FileSelectListState
import java.io.File
import javax.inject.Inject
@ -94,10 +95,12 @@ class LocalLibraryFragment : BaseFragment() {
}
private val bookDelegate: BookOnDiskDelegate.BookDelegate by lazy {
BookOnDiskDelegate.BookDelegate(sharedPreferenceUtil,
BookOnDiskDelegate.BookDelegate(
sharedPreferenceUtil,
{ offerAction(RequestNavigateTo(it)) },
{ offerAction(RequestMultiSelection(it)) },
{ offerAction(RequestSelect(it)) })
{ offerAction(RequestSelect(it)) }
)
}
private val booksOnDiskAdapter: BooksOnDiskAdapter by lazy {
BooksOnDiskAdapter(bookDelegate, BookOnDiskDelegate.LanguageDelegate)
@ -138,9 +141,9 @@ class LocalLibraryFragment : BaseFragment() {
}
zimManageViewModel.fileSelectListStates.observe(viewLifecycleOwner, Observer(::render))
disposable.add(sideEffects())
zimManageViewModel.deviceListIsRefreshing.observe(viewLifecycleOwner, Observer {
zimManageViewModel.deviceListIsRefreshing.observe(viewLifecycleOwner) {
zim_swiperefresh.isRefreshing = it!!
})
}
if (savedInstanceState != null && savedInstanceState.getBoolean(WAS_IN_ACTION_MODE)) {
zimManageViewModel.fileSelectActions.offer(FileSelectActions.RestartActionMode)
}
@ -148,6 +151,15 @@ class LocalLibraryFragment : BaseFragment() {
go_to_downloads_button_no_files.setOnClickListener {
offerAction(FileSelectActions.UserClickedDownloadBooksButton)
}
hideFilePickerButton()
}
private fun hideFilePickerButton() {
if (sharedPreferenceUtil.isPlayStoreBuild) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
select_file.visibility = View.GONE
}
}
select_file.setOnClickListener {
showFileChooser()
@ -229,7 +241,8 @@ class LocalLibraryFragment : BaseFragment() {
override fun onResume() {
super.onResume()
checkPermissions()
if (!sharedPreferenceUtil.isPlayStoreBuildWithAndroid11OrAbove())
checkPermissions()
}
override fun onDestroyView() {
@ -273,39 +286,59 @@ class LocalLibraryFragment : BaseFragment() {
Manifest.permission.READ_EXTERNAL_STORAGE
) != PackageManager.PERMISSION_GRANTED
) {
context.toast(R.string.request_storage)
requestPermissions(
arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
),
REQUEST_STORAGE_PERMISSION
)
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
if (Environment.isExternalStorageManager()) {
// We already have permission!!
requestFileSystemCheck()
} else {
if (sharedPreferenceUtil.manageExternalFilesPermissionDialog) {
// We should only ask for first time, If the users wants to revoke settings
// then they can directly toggle this feature from settings screen
sharedPreferenceUtil.manageExternalFilesPermissionDialog = false
// Show Dialog and Go to settings to give permission
dialogShower.show(
KiwixDialog.ManageExternalFilesPermissionDialog,
{
this.activity?.let(FragmentActivity::navigateToSettings)
}
)
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
dialogShower.show(
KiwixDialog.StoragePermissionRationale,
::openAppSettings
)
} else {
context.toast(R.string.request_storage)
requestPermissions(
arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
),
REQUEST_STORAGE_PERMISSION
)
}
} else {
if (sharedPreferenceUtil.isPlayStoreBuild) {
requestFileSystemCheck()
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
if (Environment.isExternalStorageManager()) {
// We already have permission!!
requestFileSystemCheck()
} else {
if (sharedPreferenceUtil.manageExternalFilesPermissionDialog) {
// We should only ask for first time, If the users wants to revoke settings
// then they can directly toggle this feature from settings screen
sharedPreferenceUtil.manageExternalFilesPermissionDialog = false
// Show Dialog and Go to settings to give permission
dialogShower.show(
KiwixDialog.ManageExternalFilesPermissionDialog,
{
this.activity?.let(FragmentActivity::navigateToSettings)
}
)
}
}
} else {
requestFileSystemCheck()
}
}
}
}
private fun openAppSettings() {
val uri: Uri = Uri.fromParts("package", requireActivity().packageName, null)
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK
data = uri
}
startActivity(intent)
}
private fun requestFileSystemCheck() {
zimManageViewModel.requestFileSystemCheck.onNext(Unit)
}

View File

@ -43,10 +43,13 @@ import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.observe
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.tonyodev.fetch2.Status
import eu.mhutti1.utils.storage.StorageDevice
import eu.mhutti1.utils.storage.StorageSelectDialog
import kotlinx.android.synthetic.main.fragment_destination_download.allowInternetPermissionButton
import kotlinx.android.synthetic.main.fragment_destination_download.libraryErrorText
import kotlinx.android.synthetic.main.fragment_destination_download.libraryList
import kotlinx.android.synthetic.main.fragment_destination_download.librarySwipeRefresh
@ -78,12 +81,12 @@ import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog
import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog.SelectFolder
import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog.YesNoDialog.WifiOnly
import org.kiwix.kiwixmobile.core.utils.files.FileUtils.getPathFromUri
import org.kiwix.kiwixmobile.zim_manager.NetworkState
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel
import org.kiwix.kiwixmobile.zim_manager.library_view.AvailableSpaceCalculator
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryAdapter
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryDelegate
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryListItem
import org.kiwix.kiwixmobile.zimManager.NetworkState
import org.kiwix.kiwixmobile.zimManager.ZimManageViewModel
import org.kiwix.kiwixmobile.zimManager.libraryView.AvailableSpaceCalculator
import org.kiwix.kiwixmobile.zimManager.libraryView.adapter.LibraryAdapter
import org.kiwix.kiwixmobile.zimManager.libraryView.adapter.LibraryDelegate
import org.kiwix.kiwixmobile.zimManager.libraryView.adapter.LibraryListItem
import javax.inject.Inject
class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
@ -91,11 +94,11 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
@Inject lateinit var conMan: ConnectivityManager
@Inject lateinit var downloader: Downloader
@Inject lateinit var dialogShower: DialogShower
@Inject lateinit var alertDialogShower: AlertDialogShower
@Inject lateinit var sharedPreferenceUtil: SharedPreferenceUtil
@Inject lateinit var viewModelFactory: ViewModelProvider.Factory
@Inject lateinit var bookUtils: BookUtils
@Inject lateinit var availableSpaceCalculator: AvailableSpaceCalculator
@Inject lateinit var alertDialogShower: AlertDialogShower
private val zimManageViewModel by lazy {
requireActivity().viewModel<ZimManageViewModel>(viewModelFactory)
}
@ -104,9 +107,18 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
LibraryAdapter(
LibraryDelegate.BookDelegate(bookUtils, ::onBookItemClick),
LibraryDelegate.DownloadDelegate {
dialogShower.show(
KiwixDialog.YesNoDialog.StopDownload,
{ downloader.cancelDownload(it.downloadId) })
if (it.currentDownloadState == Status.FAILED) {
if (isNotConnected) {
noInternetSnackbar()
} else {
downloader.retryDownload(it.downloadId)
}
} else {
dialogShower.show(
KiwixDialog.YesNoDialog.StopDownload,
{ downloader.cancelDownload(it.downloadId) }
)
}
},
LibraryDelegate.DividerDelegate
)
@ -152,31 +164,49 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
viewLifecycleOwner, Observer(::onRefreshStateChange)
)
zimManageViewModel.networkStates.observe(viewLifecycleOwner, Observer(::onNetworkStateChange))
zimManageViewModel.shouldShowWifiOnlyDialog.observe(viewLifecycleOwner, Observer {
zimManageViewModel.shouldShowWifiOnlyDialog.observe(
viewLifecycleOwner
) {
if (it) {
dialogShower.show(
WifiOnly,
{
sharedPreferenceUtil.putPrefWifiOnly(false)
zimManageViewModel.shouldShowWifiOnlyDialog.value = false
},
{
onRefreshStateChange(false)
context.toast(
resources.getString(R.string.denied_internet_permission_message),
Toast.LENGTH_SHORT
)
}
)
showInternetPermissionDialog()
}
})
}
// hides keyboard when scrolled
libraryList.addOnScrollListener(SimpleRecyclerViewScrollListener { _, newState ->
if (newState == RecyclerView.SCROLL_STATE_DRAGGING) {
libraryList.closeKeyboard()
libraryList.addOnScrollListener(
SimpleRecyclerViewScrollListener { _, newState ->
if (newState == RecyclerView.SCROLL_STATE_DRAGGING) {
libraryList.closeKeyboard()
}
}
})
)
allowInternetPermissionButton.setOnClickListener {
showInternetPermissionDialog()
}
}
private fun showInternetPermissionDialog() {
dialogShower.show(
WifiOnly,
{
onRefreshStateChange(true)
libraryErrorText.visibility = View.GONE
allowInternetPermissionButton.visibility = View.GONE
sharedPreferenceUtil.putPrefWifiOnly(false)
zimManageViewModel.shouldShowWifiOnlyDialog.value = false
},
{
onRefreshStateChange(false)
context.toast(
resources.getString(R.string.denied_internet_permission_message),
Toast.LENGTH_SHORT
)
libraryErrorText.setText(R.string.allow_internet_permission_message)
libraryErrorText.visibility = View.VISIBLE
allowInternetPermissionButton.visibility = View.VISIBLE
}
)
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
@ -227,8 +257,10 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
libraryErrorText.setText(R.string.no_network_connection)
libraryErrorText.visibility = View.VISIBLE
}
allowInternetPermissionButton.visibility = View.GONE
librarySwipeRefresh.isRefreshing = false
}
else -> {}
}
}
@ -255,6 +287,7 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
} else {
libraryErrorText.visibility = View.GONE
}
allowInternetPermissionButton.visibility = View.GONE
}
private fun refreshFragment() {
@ -277,16 +310,24 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
)
sharedPreferenceUtil.putStoragePosition(INTERNAL_SELECT_POSITION)
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val view = LayoutInflater.from(activity).inflate(R.layout.select_folder_dialog, null)
dialogShower.show(SelectFolder { view }, ::selectFolder)
if (sharedPreferenceUtil.isPlayStoreBuild) {
setExternalStoragePath(storageDevice)
} else {
sharedPreferenceUtil.putPrefStorage(storageDevice.name)
sharedPreferenceUtil.putStoragePosition(EXTERNAL_SELECT_POSITION)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val view = LayoutInflater.from(activity).inflate(R.layout.select_folder_dialog, null)
dialogShower.show(SelectFolder { view }, ::selectFolder)
} else {
setExternalStoragePath(storageDevice)
}
}
}
}
private fun setExternalStoragePath(storageDevice: StorageDevice) {
sharedPreferenceUtil.putPrefStorage(storageDevice.name)
sharedPreferenceUtil.putStoragePosition(EXTERNAL_SELECT_POSITION)
}
private fun selectFolder() {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
intent.addFlags(
@ -319,21 +360,24 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
}
private fun checkExternalStorageWritePermission(): Boolean {
return hasPermission(WRITE_EXTERNAL_STORAGE).also { permissionGranted ->
if (!permissionGranted) {
if (shouldShowRationale(WRITE_EXTERNAL_STORAGE)) {
alertDialogShower.show(
KiwixDialog.WriteStoragePermissionRationale,
::requestExternalStoragePermission
)
} else {
alertDialogShower.show(
KiwixDialog.WriteStoragePermissionRationale,
::openAppSettings
)
if (!sharedPreferenceUtil.isPlayStoreBuildWithAndroid11OrAbove()) {
return hasPermission(WRITE_EXTERNAL_STORAGE).also { permissionGranted ->
if (!permissionGranted) {
if (shouldShowRationale(WRITE_EXTERNAL_STORAGE)) {
alertDialogShower.show(
KiwixDialog.WriteStoragePermissionRationale,
::requestExternalStoragePermission
)
} else {
alertDialogShower.show(
KiwixDialog.WriteStoragePermissionRationale,
::openAppSettings
)
}
}
}
}
return true
}
private fun openAppSettings() {
@ -366,7 +410,8 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
permissions[0] == Manifest.permission.WRITE_EXTERNAL_STORAGE
) {
if (grantResults[0] != PERMISSION_GRANTED) {
checkExternalStorageWritePermission()
if (!sharedPreferenceUtil.isPlayStoreBuildWithAndroid11OrAbove())
checkExternalStorageWritePermission()
}
}
}
@ -388,17 +433,24 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
})
return
}
else -> availableSpaceCalculator.hasAvailableSpaceFor(item,
{ downloadFile(item.book) },
{
libraryList.snack(
getString(R.string.download_no_space) +
"\n" + getString(R.string.space_available) + " " +
it,
R.string.download_change_storage,
::showStorageSelectDialog
)
})
else -> if (sharedPreferenceUtil.showStorageOption) {
showStorageConfigureDialog()
} else {
availableSpaceCalculator.hasAvailableSpaceFor(
item,
{ downloadFile(item.book) },
{
libraryList.snack(
"""
${getString(R.string.download_no_space)}
${getString(R.string.space_available)} $it
""".trimIndent(),
R.string.download_change_storage,
::showStorageSelectDialog
)
}
)
}
}
}
}
@ -408,4 +460,17 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
onSelectAction = ::storeDeviceInPreferences
}
.show(requireFragmentManager(), getString(R.string.pref_storage))
private fun showStorageConfigureDialog() {
alertDialogShower.show(
KiwixDialog.StorageConfigure,
{
showStorageSelectDialog()
sharedPreferenceUtil.showStorageOption = false
},
{
sharedPreferenceUtil.showStorageOption = false
}
)
}
}

View File

@ -268,7 +268,7 @@ class KiwixReaderFragment : CoreReaderFragment() {
override fun createWebView(attrs: AttributeSet): ToolbarScrollingKiwixWebView {
return ToolbarScrollingKiwixWebView(
requireContext(), this, attrs, activityMainRoot as ViewGroup, videoView,
CoreWebViewClient(this, zimReaderContainer),
CoreWebViewClient(this, zimReaderContainer, sharedPreferenceUtil),
toolbarContainer, bottomToolbar, sharedPreferenceUtil = sharedPreferenceUtil,
parentNavigationBar = requireActivity().bottom_nav_view
)

View File

@ -41,8 +41,9 @@ class KiwixPrefsFragment : CorePrefsFragment() {
override fun setStorage() {
findPreference<Preference>(PREF_STORAGE)?.title = getString(
if (sharedPreferenceUtil.prefStorage == internalStorage()?.let
(sharedPreferenceUtil::getPublicDirectoryPath)
if (sharedPreferenceUtil.prefStorage == internalStorage()?.let(
sharedPreferenceUtil::getPublicDirectoryPath
)
) R.string.internal_storage
else R.string.external_storage
)
@ -54,7 +55,7 @@ class KiwixPrefsFragment : CorePrefsFragment() {
private fun setMangeExternalStoragePermission() {
val permissionPref = findPreference<Preference>(PREF_MANAGE_EXTERNAL_STORAGE_PERMISSION)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && !sharedPreferenceUtil.isPlayStoreBuild) {
showPermissionPreference()
val externalStorageManager = Environment.isExternalStorageManager()
if (externalStorageManager) {

View File

@ -35,6 +35,7 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
@ -62,7 +63,6 @@ import org.kiwix.kiwixmobile.webserver.wifi_hotspot.HotspotService
import org.kiwix.kiwixmobile.webserver.wifi_hotspot.HotspotService.ACTION_CHECK_IP_ADDRESS
import org.kiwix.kiwixmobile.webserver.wifi_hotspot.HotspotService.ACTION_START_SERVER
import org.kiwix.kiwixmobile.webserver.wifi_hotspot.HotspotService.ACTION_STOP_SERVER
import java.util.ArrayList
import javax.inject.Inject
class ZimHostFragment : BaseFragment(), ZimHostCallbacks, ZimHostContract.View {
@ -140,7 +140,13 @@ class ZimHostFragment : BaseFragment(), ZimHostCallbacks, ZimHostContract.View {
}
startServerButton.setOnClickListener {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P || checkCoarseLocationAccessPermission()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU ||
checkNearbyWifiDevicesPermission()
) {
startStopServer()
} else if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P ||
checkCoarseLocationAccessPermission()
) {
startStopServer()
}
}
@ -169,6 +175,33 @@ class ZimHostFragment : BaseFragment(), ZimHostCallbacks, ZimHostContract.View {
true
}
private fun checkNearbyWifiDevicesPermission(): Boolean =
if (ContextCompat.checkSelfPermission(
requireActivity(),
Manifest.permission.NEARBY_WIFI_DEVICES
) == PackageManager.PERMISSION_DENIED
) {
if (ActivityCompat.shouldShowRequestPermissionRationale(
requireActivity(),
Manifest.permission.NEARBY_WIFI_DEVICES
)
) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
alertDialogShower.show(
KiwixDialog.NearbyWifiPermissionRationaleOnHostZimFile,
::askNearbyWifiDevicesPermission
)
}
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
askNearbyWifiDevicesPermission()
}
}
false
} else {
true
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
@ -189,6 +222,14 @@ class ZimHostFragment : BaseFragment(), ZimHostCallbacks, ZimHostContract.View {
)
}
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
private fun askNearbyWifiDevicesPermission() {
ActivityCompat.requestPermissions(
requireActivity(), arrayOf(Manifest.permission.NEARBY_WIFI_DEVICES),
PERMISSION_REQUEST_CODE_COARSE_LOCATION
)
}
private fun startStopServer() {
when {
ServerUtils.isServerStarted -> stopServer()
@ -391,6 +432,6 @@ class ZimHostFragment : BaseFragment(), ZimHostCallbacks, ZimHostContract.View {
companion object {
const val SELECTED_ZIM_PATHS_KEY = "selected_zim_paths"
private const val PERMISSION_REQUEST_CODE_COARSE_LOCATION = 10
const val PERMISSION_REQUEST_CODE_COARSE_LOCATION = 10
}
}

View File

@ -34,7 +34,7 @@ class ZimHostPresenter @Inject internal constructor(private val dataSource: Data
Presenter {
override fun loadBooks(previouslyHostedBooks: Set<String>) {
dataSource.languageCategorizedBooks
dataSource.getLanguageCategorizedBooks()
.map { books ->
books
.filterIsInstance<BooksOnDiskListItem.BookOnDisk>()

View File

@ -17,6 +17,7 @@
*/
package org.kiwix.kiwixmobile.webserver.wifi_hotspot
import android.annotation.SuppressLint
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
@ -38,18 +39,20 @@ class HotspotNotificationManager @Inject constructor(
private fun hotspotNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
notificationManager.createNotificationChannel(NotificationChannel(
HOTSPOT_SERVICE_CHANNEL_ID,
context.getString(R.string.hotspot_service_channel_name),
NotificationManager.IMPORTANCE_DEFAULT
).apply {
description = context.getString(R.string.hotspot_channel_description)
setSound(null, null)
})
notificationManager.createNotificationChannel(
NotificationChannel(
HOTSPOT_SERVICE_CHANNEL_ID,
context.getString(R.string.hotspot_service_channel_name),
NotificationManager.IMPORTANCE_DEFAULT
).apply {
description = context.getString(R.string.hotspot_channel_description)
setSound(null, null)
}
)
}
}
fun buildForegroundNotification(): Notification {
@SuppressLint("UnspecifiedImmutableFlag") fun buildForegroundNotification(): Notification {
val contentIntent = NavDeepLinkBuilder(context).setComponentName(
KiwixMainActivity::class.java
)
@ -60,8 +63,16 @@ class HotspotNotificationManager @Inject constructor(
val stopIntent = Intent(context, HotspotService::class.java).setAction(
HotspotService.ACTION_STOP_SERVER
)
val stopHotspot =
val stopHotspot = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
PendingIntent.getService(
context,
0,
stopIntent,
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)
} else {
PendingIntent.getService(context, 0, stopIntent, PendingIntent.FLAG_UPDATE_CURRENT)
}
return NotificationCompat.Builder(context)
.setContentTitle(context.getString(R.string.hotspot_notification_content_title))
.setContentText(context.getString(R.string.hotspot_running))

View File

@ -16,7 +16,7 @@
*
*/
package org.kiwix.kiwixmobile.zim_manager
package org.kiwix.kiwixmobile.zimManager
import android.content.Context
import android.content.Intent

View File

@ -16,7 +16,7 @@
*
*/
package org.kiwix.kiwixmobile.zim_manager
package org.kiwix.kiwixmobile.zimManager
import android.content.Context
import org.kiwix.kiwixmobile.core.zim_manager.Language

View File

@ -15,7 +15,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.kiwix.kiwixmobile.zim_manager
package org.kiwix.kiwixmobile.zimManager
import android.os.FileObserver
import io.reactivex.Flowable
@ -23,13 +23,13 @@ import io.reactivex.functions.BiFunction
import io.reactivex.processors.BehaviorProcessor
import io.reactivex.schedulers.Schedulers
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState.CanWrite4GbFile
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState.CannotWrite4GbFile
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState.NotEnoughSpaceFor4GbFile
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState.Unknown
import org.kiwix.kiwixmobile.zim_manager.FileSystemCapability.CANNOT_WRITE_4GB
import org.kiwix.kiwixmobile.zim_manager.FileSystemCapability.CAN_WRITE_4GB
import org.kiwix.kiwixmobile.zim_manager.FileSystemCapability.INCONCLUSIVE
import org.kiwix.kiwixmobile.zimManager.Fat32Checker.FileSystemState.CanWrite4GbFile
import org.kiwix.kiwixmobile.zimManager.Fat32Checker.FileSystemState.CannotWrite4GbFile
import org.kiwix.kiwixmobile.zimManager.Fat32Checker.FileSystemState.NotEnoughSpaceFor4GbFile
import org.kiwix.kiwixmobile.zimManager.Fat32Checker.FileSystemState.Unknown
import org.kiwix.kiwixmobile.zimManager.FileSystemCapability.CANNOT_WRITE_4GB
import org.kiwix.kiwixmobile.zimManager.FileSystemCapability.CAN_WRITE_4GB
import org.kiwix.kiwixmobile.zimManager.FileSystemCapability.INCONCLUSIVE
import java.io.File
class Fat32Checker constructor(
@ -77,7 +77,7 @@ class Fat32Checker constructor(
}
private fun canCreate4GbFile(storage: String): Boolean {
fileSystemCheckers.forEach {
fileSystemCheckers.iterator().forEach {
when (it.checkFilesystemSupports4GbFiles(storage)) {
CAN_WRITE_4GB -> return@canCreate4GbFile true
CANNOT_WRITE_4GB -> return@canCreate4GbFile false

View File

@ -16,7 +16,7 @@
*
*/
package org.kiwix.kiwixmobile.zim_manager
package org.kiwix.kiwixmobile.zimManager
interface FileSystemChecker {
fun checkFilesystemSupports4GbFiles(path: String): FileSystemCapability

View File

@ -16,12 +16,12 @@
*
*/
package org.kiwix.kiwixmobile.zim_manager
package org.kiwix.kiwixmobile.zimManager
import android.util.Log
import org.kiwix.kiwixmobile.zim_manager.FileSystemCapability.CANNOT_WRITE_4GB
import org.kiwix.kiwixmobile.zim_manager.FileSystemCapability.CAN_WRITE_4GB
import org.kiwix.kiwixmobile.zim_manager.FileSystemCapability.INCONCLUSIVE
import org.kiwix.kiwixmobile.zimManager.FileSystemCapability.CANNOT_WRITE_4GB
import org.kiwix.kiwixmobile.zimManager.FileSystemCapability.CAN_WRITE_4GB
import org.kiwix.kiwixmobile.zimManager.FileSystemCapability.INCONCLUSIVE
import java.io.File
import java.io.RandomAccessFile
@ -32,9 +32,10 @@ class FileWritingFileSystemChecker : FileSystemChecker {
when (val capability = readCapability(resultFile)) {
CAN_WRITE_4GB,
CANNOT_WRITE_4GB -> return capability
else -> {}
}
}
return with(File("$path/large_file_test.txt"), {
return with(File("$path/large_file_test.txt")) {
deleteIfExists()
try {
RandomAccessFile(this.path, "rw").use {
@ -50,7 +51,7 @@ class FileWritingFileSystemChecker : FileSystemChecker {
} finally {
deleteIfExists()
}
})
}
}
private fun readCapability(resultFile: File) =

View File

@ -16,13 +16,13 @@
*
*/
package org.kiwix.kiwixmobile.zim_manager
package org.kiwix.kiwixmobile.zimManager
import org.kiwix.kiwixmobile.core.zim_manager.MountInfo
import org.kiwix.kiwixmobile.core.zim_manager.MountPointProducer
import org.kiwix.kiwixmobile.zim_manager.FileSystemCapability.CANNOT_WRITE_4GB
import org.kiwix.kiwixmobile.zim_manager.FileSystemCapability.CAN_WRITE_4GB
import org.kiwix.kiwixmobile.zim_manager.FileSystemCapability.INCONCLUSIVE
import org.kiwix.kiwixmobile.zimManager.FileSystemCapability.CANNOT_WRITE_4GB
import org.kiwix.kiwixmobile.zimManager.FileSystemCapability.CAN_WRITE_4GB
import org.kiwix.kiwixmobile.zimManager.FileSystemCapability.INCONCLUSIVE
import javax.inject.Inject
class MountFileSystemChecker @Inject constructor(

View File

@ -16,7 +16,7 @@
*
*/
package org.kiwix.kiwixmobile.zim_manager
package org.kiwix.kiwixmobile.zimManager
enum class NetworkState {
CONNECTED,

View File

@ -16,7 +16,7 @@
*
*/
package org.kiwix.kiwixmobile.zim_manager
package org.kiwix.kiwixmobile.zimManager
import androidx.viewpager.widget.ViewPager.OnPageChangeListener

View File

@ -16,7 +16,7 @@
*
*/
package org.kiwix.kiwixmobile.zim_manager
package org.kiwix.kiwixmobile.zimManager
import android.app.Application
import android.net.ConnectivityManager
@ -51,27 +51,27 @@ import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.SelectionMode.MULT
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.SelectionMode.NORMAL
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState
import org.kiwix.kiwixmobile.zim_manager.NetworkState.CONNECTED
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.MultiModeFinished
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RequestDeleteMultiSelection
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RequestMultiSelection
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RequestNavigateTo
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RequestSelect
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RequestShareMultiSelection
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RestartActionMode
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.UserClickedDownloadBooksButton
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.FileSelectListState
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects.DeleteFiles
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects.NavigateToDownloads
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects.None
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects.OpenFileWithNavigation
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects.ShareFiles
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects.StartMultiSelection
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryListItem
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryListItem.BookItem
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryListItem.DividerItem
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryListItem.LibraryDownloadItem
import org.kiwix.kiwixmobile.zimManager.Fat32Checker.FileSystemState
import org.kiwix.kiwixmobile.zimManager.NetworkState.CONNECTED
import org.kiwix.kiwixmobile.zimManager.ZimManageViewModel.FileSelectActions.MultiModeFinished
import org.kiwix.kiwixmobile.zimManager.ZimManageViewModel.FileSelectActions.RequestDeleteMultiSelection
import org.kiwix.kiwixmobile.zimManager.ZimManageViewModel.FileSelectActions.RequestMultiSelection
import org.kiwix.kiwixmobile.zimManager.ZimManageViewModel.FileSelectActions.RequestNavigateTo
import org.kiwix.kiwixmobile.zimManager.ZimManageViewModel.FileSelectActions.RequestSelect
import org.kiwix.kiwixmobile.zimManager.ZimManageViewModel.FileSelectActions.RequestShareMultiSelection
import org.kiwix.kiwixmobile.zimManager.ZimManageViewModel.FileSelectActions.RestartActionMode
import org.kiwix.kiwixmobile.zimManager.ZimManageViewModel.FileSelectActions.UserClickedDownloadBooksButton
import org.kiwix.kiwixmobile.zimManager.fileselectView.FileSelectListState
import org.kiwix.kiwixmobile.zimManager.fileselectView.effects.DeleteFiles
import org.kiwix.kiwixmobile.zimManager.fileselectView.effects.NavigateToDownloads
import org.kiwix.kiwixmobile.zimManager.fileselectView.effects.None
import org.kiwix.kiwixmobile.zimManager.fileselectView.effects.OpenFileWithNavigation
import org.kiwix.kiwixmobile.zimManager.fileselectView.effects.ShareFiles
import org.kiwix.kiwixmobile.zimManager.fileselectView.effects.StartMultiSelection
import org.kiwix.kiwixmobile.zimManager.libraryView.adapter.LibraryListItem
import org.kiwix.kiwixmobile.zimManager.libraryView.adapter.LibraryListItem.BookItem
import org.kiwix.kiwixmobile.zimManager.libraryView.adapter.LibraryListItem.DividerItem
import org.kiwix.kiwixmobile.zimManager.libraryView.adapter.LibraryListItem.LibraryDownloadItem
import java.util.LinkedList
import java.util.Locale
import java.util.concurrent.TimeUnit.MILLISECONDS
@ -409,7 +409,7 @@ class ZimManageViewModel @Inject constructor(
) = if (filter.isEmpty()) {
unDownloadedBooks
} else {
unDownloadedBooks.forEach { it.calculateSearchMatches(filter, bookUtils) }
unDownloadedBooks.iterator().forEach { it.calculateSearchMatches(filter, bookUtils) }
unDownloadedBooks.filter { it.searchMatches > 0 }
}
@ -462,15 +462,16 @@ class ZimManageViewModel @Inject constructor(
private fun updateBookItems() =
dataSource.booksOnDiskAsListItems()
.subscribe(
{ newList ->
fileSelectListStates.postValue(
fileSelectListStates.value?.let { inheritSelections(it, newList) }
?: FileSelectListState(newList)
)
},
Throwable::printStackTrace
)
.subscribe({ newList ->
fileSelectListStates.postValue(
fileSelectListStates.value?.let {
inheritSelections(
it,
newList.toMutableList()
)
} ?: FileSelectListState(newList)
)
}, Throwable::printStackTrace)
private fun inheritSelections(
oldState: FileSelectListState,
@ -483,6 +484,7 @@ class ZimManageViewModel @Inject constructor(
oldBookOnDisk.id == newBookOnDisk.id
}
newBookOnDisk.apply { isSelected = firstOrNull?.isSelected ?: false }
})
}
)
}
}

View File

@ -15,7 +15,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.kiwix.kiwixmobile.zim_manager.fileselect_view
package org.kiwix.kiwixmobile.zimManager.fileselectView
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.SelectionMode
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.SelectionMode.NORMAL

View File

@ -16,7 +16,7 @@
*
*/
package org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects
package org.kiwix.kiwixmobile.zimManager.fileselectView.effects
import androidx.appcompat.app.AppCompatActivity
import org.kiwix.kiwixmobile.R

View File

@ -16,7 +16,7 @@
*
*/
package org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects
package org.kiwix.kiwixmobile.zimManager.fileselectView.effects
import androidx.appcompat.app.AppCompatActivity
import org.kiwix.kiwixmobile.core.base.SideEffect

View File

@ -16,7 +16,7 @@
*
*/
package org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects
package org.kiwix.kiwixmobile.zimManager.fileselectView.effects
import androidx.appcompat.app.AppCompatActivity
import org.kiwix.kiwixmobile.core.base.SideEffect

View File

@ -16,7 +16,7 @@
*
*/
package org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects
package org.kiwix.kiwixmobile.zimManager.fileselectView.effects
import androidx.appcompat.app.AppCompatActivity
import androidx.core.net.toUri

View File

@ -16,7 +16,7 @@
*
*/
package org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects
package org.kiwix.kiwixmobile.zimManager.fileselectView.effects
import android.net.Uri
import android.os.Build

View File

@ -16,7 +16,7 @@
*
*/
package org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects
package org.kiwix.kiwixmobile.zimManager.fileselectView.effects
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ActionMode
@ -24,10 +24,10 @@ import io.reactivex.processors.PublishProcessor
import org.kiwix.kiwixmobile.R
import org.kiwix.kiwixmobile.core.base.SideEffect
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.startActionMode
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.MultiModeFinished
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RequestDeleteMultiSelection
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RequestShareMultiSelection
import org.kiwix.kiwixmobile.zimManager.ZimManageViewModel.FileSelectActions
import org.kiwix.kiwixmobile.zimManager.ZimManageViewModel.FileSelectActions.MultiModeFinished
import org.kiwix.kiwixmobile.zimManager.ZimManageViewModel.FileSelectActions.RequestDeleteMultiSelection
import org.kiwix.kiwixmobile.zimManager.ZimManageViewModel.FileSelectActions.RequestShareMultiSelection
data class StartMultiSelection(
val fileSelectActions: PublishProcessor<FileSelectActions>

View File

@ -16,7 +16,7 @@
*
*/
package org.kiwix.kiwixmobile.zim_manager.library_view
package org.kiwix.kiwixmobile.zimManager.libraryView
import eu.mhutti1.utils.storage.Bytes
import eu.mhutti1.utils.storage.Kb
@ -25,7 +25,7 @@ import io.reactivex.schedulers.Schedulers
import org.kiwix.kiwixmobile.core.dao.FetchDownloadDao
import org.kiwix.kiwixmobile.core.downloader.model.DownloadModel
import org.kiwix.kiwixmobile.core.settings.StorageCalculator
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryListItem
import org.kiwix.kiwixmobile.zimManager.libraryView.adapter.LibraryListItem
import javax.inject.Inject
class AvailableSpaceCalculator @Inject constructor(

View File

@ -1,6 +1,6 @@
/*
* Kiwix Android
* Copyright (c) 2019 Kiwix <android.kiwix.org>
* Copyright (c) 2022 Kiwix <android.kiwix.org>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
@ -15,30 +15,21 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.kiwix.kiwixmobile.zim_manager.library_view;
package org.kiwix.kiwixmobile.zimManager.libraryView
import java.util.LinkedList;
import org.kiwix.kiwixmobile.core.base.BaseContract;
import org.kiwix.kiwixmobile.core.entity.LibraryNetworkEntity;
import org.kiwix.kiwixmobile.core.entity.LibraryNetworkEntity.Book;
import org.kiwix.kiwixmobile.core.base.BaseContract
import org.kiwix.kiwixmobile.core.entity.LibraryNetworkEntity
import java.util.LinkedList
/**
* Created by EladKeyshawn on 06/04/2017.
*/
public interface LibraryViewCallback extends BaseContract.View {
void showBooks(LinkedList<Book> books);
void displayNoNetworkConnection();
void displayNoItemsFound();
void displayNoItemsAvailable();
void displayScanningContent();
void stopScanningContent();
void downloadFile(LibraryNetworkEntity.Book book);
interface LibraryViewCallback : BaseContract.View<Any?> {
fun showBooks(books: LinkedList<LibraryNetworkEntity.Book?>?)
fun displayNoNetworkConnection()
fun displayNoItemsFound()
fun displayNoItemsAvailable()
fun displayScanningContent()
fun stopScanningContent()
fun downloadFile(book: LibraryNetworkEntity.Book?)
}

View File

@ -16,7 +16,7 @@
*
*/
package org.kiwix.kiwixmobile.zim_manager.library_view.adapter
package org.kiwix.kiwixmobile.zimManager.libraryView.adapter
import org.kiwix.kiwixmobile.core.base.adapter.AdapterDelegate
import org.kiwix.kiwixmobile.core.base.adapter.BaseDelegateAdapter

View File

@ -15,19 +15,19 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.kiwix.kiwixmobile.zim_manager.library_view.adapter
package org.kiwix.kiwixmobile.zimManager.libraryView.adapter
import android.view.ViewGroup
import org.kiwix.kiwixmobile.R
import org.kiwix.kiwixmobile.core.base.adapter.AbsDelegateAdapter
import org.kiwix.kiwixmobile.core.extensions.ViewGroupExtensions.inflate
import org.kiwix.kiwixmobile.core.utils.BookUtils
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryListItem.BookItem
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryListItem.DividerItem
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryListItem.LibraryDownloadItem
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryViewHolder.DownloadViewHolder
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryViewHolder.LibraryBookViewHolder
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryViewHolder.LibraryDividerViewHolder
import org.kiwix.kiwixmobile.zimManager.libraryView.adapter.LibraryListItem.BookItem
import org.kiwix.kiwixmobile.zimManager.libraryView.adapter.LibraryListItem.DividerItem
import org.kiwix.kiwixmobile.zimManager.libraryView.adapter.LibraryListItem.LibraryDownloadItem
import org.kiwix.kiwixmobile.zimManager.libraryView.adapter.LibraryViewHolder.DownloadViewHolder
import org.kiwix.kiwixmobile.zimManager.libraryView.adapter.LibraryViewHolder.LibraryBookViewHolder
import org.kiwix.kiwixmobile.zimManager.libraryView.adapter.LibraryViewHolder.LibraryDividerViewHolder
sealed class LibraryDelegate<I : LibraryListItem, out VH : LibraryViewHolder<I>> :
AbsDelegateAdapter<I, LibraryListItem, VH> {

View File

@ -16,21 +16,22 @@
*
*/
package org.kiwix.kiwixmobile.zim_manager.library_view.adapter
package org.kiwix.kiwixmobile.zimManager.libraryView.adapter
import androidx.annotation.StringRes
import com.tonyodev.fetch2.Status
import org.kiwix.kiwixmobile.core.downloader.model.Base64String
import org.kiwix.kiwixmobile.core.downloader.model.DownloadModel
import org.kiwix.kiwixmobile.core.downloader.model.DownloadState
import org.kiwix.kiwixmobile.core.downloader.model.Seconds
import org.kiwix.kiwixmobile.core.entity.LibraryNetworkEntity.Book
import org.kiwix.kiwixmobile.core.zim_manager.KiwixTag
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState.CanWrite4GbFile
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState.CannotWrite4GbFile
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState.NotEnoughSpaceFor4GbFile
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState.Unknown
import org.kiwix.kiwixmobile.zimManager.Fat32Checker
import org.kiwix.kiwixmobile.zimManager.Fat32Checker.FileSystemState
import org.kiwix.kiwixmobile.zimManager.Fat32Checker.FileSystemState.CanWrite4GbFile
import org.kiwix.kiwixmobile.zimManager.Fat32Checker.FileSystemState.CannotWrite4GbFile
import org.kiwix.kiwixmobile.zimManager.Fat32Checker.FileSystemState.NotEnoughSpaceFor4GbFile
import org.kiwix.kiwixmobile.zimManager.Fat32Checker.FileSystemState.Unknown
sealed class LibraryListItem {
abstract val id: Long
@ -69,7 +70,8 @@ sealed class LibraryListItem {
val progress: Int,
val eta: Seconds,
val downloadState: DownloadState,
override val id: Long
override val id: Long,
val currentDownloadState: Status
) : LibraryListItem() {
val readableEta: CharSequence = eta.takeIf { it.seconds > 0L }?.toHumanReadableTime() ?: ""
@ -84,7 +86,8 @@ sealed class LibraryListItem {
downloadModel.progress,
Seconds(downloadModel.etaInMilliSeconds / 1000L),
DownloadState.from(downloadModel.state, downloadModel.error),
downloadModel.book.id.hashCode().toLong()
downloadModel.book.id.hashCode().toLong(),
downloadModel.state
)
}
}

View File

@ -16,13 +16,15 @@
*
*/
package org.kiwix.kiwixmobile.zim_manager.library_view.adapter
package org.kiwix.kiwixmobile.zimManager.libraryView.adapter
import android.annotation.SuppressLint
import android.view.Gravity
import android.view.View
import android.view.View.MeasureSpec
import android.widget.Toast
import androidx.annotation.StringRes
import com.tonyodev.fetch2.Status
import kotlinx.android.synthetic.main.item_download.downloadProgress
import kotlinx.android.synthetic.main.item_download.downloadState
import kotlinx.android.synthetic.main.item_download.eta
@ -47,11 +49,11 @@ import org.kiwix.kiwixmobile.core.extensions.setBitmap
import org.kiwix.kiwixmobile.core.extensions.setTextAndVisibility
import org.kiwix.kiwixmobile.core.utils.BookUtils
import org.kiwix.kiwixmobile.core.zim_manager.KiloByte
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState.CannotWrite4GbFile
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState.Unknown
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryListItem.BookItem
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryListItem.DividerItem
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryListItem.LibraryDownloadItem
import org.kiwix.kiwixmobile.zimManager.Fat32Checker.FileSystemState.CannotWrite4GbFile
import org.kiwix.kiwixmobile.zimManager.Fat32Checker.FileSystemState.Unknown
import org.kiwix.kiwixmobile.zimManager.libraryView.adapter.LibraryListItem.BookItem
import org.kiwix.kiwixmobile.zimManager.libraryView.adapter.LibraryListItem.DividerItem
import org.kiwix.kiwixmobile.zimManager.libraryView.adapter.LibraryListItem.LibraryDownloadItem
sealed class LibraryViewHolder<in T : LibraryListItem>(containerView: View) :
BaseViewHolder<T>(containerView) {
@ -99,6 +101,9 @@ sealed class LibraryViewHolder<in T : LibraryListItem>(containerView: View) :
downloadProgress.progress = item.progress
stop.setOnClickListener { clickAction.invoke(item) }
downloadState.text = item.downloadState.toReadableState(containerView.context)
if (item.currentDownloadState == Status.FAILED) {
clickAction.invoke(item)
}
eta.text = item.readableEta
}
}
@ -110,6 +115,7 @@ sealed class LibraryViewHolder<in T : LibraryListItem>(containerView: View) :
}
}
@SuppressLint("ShowToast")
private fun View.centreToast(@StringRes id: Int) {
val locationXAndY = intArrayOf(0, 0)
getLocationOnScreen(locationXAndY)

View File

@ -61,7 +61,20 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.45" />
app:layout_constraintVertical_bias="0.45"
tools:ignore="RequiredSize" />
<Button
android:id="@+id/allowInternetPermissionButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/allow"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/libraryErrorText" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -64,6 +64,7 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="RequiredSize"
app:layout_constraintVertical_bias="0.45" />
<Button

View File

@ -86,6 +86,11 @@
android:name="org.kiwix.kiwixmobile.core.page.bookmark.BookmarksFragment"
android:label="BookmarksFragment"
tools:layout="@layout/fragment_page" />
<fragment
android:id="@+id/notesFragment"
android:name="org.kiwix.kiwixmobile.core.page.notes.NotesFragment"
android:label="NotesFragment"
tools:layout="@layout/fragment_page" />
<fragment
android:id="@+id/introFragment"
android:name="org.kiwix.kiwixmobile.intro.IntroFragment"

View File

@ -10,4 +10,5 @@
<string name="cannot_open_file">Échec de louverture du fichier.\nEssayez de chercher ce fichier dans longlet Appareils de votre Bibliothèque.</string>
<string name="send_files_title">Envoyer des fichiers</string>
<string name="receive_files_title">Recevoir des fichiers</string>
<string name="no_app_found_to_open">Aucune application trouvée pour sélectionner le fichier ZIM!</string>
</resources>

View File

@ -9,4 +9,5 @@
<string name="cannot_open_file">פתיחת הקובץ נכשלה\nנא לנסות לחפש את הקובץ הזה בלשונית \"מכשיר\" בספרייה שלך</string>
<string name="send_files_title">שליחת קבצים</string>
<string name="receive_files_title">קבלת קבצים</string>
<string name="no_app_found_to_open">לא נמצא יישום שיכול לבחור קובץ zim!</string>
</resources>

View File

@ -8,4 +8,5 @@
<string name="cannot_open_file">Vekirina dosyeyê têk çû\nJi kerema xwe biceribîne ku vê dosyeyê di Hilpekîna Cîhazê ya Kitêbxaneya xwe de vekî</string>
<string name="send_files_title">Dosyeyan Bişîne</string>
<string name="receive_files_title">Dosyeyan Werbigire</string>
<string name="no_app_found_to_open">Ji bo bijartina dosyeya zim-ê ti sepanekê nehate dîtin!</string>
</resources>

View File

@ -8,4 +8,5 @@
<string name="cannot_open_file">Не можев да ја отворам податотеката\nВидете дали можете да ја најдете во јазичето „Уред“ на вашата Библиотека</string>
<string name="send_files_title">Испраќање на податотеки</string>
<string name="receive_files_title">Примање на податотеки</string>
<string name="no_app_found_to_open">Не пронајдов прилог за избирање на ZIM-податотека!</string>
</resources>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Authors:
* Aboubacar.banamoridou.toure
* Lancine.kounfantoh.fofana
-->
<resources>
<string name="file_system_does_not_support_4gb">ߌ ߟߊ߫ ߞߐߕߐ߮ ߞߊ߲ߞߋ ߕߴߊ߬ ߛߐ߲߬ ߠߊ߫ ߘߏ߬ߣߌ߲߬ ߘߐ߫ ߞߊ߬ ߕߊ߬ߡߌ߲߬ 4GB ߟߊ߫</string>
<string name="detecting_file_system">ߡߊ߬ߟߐ߲߬ߠߌ߲ ߦߴߌ ߘߐ߫ ߣߴߌ ߟߊ߫ ߞߊ߲ߞߋ ߘߌ߫ ߛߋ߫ 4GB ߞߐߕߐ߮ ߛߌ߲ߘߌ߫ ߟߊ߫</string>
<string name="send_files_title">ߞߐߕߐ߮ ߟߎ߬ ߟߊߕߊ߯</string>
<string name="receive_files_title">ߞߐߕߐ߮ ߟߎ߬ ߟߊߛߣߍ߫</string>
<string name="no_app_found_to_open">ߟߥߊ߬ߟߌ߬ߟߊ߲߫ ߡߊ߫ ߛߐ߬ߘߐ߲߫ ߞߊ߬ ߛߋ߫ ߖ߭ߌߡ ߞߐߕߐ߮ ߛߎߥߊ߲ߘߌ߫ ߟߊ߫߹</string>
</resources>

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Authors:
* Strebski
* WaldiSt
-->
<resources>
@ -8,4 +9,5 @@
<string name="cannot_open_file">Nie można otworzyć pliku\nSpróbuj wyszukać ten plik na karcie Urządzenia twojej biblioteki</string>
<string name="send_files_title">Wyślij pliki</string>
<string name="receive_files_title">Odbierz pliki</string>
<string name="no_app_found_to_open">Nie znaleziono aplikacji do wybrania pliku zim!</string>
</resources>

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Authors:
* Fenixs-ru
* Smavrina
-->
<resources>
@ -8,4 +9,5 @@
<string name="cannot_open_file">Невозможно открыть файл\nПожалуйста, попытайтесь найти этот файл во вкладке “Устройство” в вашей библиотеке</string>
<string name="send_files_title">Послать файлы</string>
<string name="receive_files_title">Получить файлы</string>
<string name="no_app_found_to_open">Не найдено приложение для выбора ZIM-файла!</string>
</resources>

View File

@ -8,4 +8,5 @@
<string name="cannot_open_file">Abertura de su documentu fallida\nPro praghere proa a chircare custu documentu in s\'Ischeda de sos Dispositivos de sa Biblioteca tua</string>
<string name="send_files_title">Imbia documentos</string>
<string name="receive_files_title">Retzi documentos</string>
<string name="no_app_found_to_open">Peruna aplicatzione atzapada pro seletzionare s\'archìviu zim!</string>
</resources>

View File

@ -8,4 +8,5 @@
<string name="cannot_open_file">Nepodarilo sa otvoriť súbor\nProsím skúste tento súbor nájsť na karte zariadenia vo vašej knižnici</string>
<string name="send_files_title">Poslať súbory</string>
<string name="receive_files_title">Prijať súbory</string>
<string name="no_app_found_to_open">Nepodarilo sa nájsť aplikáciu pre výber súboru zim!</string>
</resources>

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Authors:
* BaRaN6161 TURK
* Hedda
* MuratTheTurkish
-->
<resources>
@ -9,4 +10,5 @@
<string name="cannot_open_file">Dosya açılamadı\nLütfen bu dosyayı Kitaplığınızın Cihaz Sekmesinde aramayı deneyin</string>
<string name="send_files_title">Dosya Yükle</string>
<string name="receive_files_title">Dosya Al</string>
<string name="no_app_found_to_open">zim dosyasını seçecek uygulama bulunamadı!</string>
</resources>

View File

@ -8,4 +8,5 @@
<string name="cannot_open_file">檔案開啟失敗\n請嘗試查看在您圖書館設備分頁裡的該檔案</string>
<string name="send_files_title">發送檔案</string>
<string name="receive_files_title">接收檔案</string>
<string name="no_app_found_to_open">沒有找到選擇 zim 檔案的應用程式!</string>
</resources>

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Authors:
* GuoPC
* Lynzrand
* StarrySky
-->
@ -9,4 +10,5 @@
<string name="cannot_open_file">未能打开文件\n请尝试在图书馆界面的“设备”选项卡中寻找这个文件</string>
<string name="send_files_title">发送文件</string>
<string name="receive_files_title">接收文件</string>
<string name="no_app_found_to_open">没有找到选择 ZIM 文件的应用程序!</string>
</resources>

View File

@ -16,7 +16,7 @@
*
*/
package org.kiwix.kiwixmobile.zim_manager
package org.kiwix.kiwixmobile.zimManager
import android.app.Application
import android.net.ConnectivityManager
@ -50,23 +50,23 @@ import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.SelectionMode.MULT
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.SelectionMode.NORMAL
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState.CanWrite4GbFile
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState.CannotWrite4GbFile
import org.kiwix.kiwixmobile.zim_manager.NetworkState.CONNECTED
import org.kiwix.kiwixmobile.zim_manager.NetworkState.NOT_CONNECTED
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.MultiModeFinished
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RequestDeleteMultiSelection
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RequestMultiSelection
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RequestSelect
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RequestShareMultiSelection
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RestartActionMode
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.FileSelectListState
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects.DeleteFiles
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects.None
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects.ShareFiles
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects.StartMultiSelection
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryListItem
import org.kiwix.kiwixmobile.zimManager.Fat32Checker.FileSystemState
import org.kiwix.kiwixmobile.zimManager.Fat32Checker.FileSystemState.CanWrite4GbFile
import org.kiwix.kiwixmobile.zimManager.Fat32Checker.FileSystemState.CannotWrite4GbFile
import org.kiwix.kiwixmobile.zimManager.NetworkState.CONNECTED
import org.kiwix.kiwixmobile.zimManager.NetworkState.NOT_CONNECTED
import org.kiwix.kiwixmobile.zimManager.ZimManageViewModel.FileSelectActions.MultiModeFinished
import org.kiwix.kiwixmobile.zimManager.ZimManageViewModel.FileSelectActions.RequestDeleteMultiSelection
import org.kiwix.kiwixmobile.zimManager.ZimManageViewModel.FileSelectActions.RequestMultiSelection
import org.kiwix.kiwixmobile.zimManager.ZimManageViewModel.FileSelectActions.RequestSelect
import org.kiwix.kiwixmobile.zimManager.ZimManageViewModel.FileSelectActions.RequestShareMultiSelection
import org.kiwix.kiwixmobile.zimManager.ZimManageViewModel.FileSelectActions.RestartActionMode
import org.kiwix.kiwixmobile.zimManager.fileselectView.FileSelectListState
import org.kiwix.kiwixmobile.zimManager.fileselectView.effects.DeleteFiles
import org.kiwix.kiwixmobile.zimManager.fileselectView.effects.None
import org.kiwix.kiwixmobile.zimManager.fileselectView.effects.ShareFiles
import org.kiwix.kiwixmobile.zimManager.fileselectView.effects.StartMultiSelection
import org.kiwix.kiwixmobile.zimManager.libraryView.adapter.LibraryListItem
import org.kiwix.sharedFunctions.InstantExecutorExtension
import org.kiwix.sharedFunctions.book
import org.kiwix.sharedFunctions.bookOnDisk

View File

@ -16,7 +16,7 @@
*
*/
package org.kiwix.kiwixmobile.zim_manager.library_view.adapter
package org.kiwix.kiwixmobile.zimManager.libraryView.adapter
import io.mockk.clearAllMocks
import io.mockk.every
@ -25,13 +25,13 @@ import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.kiwix.kiwixmobile.core.entity.LibraryNetworkEntity.Book
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState.CanWrite4GbFile
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState.CannotWrite4GbFile
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState.NotEnoughSpaceFor4GbFile
import org.kiwix.kiwixmobile.zim_manager.Fat32Checker.FileSystemState.Unknown
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryListItem.BookItem
import org.kiwix.kiwixmobile.zimManager.Fat32Checker
import org.kiwix.kiwixmobile.zimManager.Fat32Checker.FileSystemState
import org.kiwix.kiwixmobile.zimManager.Fat32Checker.FileSystemState.CanWrite4GbFile
import org.kiwix.kiwixmobile.zimManager.Fat32Checker.FileSystemState.CannotWrite4GbFile
import org.kiwix.kiwixmobile.zimManager.Fat32Checker.FileSystemState.NotEnoughSpaceFor4GbFile
import org.kiwix.kiwixmobile.zimManager.Fat32Checker.FileSystemState.Unknown
import org.kiwix.kiwixmobile.zimManager.libraryView.adapter.LibraryListItem.BookItem
internal class LibraryListItemTest {

View File

@ -12,12 +12,13 @@ repositories {
}
dependencies {
implementation("com.android.tools.build:gradle:4.0.1")
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72")
implementation("com.hiya:jacoco-android:0.2")
implementation("org.jlleitschuh.gradle:ktlint-gradle:9.2.1")
implementation("com.android.tools.build:gradle:7.1.0")
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.0")
implementation("com.dicedmelon.gradle:jacoco-android:0.1.5")
implementation("org.jlleitschuh.gradle:ktlint-gradle:10.3.0")
implementation("com.google.apis:google-api-services-androidpublisher:v3-rev129-1.25.0")
implementation("io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.9.1")
implementation("io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.20.0")
implementation("com.googlecode.json-simple:json-simple:1.1")
implementation(gradleApi())
implementation(localGroovy())

View File

@ -19,8 +19,8 @@
import org.gradle.api.JavaVersion
object Config {
const val compileSdk = 30
const val compileSdk = 33
const val minSdk = 21
const val targetSdk = 30
const val targetSdk = 33
val javaVersion = JavaVersion.VERSION_1_8
}

View File

@ -32,7 +32,7 @@ object Libs {
* https://developer.android.com/testing
*/
const val espresso_contrib: String = "androidx.test.espresso:espresso-contrib:" +
Versions.androidx_test_espresso
Versions.androidx_test_espresso_contrib
/**
* https://developer.android.com/testing
@ -138,7 +138,7 @@ object Libs {
* https://developer.android.com/topic/libraries/architecture/index.html
*/
const val navigation_ui_ktx: String = "androidx.navigation:navigation-ui-ktx:" +
Versions.androidx_navigation
Versions.navigation_ui_ktx
/**
* https://github.com/google/dagger
@ -420,4 +420,10 @@ object Libs {
* https://developer.android.com/testing
*/
const val junit: String = "androidx.test.ext:junit:" + Versions.junit
/**
* https://developer.android.com/reference/com/google/android/play/core/release-notes
*/
const val google_android_play_core: String =
"com.google.android.play:core:" + Versions.google_android_play_core
}

View File

@ -12,23 +12,29 @@ import org.gradle.plugin.use.PluginDependencySpec
*/
object Versions {
const val google_android_play_core: String = "1.7.3"
const val document_file_version: String = "1.0.1"
const val org_jetbrains_kotlinx_kotlinx_coroutines: String = "1.4.1"
const val androidx_test_espresso: String = "3.3.0"
const val androidx_test_espresso: String = "3.4.0"
const val androidx_test_espresso_contrib: String = "3.3.0"
const val com_squareup_retrofit2: String = "2.9.0"
const val com_squareup_okhttp3: String = "4.9.0"
const val org_jetbrains_kotlin: String = "1.4.20"
const val org_jetbrains_kotlin: String = "1.7.0"
const val androidx_navigation: String = "2.3.1"
const val com_google_dagger: String = "2.29.1"
const val navigation_ui_ktx: String = "2.4.1"
const val com_yahoo_squidb: String = "2.0.0" // available: "3.2.3"
const val com_google_dagger: String = "2.42"
const val com_yahoo_squidb: String = "4.0.0-beta.2" // available: "3.2.3"
const val com_jakewharton: String = "10.2.3"
@ -42,15 +48,15 @@ object Versions {
const val android_arch_lifecycle_extensions: String = "1.1.1"
const val com_android_tools_build_gradle: String = "4.0.1" // available: "4.1.1"
const val com_android_tools_build_gradle: String = "7.1.0"
const val de_fayard_buildsrcversions_gradle_plugin: String = "0.7.0"
const val com_github_triplet_play_gradle_plugin: String = "2.8.0" // available: "3.0.0"
const val com_github_triplet_play_gradle_plugin: String = "3.7.0"
const val javax_annotation_api: String = "1.3.2"
const val leakcanary_android: String = "2.5"
const val leakcanary_android: String = "2.9.1"
const val constraintlayout: String = "2.0.4"

View File

@ -23,8 +23,6 @@ import Libs
import com.android.build.gradle.BaseExtension
import io.gitlab.arturbosch.detekt.extensions.DetektExtension
import org.gradle.api.Project
import org.gradle.api.tasks.testing.Test
import org.gradle.kotlin.dsl.KotlinClosure1
import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.dependencies
import org.gradle.testing.jacoco.plugins.JacocoPluginExtension
@ -39,7 +37,7 @@ class AllProjectConfigurer {
target.plugins.apply("kotlin-android")
target.plugins.apply("kotlin-android-extensions")
target.plugins.apply("kotlin-kapt")
target.plugins.apply("com.hiya.jacoco-android")
target.plugins.apply("com.dicedmelon.gradle.jacoco-android")
target.plugins.apply("org.jlleitschuh.gradle.ktlint")
target.plugins.apply("io.gitlab.arturbosch.detekt")
target.plugins.apply("androidx.navigation.safeargs")
@ -75,19 +73,21 @@ class AllProjectConfigurer {
execution = "ANDROIDX_TEST_ORCHESTRATOR"
unitTests.apply {
isReturnDefaultValues = true
all(KotlinClosure1<Any, Test>({
(this as Test).also { testTask ->
all {
it.also { testTask ->
testTask.useJUnitPlatform()
testTask.testLogging {
setEvents(setOf("passed", "skipped", "failed", "standardOut", "standardError"))
outputs.upToDateWhen { false }
testTask.outputs.upToDateWhen { false }
showStandardStreams = true
}
testTask.extensions
.getByType(JacocoTaskExtension::class.java)
.isIncludeNoLocationClasses = true
.getByType(JacocoTaskExtension::class.java).apply {
isIncludeNoLocationClasses = true
excludes = listOf("jdk.internal.*")
}
}
}, this))
}
}
}
@ -104,16 +104,18 @@ class AllProjectConfigurer {
"CheckResult",
"LabelFor",
"LogConditional",
"ConvertToWebp"
)
warning(
"ConvertToWebp",
//TODO remove this when we remove jcenter from gradle
"JcenterRepositoryObsolete",
"UnknownNullness",
"SelectableText",
"MissingTranslation",
"IconDensities",
"ContentDescription",
"IconDipSize"
"IconDipSize",
"UnusedResources",
"NonConstantResourceId",
"NotifyDataSetChanged"
)
lintConfig = target.rootProject.file("lintConfig.xml")
}
@ -138,15 +140,29 @@ class AllProjectConfigurer {
}
}
fun configureJacoco(target: Project) {
target.configurations.all {
resolutionStrategy {
eachDependency {
if ("org.jacoco" == this.requested.group) {
useVersion("0.8.7")
}
}
}
}
}
fun configurePlugins(target: Project) {
target.run {
configureExtension<AndroidExtensionsExtension> { isExperimental = true }
configureExtension<JacocoPluginExtension> { toolVersion = "0.8.7" }
configureExtension<KtlintExtension> { android.set(true) }
configureExtension<DetektExtension> {
buildUponDefaultConfig = true
allRules = false
config = target.files("${target.rootDir}/config/detekt/detekt.yml")
baseline = project.file("detekt_baseline.xml")
}
}
}
@ -188,6 +204,7 @@ class AllProjectConfigurer {
implementation(Libs.rxandroid)
implementation(Libs.rxjava)
implementation(Libs.preference_ktx)
implementation(Libs.google_android_play_core)
}
}
}

View File

@ -44,6 +44,7 @@ class KiwixConfigurationPlugin : Plugin<Project> {
allProjectConfigurer.configurePlugins(target)
allProjectConfigurer.applyScripts(target)
allProjectConfigurer.configureDependencies(target)
allProjectConfigurer.configureJacoco(target)
}
private fun doDefaultConfiguration(target: Project) {

View File

@ -2,10 +2,10 @@ build:
maxIssues: 0
excludeCorrectable: false
weights:
# complexity: 2
# LongParameterList: 1
# style: 1
# comments: 1
# complexity: 2
# LongParameterList: 1
# style: 1
# comments: 1
config:
validation: true
@ -25,11 +25,11 @@ processors:
console-reports:
active: true
exclude:
- 'ProjectStatisticsReport'
- 'ComplexityReport'
- 'NotificationReport'
# - 'FindingsReport'
- 'FileBasedFindingsReport'
- 'ProjectStatisticsReport'
- 'ComplexityReport'
- 'NotificationReport'
# - 'FindingsReport'
- 'FileBasedFindingsReport'
comments:
active: true
@ -180,130 +180,22 @@ exceptions:
active: true
excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt"
exceptionNames:
- ArrayIndexOutOfBoundsException
- Error
- Exception
- IllegalMonitorStateException
- NullPointerException
- IndexOutOfBoundsException
- RuntimeException
- Throwable
- ArrayIndexOutOfBoundsException
- Error
- Exception
- IllegalMonitorStateException
- NullPointerException
- IndexOutOfBoundsException
- RuntimeException
- Throwable
allowedExceptionNameRegex: "^(_|(ignore|expected).*)"
TooGenericExceptionThrown:
active: true
exceptionNames:
- Error
- Exception
- Throwable
- RuntimeException
formatting:
active: true
android: false
autoCorrect: true
AnnotationOnSeparateLine:
active: false
autoCorrect: true
ChainWrapping:
active: true
autoCorrect: true
CommentSpacing:
active: true
autoCorrect: true
EnumEntryNameCase:
active: false
autoCorrect: true
Filename:
active: true
FinalNewline:
active: true
autoCorrect: true
ImportOrdering:
active: false
autoCorrect: true
Indentation:
active: false
autoCorrect: true
indentSize: 4
continuationIndentSize: 4
MaximumLineLength:
active: true
maxLineLength: 120
ModifierOrdering:
active: true
autoCorrect: true
MultiLineIfElse:
active: true
autoCorrect: true
NoBlankLineBeforeRbrace:
active: true
autoCorrect: true
NoConsecutiveBlankLines:
active: true
autoCorrect: true
NoEmptyClassBody:
active: true
autoCorrect: true
NoEmptyFirstLineInMethodBlock:
active: false
autoCorrect: true
NoLineBreakAfterElse:
active: true
autoCorrect: true
NoLineBreakBeforeAssignment:
active: true
autoCorrect: true
NoMultipleSpaces:
active: true
autoCorrect: true
NoSemicolons:
active: true
autoCorrect: true
NoTrailingSpaces:
active: true
autoCorrect: true
NoUnitReturn:
active: true
autoCorrect: true
NoUnusedImports:
active: true
autoCorrect: true
NoWildcardImports:
active: true
PackageName:
active: true
autoCorrect: true
ParameterListWrapping:
active: true
autoCorrect: true
indentSize: 4
SpacingAroundColon:
active: true
autoCorrect: true
SpacingAroundComma:
active: true
autoCorrect: true
SpacingAroundCurly:
active: true
autoCorrect: true
SpacingAroundDot:
active: true
autoCorrect: true
SpacingAroundKeyword:
active: true
autoCorrect: true
SpacingAroundOperators:
active: true
autoCorrect: true
SpacingAroundParens:
active: true
autoCorrect: true
SpacingAroundRangeOperator:
active: true
autoCorrect: true
StringTemplate:
active: true
autoCorrect: true
- Error
- Exception
- Throwable
- RuntimeException
naming:
active: true

View File

@ -19,8 +19,34 @@ plugins {
plugins.apply(KiwixConfigurationPlugin::class)
apply(plugin = "io.objectbox")
apply(plugin = "com.jakewharton.butterknife")
ext {
set("versionMajor", 3)
set("versionMinor", 6)
set("versionPatch", 0)
}
/*
* max version code: 21-0-0-00-00-00
* our template : UU-D-A-ZZ-YY-XX
* where:
* X = patch version
* Y = minor version
* Z = major version (+ 20 to distinguish from previous, non semantic, versions of the app)
* A = number representing ABI split
* D = number representing density split
* U = unused
*/
fun generateVersionCode() =
20 * 10000 +
ext["versionMajor"] as Int * 10000 +
ext["versionMinor"] as Int * 100 +
ext["versionPatch"] as Int
android {
defaultConfig {
buildConfigField("long", "VERSION_CODE", "${generateVersionCode()}")
}
buildTypes {
getByName("release") {
isMinifyEnabled = false
@ -31,7 +57,6 @@ android {
fun shouldUseLocalVersion() = File(projectDir, "libs").exists()
dependencies {
// use jdk8 java.time backport, as long app < Build.VERSION_CODES.O
implementation(Libs.threetenabp)

View File

@ -1,18 +1,20 @@
<?xml version="1.0" ?>
<?xml version='1.0' encoding='UTF-8'?>
<SmellBaseline>
<Blacklist></Blacklist>
<Whitelist>
<ManuallySuppressedIssues/>
<CurrentIssues>
<ID>EmptyFunctionBlock:BooksOnDiskViewHolder.kt$BookOnDiskViewHolder.BookViewHolder${ }</ID>
<ID>EmptyFunctionBlock:FetchDownloadMonitor.kt$FetchDownloadMonitor.&lt;no name provided&gt;${}</ID>
<ID>EmptyFunctionBlock:FetchDownloadMonitor.kt$FetchDownloadMonitor.&lt;no name provided>${}</ID>
<ID>EmptyFunctionBlock:OnSwipeTouchListener.kt$OnSwipeTouchListener${}</ID>
<ID>ForbiddenComment:JNIInitialiser.kt$JNIInitialiser$// TODO: Consider surfacing to user</ID>
<ID>LongMethod:TabsAdapter.kt$TabsAdapter$onCreateViewHolder</ID>
<ID>LongParameterList:KiwixDialog.kt$KiwixDialog$( val title: Int?, val message: Int?, val positiveMessage: Int, val negativeMessage: Int?, val cancelable: Boolean = true, val icon: Int? = null, val neutralMessage: Int? = null, val getView: (() -&gt; View)? = null )</ID>
<ID>LongParameterList:MainMenu.kt$MainMenu$( private val activity: Activity, zimFileReader: ZimFileReader?, menu: Menu, webViews: MutableList&lt;KiwixWebView&gt;, urlIsValid: Boolean, disableReadAloud: Boolean = false, disableTabs: Boolean = false, private val menuClickListener: MenuClickListener )</ID>
<ID>LongParameterList:MainMenu.kt$MainMenu.Factory$( menu: Menu, webViews: MutableList&lt;KiwixWebView&gt;, urlIsValid: Boolean, menuClickListener: MenuClickListener, disableReadAloud: Boolean, disableTabs: Boolean )</ID>
<ID>ForbiddenComment:NetworkUtilsTest.kt$NetworkUtilsTest$// TODO: find a way to assert regex matching via JUnit and rewrite the test</ID>
<ID>LongMethod:ChunkUtilsTest.kt$ChunkUtilsTest$@Test fun testGetChunks()</ID>
<ID>LongMethod:TabsAdapter.kt$TabsAdapter$@SuppressLint("ResourceType") override fun onCreateViewHolder( parent: ViewGroup, viewType: Int ): ViewHolder</ID>
<ID>LongParameterList:KiwixDialog.kt$KiwixDialog$( val title: Int?, val message: Int?, val positiveMessage: Int, val negativeMessage: Int?, val cancelable: Boolean = true, val icon: Int? = null, val neutralMessage: Int? = null, val getView: (() -> View)? = null )</ID>
<ID>LongParameterList:MainMenu.kt$MainMenu$( private val activity: Activity, zimFileReader: ZimFileReader?, menu: Menu, webViews: MutableList&lt;KiwixWebView>, urlIsValid: Boolean, disableReadAloud: Boolean = false, disableTabs: Boolean = false, private val menuClickListener: MenuClickListener )</ID>
<ID>LongParameterList:MainMenu.kt$MainMenu.Factory$( menu: Menu, webViews: MutableList&lt;KiwixWebView>, urlIsValid: Boolean, menuClickListener: MenuClickListener, disableReadAloud: Boolean, disableTabs: Boolean )</ID>
<ID>LongParameterList:PageTestHelpers.kt$( bookmarkTitle: String = "bookmarkTitle", isSelected: Boolean = false, id: Long = 2, zimId: String = "zimId", zimName: String = "zimName", zimFilePath: String = "zimFilePath", bookmarkUrl: String = "bookmarkUrl", favicon: String = "favicon" )</ID>
<ID>LongParameterList:Repository.kt$Repository$( @param:IO private val io: Scheduler, @param:MainThread private val mainThread: Scheduler, private val bookDao: NewBookDao, private val bookmarksDao: NewBookmarksDao, private val historyDao: HistoryDao, private val languageDao: NewLanguagesDao, private val recentSearchDao: NewRecentSearchDao, private val zimReaderContainer: ZimReaderContainer )</ID>
<ID>LongParameterList:ToolbarScrollingKiwixWebView.kt$ToolbarScrollingKiwixWebView$( context: Context, callback: WebViewCallback, attrs: AttributeSet, nonVideoView: ViewGroup, videoView: ViewGroup, webViewClient: CoreWebViewClient, private val toolbarView: View, private val bottomBarView: View, sharedPreferenceUtil: SharedPreferenceUtil, private val parentNavigationBar: View? = null )</ID>
<ID>MagicNumber:ArticleCount.kt$ArticleCount$1000.0</ID>
<ID>MagicNumber:ArticleCount.kt$ArticleCount$3</ID>
<ID>MagicNumber:CompatFindActionModeCallback.kt$CompatFindActionModeCallback$100</ID>
<ID>MagicNumber:DownloadItem.kt$DownloadItem$1000L</ID>
@ -29,11 +31,15 @@
<ID>MagicNumber:Seconds.kt$Seconds$60</ID>
<ID>MagicNumber:Seconds.kt$Seconds$60.0</ID>
<ID>MagicNumber:TabsAdapter.kt$TabsAdapter$8</ID>
<ID>NestedBlockDepth:FileUtils.kt$FileUtils$deleteZimFile</ID>
<ID>NestedBlockDepth:ImageUtils.kt$ImageUtils$getBitmapFromView</ID>
<ID>NestedBlockDepth:JNIInitialiser.kt$JNIInitialiser$loadICUData</ID>
<ID>NestedBlockDepth:OnSwipeTouchListener.kt$OnSwipeTouchListener.GestureListener$onFling</ID>
<ID>NestedBlockDepth:StorageDeviceUtils.kt$StorageDeviceUtils$canWrite</ID>
<ID>MatchingDeclarationName:PageTestHelpers.kt$PageImpl : Page</ID>
<ID>MaxLineLength:BookUtilsTest.kt$BookUtilsTest$// this case uses the result from the container nested class inside LanguageUtils. It will be tested in LanguageUtilsTest</ID>
<ID>MaxLineLength:MetaLinkNetworkEntityTest.kt$MetaLinkNetworkEntityTest$"http://www.mirrorservice.org/sites/download.kiwix.org/zim/wikipedia/wikipedia_af_all_nopic_2016-05.zim"</ID>
<ID>MaxLineLength:NetworkUtilsTest.kt$NetworkUtilsTest$// Here the Method should return the substring between the first '?' character and the nearest '/' character preceeding it</ID>
<ID>NestedBlockDepth:FileUtils.kt$FileUtils$@JvmStatic @Synchronized fun deleteZimFile(path: String)</ID>
<ID>NestedBlockDepth:ImageUtils.kt$ImageUtils$private fun getBitmapFromView(width: Int, height: Int, viewToDrawFrom: View): Bitmap?</ID>
<ID>NestedBlockDepth:JNIInitialiser.kt$JNIInitialiser$private fun loadICUData(context: Context): String?</ID>
<ID>NestedBlockDepth:OnSwipeTouchListener.kt$OnSwipeTouchListener.GestureListener$override fun onFling( e1: MotionEvent, e2: MotionEvent, velocityX: Float, velocityY: Float ): Boolean</ID>
<ID>NestedBlockDepth:StorageDeviceUtils.kt$StorageDeviceUtils$// Amazingly file.canWrite() does not always return the correct value private fun canWrite(file: File): Boolean</ID>
<ID>PackageNaming:ArticleCount.kt$package org.kiwix.kiwixmobile.core.zim_manager.fileselect_view</ID>
<ID>PackageNaming:BookOnDiskDelegate.kt$package org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter</ID>
<ID>PackageNaming:BooksOnDiskAdapter.kt$package org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter</ID>
@ -45,7 +51,7 @@
<ID>PackageNaming:MountPointProducer.kt$package org.kiwix.kiwixmobile.core.zim_manager</ID>
<ID>PackageNaming:SelectionMode.kt$package org.kiwix.kiwixmobile.core.zim_manager.fileselect_view</ID>
<ID>PackageNaming:TagsView.kt$package org.kiwix.kiwixmobile.core.zim_manager</ID>
<ID>ReturnCount:FileUtils.kt$FileUtils$@JvmStatic fun getAllZimParts(book: Book): List&lt;File&gt;</ID>
<ID>ReturnCount:FileUtils.kt$FileUtils$@JvmStatic fun getAllZimParts(book: Book): List&lt;File></ID>
<ID>ReturnCount:FileUtils.kt$FileUtils$@JvmStatic fun getLocalFilePathByUri( context: Context, uri: Uri ): String?</ID>
<ID>ReturnCount:FileUtils.kt$FileUtils$@JvmStatic fun hasPart(file: File): Boolean</ID>
<ID>ReturnCount:FileUtils.kt$FileUtils$@Synchronized private fun deleteZimFileParts(path: String): Boolean</ID>
@ -68,5 +74,5 @@
<ID>TopLevelPropertyNaming:Bytes.kt$const val Mb = Kb * 1024</ID>
<ID>TopLevelPropertyNaming:Bytes.kt$const val Pb = Tb * 1024</ID>
<ID>TopLevelPropertyNaming:Bytes.kt$const val Tb = Gb * 1024</ID>
</Whitelist>
</CurrentIssues>
</SmellBaseline>

View File

@ -376,9 +376,53 @@
}
],
"relations": []
},
{
"id": "10:3205842982118792800",
"lastPropertyId": "9:5286545520416917562",
"name": "NotesEntity",
"properties": [
{
"id": "1:7685155280711004546",
"name": "id",
"type": 6,
"flags": 1
},
{
"id": "2:4525352813855835976",
"name": "zimId",
"type": 9
},
{
"id": "4:4488566778591049174",
"name": "zimFilePath",
"type": 9
},
{
"id": "5:7998345745727500384",
"name": "noteTitle",
"type": 9
},
{
"id": "7:2160052450778801841",
"name": "favicon",
"type": 9
},
{
"id": "8:4304230668306976620",
"name": "noteFilePath",
"type": 9
},
{
"id": "9:5286545520416917562",
"name": "zimUrl",
"type": 9
}
],
"relations": []
}
],
"lastEntityId": "8:8093454424037540087",
"lastEntityId": "10:3205842982118792800",
"lastIndexId": "4:4868787482832538530",
"lastRelationId": "0:0",
"lastSequenceId": "0:0",
@ -386,7 +430,8 @@
"modelVersionParserMinimum": 5,
"retiredEntityUids": [
349148274283701276,
7257718270326155947
7257718270326155947,
7394649290555378565
],
"retiredIndexUids": [
1293695782925933448,
@ -428,7 +473,16 @@
3550975911715416030,
8949996430663588693,
7554483297276446029,
8085320504542486236
8085320504542486236,
2542527583182293894,
3541826612160879182,
1761651285457480725,
8027872860151528568,
3582932035787057158,
8819082642546094709,
7233601933599801875,
4335394620556092321,
1899740026144478138
],
"retiredRelationUids": [],
"version": 1

View File

@ -376,9 +376,53 @@
}
],
"relations": []
},
{
"id": "10:3205842982118792800",
"lastPropertyId": "8:4304230668306976620",
"name": "NotesEntity",
"properties": [
{
"id": "1:7685155280711004546",
"name": "id",
"type": 6,
"flags": 1
},
{
"id": "2:4525352813855835976",
"name": "zimId",
"type": 9
},
{
"id": "4:4488566778591049174",
"name": "zimFilePath",
"type": 9
},
{
"id": "5:7998345745727500384",
"name": "noteTitle",
"type": 9
},
{
"id": "6:1899740026144478138",
"name": "noteBody",
"type": 9
},
{
"id": "7:2160052450778801841",
"name": "favicon",
"type": 9
},
{
"id": "8:4304230668306976620",
"name": "noteFilePath",
"type": 9
}
],
"relations": []
}
],
"lastEntityId": "8:8093454424037540087",
"lastEntityId": "10:3205842982118792800",
"lastIndexId": "4:4868787482832538530",
"lastRelationId": "0:0",
"lastSequenceId": "0:0",
@ -386,7 +430,8 @@
"modelVersionParserMinimum": 5,
"retiredEntityUids": [
349148274283701276,
7257718270326155947
7257718270326155947,
7394649290555378565
],
"retiredIndexUids": [
1293695782925933448,
@ -428,7 +473,15 @@
3550975911715416030,
8949996430663588693,
7554483297276446029,
8085320504542486236
8085320504542486236,
2542527583182293894,
3541826612160879182,
1761651285457480725,
8027872860151528568,
3582932035787057158,
8819082642546094709,
7233601933599801875,
4335394620556092321
],
"retiredRelationUids": [],
"version": 1

View File

@ -3,28 +3,34 @@
package="org.kiwix.kiwixmobile.core"
android:installLocation="auto">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<!-- Device with versions >= Pie need this permission -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
<uses-permission
android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission" />
<application
android:allowBackup="true"
android:extractNativeLibs="false"
android:fullBackupContent="@xml/backup_rules"
android:dataExtractionRules = "@xml/data_extraction_rules"
android:hardwareAccelerated="true"
android:requestLegacyExternalStorage="true"
android:resizeableActivity="true"
android:supportsRtl="true"
android:theme="@style/KiwixTheme"
android:usesCleartextTraffic="true"
android:requestLegacyExternalStorage="true"
tools:targetApi="q">
tools:targetApi="s">
<!-- Version < 3.0. DeX Mode and Screen Mirroring support -->
<meta-data
@ -37,6 +43,7 @@
<activity
android:name=".error.ErrorActivity"
android:exported="false"
android:process=":error_activity" />
<provider
@ -49,7 +56,9 @@
android:resource="@xml/provider_paths" />
</provider>
<activity android:name=".error.DiagnosticReportActivity" />
<activity
android:name=".error.DiagnosticReportActivity"
android:exported="false" />
</application>
</manifest>

View File

@ -17,6 +17,7 @@
*/
package org.kiwix.kiwixmobile.core
import android.app.AlertDialog
import android.app.Application
import android.content.Context
import android.os.Build
@ -25,6 +26,7 @@ import android.os.Environment.getExternalStorageState
import android.os.StrictMode
import android.os.StrictMode.VmPolicy
import androidx.multidex.MultiDex
import com.google.android.play.core.missingsplits.MissingSplitsManagerFactory
import com.jakewharton.threetenabp.AndroidThreeTen
import org.kiwix.kiwixmobile.core.data.local.KiwixDatabase
import org.kiwix.kiwixmobile.core.di.components.CoreComponent
@ -76,6 +78,15 @@ abstract class CoreApp : Application() {
}
override fun onCreate() {
if (MissingSplitsManagerFactory.create(this).disableAppIfMissingRequiredSplits()) {
AlertDialog.Builder(this)
.setMessage(R.string.missing_split_version_for_objectbox)
.setPositiveButton(R.string.yes) { _, _ ->
android.os.Process.killProcess(android.os.Process.myPid())
}
.show()
return
}
super.onCreate()
instance = this
coreComponent = DaggerCoreComponent.builder()

View File

@ -22,8 +22,10 @@ import android.view.View
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import kotlinx.android.extensions.LayoutContainer
abstract class BaseViewHolder<in ITEM>(override val containerView: View) : ViewHolder(
containerView
), LayoutContainer {
abstract class BaseViewHolder<in ITEM>(override val containerView: View) :
ViewHolder(
containerView
),
LayoutContainer {
abstract fun bind(item: ITEM)
}

View File

@ -30,9 +30,11 @@ import org.kiwix.kiwixmobile.core.reader.ZimFileReader
import javax.inject.Inject
class NewBookmarksDao @Inject constructor(val box: Box<BookmarkEntity>) : PageDao {
fun bookmarks(): Flowable<List<Page>> = box.asFlowable(box.query {
order(BookmarkEntity_.bookmarkTitle)
}).map { it.map(::BookmarkItem) }
fun bookmarks(): Flowable<List<Page>> = box.asFlowable(
box.query {
order(BookmarkEntity_.bookmarkTitle)
}
).map { it.map(::BookmarkItem) }
override fun pages(): Flowable<List<Page>> = bookmarks()
override fun deletePages(pagesToDelete: List<Page>) =
@ -55,7 +57,8 @@ class NewBookmarksDao @Inject constructor(val box: Box<BookmarkEntity>) : PageDa
.or()
.equal(BookmarkEntity_.zimName, zimFileReader?.name ?: "")
order(BookmarkEntity_.bookmarkTitle)
}).map { it.map(BookmarkEntity::bookmarkUrl) }
}
).map { it.map(BookmarkEntity::bookmarkUrl) }
.subscribeOn(Schedulers.io())
fun saveBookmark(bookmarkItem: BookmarkItem) {

View File

@ -0,0 +1,55 @@
/*
* Kiwix Android
* Copyright (c) 2020 Kiwix <android.kiwix.org>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.kiwix.kiwixmobile.core.dao
import io.objectbox.Box
import io.objectbox.kotlin.query
import io.reactivex.Flowable
import org.kiwix.kiwixmobile.core.dao.entities.NotesEntity
import org.kiwix.kiwixmobile.core.dao.entities.NotesEntity_
import org.kiwix.kiwixmobile.core.page.adapter.Page
import org.kiwix.kiwixmobile.core.page.notes.adapter.NoteListItem
import javax.inject.Inject
class NewNoteDao @Inject constructor(val box: Box<NotesEntity>) : PageDao {
fun notes(): Flowable<List<Page>> = box.asFlowable(
box.query {
order(NotesEntity_.noteTitle)
}
).map { it.map(::NoteListItem) }
override fun pages(): Flowable<List<Page>> = notes()
override fun deletePages(pagesToDelete: List<Page>) =
deleteNotes(pagesToDelete as List<NoteListItem>)
fun saveNote(noteItem: NoteListItem) {
box.put(NotesEntity(noteItem))
}
fun deleteNotes(noteList: List<NoteListItem>) {
box.remove(noteList.map(::NotesEntity))
}
fun deleteNote(noteUniqueKey: String) {
box.query {
equal(NotesEntity_.noteTitle, noteUniqueKey)
}.remove()
}
}

View File

@ -19,7 +19,6 @@ package org.kiwix.kiwixmobile.core.dao
import io.objectbox.Box
import io.objectbox.kotlin.query
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.map
import org.kiwix.kiwixmobile.core.dao.entities.RecentSearchEntity
import org.kiwix.kiwixmobile.core.dao.entities.RecentSearchEntity_
@ -31,7 +30,6 @@ class NewRecentSearchDao @Inject constructor(
private val box: Box<RecentSearchEntity>,
private val flowBuilder: FlowBuilder
) {
@OptIn(ExperimentalCoroutinesApi::class)
fun recentSearches(zimId: String?) = flowBuilder.buildCallbackFlow(
box.query {
equal(RecentSearchEntity_.zimId, zimId ?: "")

View File

@ -19,8 +19,8 @@ package org.kiwix.kiwixmobile.core.dao.entities
import io.objectbox.annotation.Entity
import io.objectbox.annotation.Id
import org.kiwix.kiwixmobile.core.page.bookmark.adapter.BookmarkItem
import org.kiwix.kiwixmobile.core.data.local.entity.Bookmark
import org.kiwix.kiwixmobile.core.page.bookmark.adapter.BookmarkItem
@Entity
data class BookmarkEntity(

View File

@ -0,0 +1,46 @@
/*
* Kiwix Android
* Copyright (c) 2022 Kiwix <android.kiwix.org>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.kiwix.kiwixmobile.core.dao.entities
import io.objectbox.annotation.Entity
import io.objectbox.annotation.Id
import io.objectbox.annotation.Unique
import org.kiwix.kiwixmobile.core.page.notes.adapter.NoteListItem
@Entity
data class NotesEntity(
@Id var id: Long = 0L,
val zimId: String,
var zimFilePath: String?,
val zimUrl: String,
@Unique
var noteTitle: String,
var noteFilePath: String,
var favicon: String?
) {
constructor(item: NoteListItem) : this(
id = item.databaseId,
zimId = item.zimId,
zimFilePath = item.zimFilePath,
zimUrl = item.zimUrl,
noteTitle = item.title,
noteFilePath = item.noteFilePath,
favicon = item.favicon
)
}

Some files were not shown because too many files have changed in this diff Show More