Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reintroduced the Fetch Library for Downloading ZIM files. #4143

Merged
merged 15 commits into from
Dec 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ are interested in our custom apps, they have their own repo
- [JUnit5](https://github.com/junit-team/junit5/) - The next generation of JUnit
- [AssertJ](https://github.com/joel-costigliola/assertj-core) - Fluent assertions for test code
- [ZXing](https://github.com/zxing/zxing) - Barcode scanning library for Java, Android
- [Fetch](https://github.com/tonyofrancis/Fetch) - A customizable file download manager library for
Android

## Contributing

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class ObjectBoxToLibkiwixMigratorTest : BaseActivityTest() {
private val objectBoxToLibkiwixMigrator = ObjectBoxToLibkiwixMigrator()

// take the existing boxStore object
private val boxStore: BoxStore? = DatabaseModule.boxStore
private var boxStore: BoxStore? = null
private lateinit var zimFile: File
private lateinit var box: Box<BookmarkEntity>
private val expectedZimName = "Alpine_Linux"
Expand Down Expand Up @@ -115,6 +115,7 @@ class ObjectBoxToLibkiwixMigratorTest : BaseActivityTest() {
it.navigate(R.id.libraryFragment)
}
}
boxStore = DatabaseModule.boxStore
CoreApp.coreComponent.inject(objectBoxToLibkiwixMigrator)
setUpObjectBoxAndData()
}
Expand All @@ -126,7 +127,7 @@ class ObjectBoxToLibkiwixMigratorTest : BaseActivityTest() {
" check is your application running"
)
}
box = boxStore.boxFor(BookmarkEntity::class.java)
box = boxStore!!.boxFor(BookmarkEntity::class.java)

// clear the data before running the test case
clearBookmarks()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,15 @@
package org.kiwix.kiwixmobile

import android.content.Context
import androidx.core.content.edit
import androidx.lifecycle.Lifecycle
import androidx.preference.PreferenceManager
import androidx.room.Room
import androidx.test.core.app.ActivityScenario
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import io.objectbox.Box
import io.objectbox.BoxStore
import kotlinx.coroutines.flow.first
Expand All @@ -44,7 +50,10 @@ import org.kiwix.kiwixmobile.core.data.KiwixRoomDatabase
import org.kiwix.kiwixmobile.core.data.remote.ObjectBoxToRoomMigrator
import org.kiwix.kiwixmobile.core.di.modules.DatabaseModule
import org.kiwix.kiwixmobile.core.page.notes.adapter.NoteListItem
import org.kiwix.kiwixmobile.core.utils.LanguageUtils
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
import org.kiwix.kiwixmobile.main.KiwixMainActivity
import org.kiwix.kiwixmobile.testutils.TestUtils

@RunWith(AndroidJUnit4::class)
class ObjectBoxToRoomMigratorTest {
Expand All @@ -57,6 +66,33 @@ class ObjectBoxToRoomMigratorTest {
@Before
fun setup() {
context = ApplicationProvider.getApplicationContext()
UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()).apply {
if (TestUtils.isSystemUINotRespondingDialogVisible(this)) {
TestUtils.closeSystemDialogs(context, this)
}
waitForIdle()
}
PreferenceManager.getDefaultSharedPreferences(context).edit {
putBoolean(SharedPreferenceUtil.PREF_SHOW_INTRO, false)
putBoolean(SharedPreferenceUtil.PREF_IS_TEST, true)
putBoolean(SharedPreferenceUtil.IS_PLAY_STORE_BUILD, true)
putString(SharedPreferenceUtil.PREF_LANG, "en")
putLong(
SharedPreferenceUtil.PREF_LAST_DONATION_POPUP_SHOWN_IN_MILLISECONDS,
System.currentTimeMillis()
)
}
ActivityScenario.launch(KiwixMainActivity::class.java).apply {
moveToState(Lifecycle.State.RESUMED)
onActivity {
LanguageUtils.handleLocaleChange(
it,
"en",
SharedPreferenceUtil(context)
)
it.navigate(R.id.libraryFragment)
}
}
kiwixRoomDatabase = Room.inMemoryDatabaseBuilder(context, KiwixRoomDatabase::class.java)
.allowMainThreadQueries()
.build()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,9 @@ class DownloadRobot : BaseRobot() {

fun assertDownloadPaused() {
testFlakyView({
onView(withSubstring(context.getString(string.paused_state))).check(matches(isDisplayed()))
onView(
withText(org.kiwix.kiwixmobile.core.R.string.paused_state)
).check(matches(isDisplayed()))
})
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,10 @@ class ImportBookmarkTest : BaseActivityTest() {
@JvmField
var retryRule = RetryRule()

private val boxStore: BoxStore? = DatabaseModule.boxStore
private var boxStore: BoxStore? = null
private val library = Library()
private val manager = Manager(library)
private val newBookDao = NewBookDao(boxStore!!.boxFor(BookOnDiskEntity::class.java))
private lateinit var newBookDao: NewBookDao
private lateinit var libkiwixBookmarks: LibkiwixBookmarks

private val bookmarkXmlData = """
Expand Down Expand Up @@ -127,6 +127,8 @@ class ImportBookmarkTest : BaseActivityTest() {
)
}
}
boxStore = DatabaseModule.boxStore
newBookDao = NewBookDao(boxStore!!.boxFor(BookOnDiskEntity::class.java))
libkiwixBookmarks =
LibkiwixBookmarks(
library,
Expand Down
3 changes: 0 additions & 3 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@
android:name="android.permission.NEARBY_WIFI_DEVICES"
android:usesPermissionFlags="neverForLocation"
tools:targetApi="s" />
<!-- Device with versions >= Pie need this permission -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />

<application
android:name=".KiwixApp"
Expand Down
5 changes: 5 additions & 0 deletions app/src/main/assets/credits.html
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,11 @@ <h3>Apache</h3>
<br/>
Copyright 2015 RxAndroid Authors
</li>
<li>
<b>Fetch</b>
<br/>
Copyright (C) 2017 Tonyo Francis
</li>
</ul>
</p>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import android.content.Intent
import android.content.pm.PackageManager.PERMISSION_GRANTED
import android.net.ConnectivityManager
import android.os.Build
import com.tonyodev.fetch2.Status
import android.os.Bundle
import android.provider.Settings
import android.view.LayoutInflater
Expand Down Expand Up @@ -62,7 +63,6 @@ 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.core.downloader.Downloader
import org.kiwix.kiwixmobile.core.downloader.downloadManager.Status
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.hasNotificationPermission
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.isManageExternalStoragePermissionGranted
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.navigate
Expand Down Expand Up @@ -154,7 +154,7 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
}
downloader.pauseResumeDownload(
it.downloadId,
it.downloadState.toReadableState(context).contains(getString(string.paused_state))
it.downloadState.toReadableState(context) == getString(string.paused_state)
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
package org.kiwix.kiwixmobile.zimManager.libraryView.adapter

import androidx.annotation.StringRes
import org.kiwix.kiwixmobile.core.downloader.downloadManager.Status
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,12 @@ package org.kiwix.kiwixmobile.zimManager.libraryView.adapter

import android.view.View
import androidx.lifecycle.LifecycleCoroutineScope
import com.tonyodev.fetch2.Status
import kotlinx.coroutines.launch
import org.kiwix.kiwixmobile.R
import org.kiwix.kiwixmobile.core.R.string
import org.kiwix.kiwixmobile.core.base.adapter.BaseViewHolder
import org.kiwix.kiwixmobile.core.downloader.downloadManager.Error
import org.kiwix.kiwixmobile.core.downloader.downloadManager.Status
import org.kiwix.kiwixmobile.core.downloader.model.Base64String
import org.kiwix.kiwixmobile.core.downloader.model.DownloadState
import org.kiwix.kiwixmobile.core.extensions.setBitmap
import org.kiwix.kiwixmobile.core.extensions.setImageDrawableCompat
import org.kiwix.kiwixmobile.core.extensions.setTextAndVisibility
Expand Down Expand Up @@ -128,44 +126,18 @@ sealed class LibraryViewHolder<in T : LibraryListItem>(containerView: View) :
itemDownloadBinding.downloadState.text =
item.downloadState.toReadableState(containerView.context).also {
val pauseResumeIconId =
if (it.contains(itemDownloadBinding.root.context.getString(string.paused_state))) {
if (it == itemDownloadBinding.root.context.getString(string.paused_state)) {
R.drawable.ic_play_24dp
} else {
R.drawable.ic_pause_24dp
}
itemDownloadBinding.pauseResume.apply {
setImageDrawableCompat(pauseResumeIconId)
if (shouldEnablePauseResumeButton(item.downloadState)) {
isEnabled = true
alpha = 1f
} else {
isEnabled = false
alpha = 0.5f
}
}
itemDownloadBinding.pauseResume.setImageDrawableCompat(pauseResumeIconId)
}
if (item.currentDownloadState == Status.FAILED) {
clickAction.invoke(item)
}
itemDownloadBinding.eta.text = item.readableEta
}

private fun shouldEnablePauseResumeButton(
downloadState: DownloadState
): Boolean =
when (downloadState) {
is DownloadState.Failed -> false
is DownloadState.Paused -> shouldEnablePauseResumeButtonForPauseReason(downloadState.reason)
else -> true
}

/**
* Disable the pause button when the DownloadManager is waiting for
* Wi-Fi or network connection. This prevents the user from trying
* to resume the download, as it will not work without a connection.
*/
private fun shouldEnablePauseResumeButtonForPauseReason(reason: Error?): Boolean =
reason !in listOf(Error.QUEUED_FOR_WIFI, Error.WAITING_FOR_NETWORK)
}

class LibraryDividerViewHolder(private val libraryDividerBinding: LibraryDividerBinding) :
Expand Down
2 changes: 1 addition & 1 deletion buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ repositories {

dependencies {
implementation("com.android.tools.build:gradle:8.1.3")
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.20")
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:2.0.0")
implementation("org.jacoco:org.jacoco.core:0.8.12")
implementation("org.jlleitschuh.gradle:ktlint-gradle:10.3.0")
implementation("com.google.apis:google-api-services-androidpublisher:v3-rev20230406-2.0.0") {
Expand Down
8 changes: 7 additions & 1 deletion buildSrc/src/main/kotlin/Libs.kt
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ object Libs {
/**
* https://kotlinlang.org/
*/
const val kotlin_stdlib_jdk7: String = "org.jetbrains.kotlin:kotlin-stdlib-jdk7:" +
const val kotlin_stdlib_jdk8: String = "org.jetbrains.kotlin:kotlin-stdlib-jdk8:" +
Versions.org_jetbrains_kotlin

/**
Expand Down Expand Up @@ -357,4 +357,10 @@ object Libs {
* https://github.com/slackhq/keeper
*/
const val keeper = "com.slack.keeper:keeper:" + Versions.keeper

/**
* https://github.com/tonyofrancis/Fetch
*/
const val fetch: String = "com.github.tonyofrancis.Fetch:fetch2:" + Versions.fetch
const val fetchOkhttp: String = "com.github.tonyofrancis.Fetch:fetch2okhttp:" + Versions.fetch
}
8 changes: 5 additions & 3 deletions buildSrc/src/main/kotlin/Versions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,21 @@ object Versions {

const val com_squareup_okhttp3: String = "4.12.0"

const val org_jetbrains_kotlin: String = "1.9.20"
const val org_jetbrains_kotlin: String = "2.0.0"

const val androidx_navigation: String = "2.5.3"

const val navigation_ui_ktx: String = "2.4.1"

const val com_google_dagger: String = "2.48.1"
const val com_google_dagger: String = "2.53.1"

const val androidx_test: String = "1.6.1"

const val androidx_test_core: String = "1.6.1"

const val androidx_test_orchestrator: String = "1.5.0"

const val io_objectbox: String = "3.5.0"
const val io_objectbox: String = "4.0.3"

const val io_mockk: String = "1.13.13"

Expand Down Expand Up @@ -109,6 +109,8 @@ object Versions {
const val zxing = "3.5.3"

const val keeper = "0.16.1"

const val fetch: String = "3.4.1"
}

/**
Expand Down
9 changes: 7 additions & 2 deletions buildSrc/src/main/kotlin/plugin/AllProjectConfigurer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.dependencies
import org.gradle.testing.jacoco.plugins.JacocoPluginExtension
import org.gradle.testing.jacoco.plugins.JacocoTaskExtension
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jlleitschuh.gradle.ktlint.KtlintExtension

Expand Down Expand Up @@ -75,7 +76,10 @@ class AllProjectConfigurer {
targetCompatibility = Config.javaVersion
}
target.tasks.withType(KotlinCompile::class.java) {
kotlinOptions.jvmTarget = "1.8"
compilerOptions {
jvmTarget.set(JvmTarget.JVM_1_8)
freeCompilerArgs.add("-Xjvm-default=all-compatibility")
}
}
buildFeatures.apply {
viewBinding = true
Expand Down Expand Up @@ -191,7 +195,7 @@ class AllProjectConfigurer {

fun configureDependencies(target: Project) {
target.dependencies {
implementation(Libs.kotlin_stdlib_jdk7)
implementation(Libs.kotlin_stdlib_jdk8)
implementation(Libs.appcompat)
implementation(Libs.material)
implementation(Libs.constraintlayout)
Expand Down Expand Up @@ -227,6 +231,7 @@ class AllProjectConfigurer {
implementation(Libs.roomRxjava2)
kapt(Libs.roomCompiler)
implementation(Libs.tracing)
implementation(Libs.fetchOkhttp)
}
}
}
4 changes: 4 additions & 0 deletions core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,8 @@ dependencies {
implementation(Libs.kotlinx_coroutines_android)
implementation(Libs.kotlinx_coroutines_rx3)
implementation(Libs.zxing)
api(Libs.fetch) {
// Todo: Will remove this when we add support for Android 15
exclude("androidx.core", "core-ktx")
}
}
6 changes: 6 additions & 0 deletions core/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@

<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
<!-- Device with versions >= Pie need this permission -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />

<queries>
<intent>
Expand Down Expand Up @@ -88,5 +91,8 @@
android:name=".error.DiagnosticReportActivity"
android:exported="false" />
<service android:name=".read_aloud.ReadAloudService" />
<service
android:name=".downloader.downloadManager.DownloadMonitorService"
android:foregroundServiceType="dataSync" />
</application>
</manifest>
Loading