Skip to content
Draft
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package io.github.landwarderer.futon.download.ui.worker

import android.content.Context
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.work.NetworkType
import io.github.landwarderer.futon.core.prefs.DownloadFormat
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import java.io.File
import java.lang.reflect.Method

@RunWith(AndroidJUnit4::class)
class DownloadWorkerIntegrationTest {

private lateinit var context: Context

@Before
fun setup() {
context = ApplicationProvider.getApplicationContext()
}

@Test
fun testDownloadWorkerConstraintsPropagation() {
val task = DownloadTask(
mangaId = 1L,
isPaused = false,
isSilent = false,
chaptersIds = longArrayOf(1L),
destination = File(context.cacheDir, "test"),
format = DownloadFormat.SINGLE_CBZ,
allowMeteredNetwork = false,
requiresCharging = true
)

// Using reflection because createConstraints is private
val constraints = callCreateConstraints(task.allowMeteredNetwork, task.requiresCharging)

assertEquals(NetworkType.UNMETERED, constraints.requiredNetworkType)
assertTrue(constraints.requiresCharging())
}

@Test
fun testDownloadWorkerConstraintsPropagationMetered() {
val task = DownloadTask(
mangaId = 1L,
isPaused = false,
isSilent = false,
chaptersIds = longArrayOf(1L),
destination = File(context.cacheDir, "test"),
format = DownloadFormat.SINGLE_CBZ,
allowMeteredNetwork = true,
requiresCharging = false
)

val constraints = callCreateConstraints(task.allowMeteredNetwork, task.requiresCharging)

assertEquals(NetworkType.CONNECTED, constraints.requiredNetworkType)
assertFalse(constraints.requiresCharging())
}

private fun callCreateConstraints(allowMetered: Boolean, requiresCharging: Boolean): androidx.work.Constraints {
val schedulerClass = DownloadWorker.Scheduler::class.java
val method: Method = schedulerClass.getDeclaredMethod("createConstraints", Boolean::class.java, Boolean::class.java)
method.isAccessible = true

// Find a constructor for Scheduler. It's a regular class, so we need an instance if it's not static.
// But in Kotlin, 'class' inside 'class' is static.
// However, 'createConstraints' is a private method, so we still need an instance if it's not a companion object method.
// It's a method of the Scheduler class.

// Since we only want to test the logic of the method, and it doesn't use 'this',
// we can try to invoke it on a dummy instance if needed, or if it's static in Java.
// In Kotlin, it's a member function of Scheduler.

// Let's just create a mock or a dummy instance of Scheduler.
// Scheduler has @Inject constructor(Context, MangaDataRepository, WorkManager)

val constructor = schedulerClass.declaredConstructors[0]
constructor.isAccessible = true
val args = arrayOfNulls<Any>(constructor.parameterCount)
val schedulerInstance = constructor.newInstance(*args)

return method.invoke(schedulerInstance, allowMetered, requiresCharging) as androidx.work.Constraints
}
}
6 changes: 6 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,9 @@
<activity
android:name="io.github.landwarderer.futon.search.ui.multi.SearchActivity"
android:label="@string/search" />
<activity
android:name="io.github.landwarderer.futon.settings.sources.TagsBlacklistActivity"
android:label="@string/tags_blacklist" />
<activity
android:name="io.github.landwarderer.futon.main.ui.protect.ProtectActivity"
android:noHistory="true"
Expand All @@ -252,6 +255,9 @@
android:name="io.github.landwarderer.futon.download.ui.list.DownloadsActivity"
android:label="@string/downloads"
android:launchMode="singleTop" />
<activity
android:name="io.github.landwarderer.futon.download.ui.DownloadQueueActivity"
android:label="@string/download_queue" />
<activity android:name="io.github.landwarderer.futon.image.ui.ImageActivity" />
<activity android:name="io.github.landwarderer.futon.favourites.ui.categories.edit.FavouritesCategoryEditActivity" />
<activity
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import io.github.landwarderer.futon.core.util.ext.printStackTraceDebug
import io.github.landwarderer.futon.core.util.ext.toUriOrNull
import io.github.landwarderer.futon.core.util.ext.withPartialWakeLock
import io.github.landwarderer.futon.core.util.progress.Progress
import io.github.landwarderer.futon.local.ui.LocalIndexUpdateService
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
Expand Down Expand Up @@ -65,6 +66,9 @@ class RestoreService : BaseBackupRestoreService() {
val result = ZipInputStream(contentResolver.openInputStream(source)).use { input ->
repository.restoreBackup(input, sections, progress, isMerge)
}
if (result.isAllSuccess && sections.contains(BackupSection.SETTINGS)) {
startService(Intent(this@RestoreService, LocalIndexUpdateService::class.java))
}
progressUpdateJob?.cancelAndJoin()
showResultNotification(source, result)
}
Expand Down
23 changes: 16 additions & 7 deletions app/src/main/kotlin/io/github/landwarderer/futon/core/AppModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,6 @@ import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import dagger.multibindings.ElementsIntoSet
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import okhttp3.OkHttpClient
import io.github.landwarderer.futon.BuildConfig
import io.github.landwarderer.futon.backups.domain.BackupObserver
import io.github.landwarderer.futon.core.db.MangaDatabase
Expand All @@ -46,7 +41,6 @@ import io.github.landwarderer.futon.core.parser.favicon.FaviconFetcher
import io.github.landwarderer.futon.core.prefs.AppSettings
import io.github.landwarderer.futon.core.ui.image.CoilImageGetter
import io.github.landwarderer.futon.core.ui.util.ActivityRecreationHandle

import io.github.landwarderer.futon.core.util.FileSize
import io.github.landwarderer.futon.core.util.ext.connectivityManager
import io.github.landwarderer.futon.core.util.ext.isLowRamDevice
Expand All @@ -61,10 +55,15 @@ import io.github.landwarderer.futon.local.domain.model.LocalManga
import io.github.landwarderer.futon.main.domain.CoverRestoreInterceptor
import io.github.landwarderer.futon.main.ui.protect.AppProtectHelper
import io.github.landwarderer.futon.main.ui.protect.ScreenshotPolicyHelper
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import io.github.landwarderer.futon.search.ui.MangaSuggestionsProvider
import io.github.landwarderer.futon.sync.domain.SyncController
import io.github.landwarderer.futon.widget.WidgetUpdater
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import okhttp3.OkHttpClient
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import javax.inject.Provider
import javax.inject.Singleton

Expand Down Expand Up @@ -197,6 +196,16 @@ interface AppModule {
@ApplicationContext context: Context,
): WorkManager = WorkManager.getInstance(context)

@Provides
fun provideDownloadQueueDao(
database: MangaDatabase,
): io.github.landwarderer.futon.download.data.dao.DownloadQueueDao = database.getDownloadQueueDao()

@Provides
fun provideMangaDao(
database: MangaDatabase,
): io.github.landwarderer.futon.core.db.dao.MangaDao = database.getMangaDao()

@Provides
@Singleton
@PageCache
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import androidx.room.Database
import androidx.room.InvalidationTracker
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.room.TypeConverters
import androidx.room.migration.Migration
import io.github.landwarderer.futon.bookmarks.data.BookmarkEntity
import io.github.landwarderer.futon.bookmarks.data.BookmarksDao
import io.github.landwarderer.futon.core.db.converters.DataConverters
import io.github.landwarderer.futon.core.db.dao.ChaptersDao
import io.github.landwarderer.futon.core.db.dao.ExternalExtensionRepoDao
import io.github.landwarderer.futon.core.db.dao.MangaDao
Expand Down Expand Up @@ -42,6 +44,7 @@ import io.github.landwarderer.futon.core.db.migrations.Migration24To25
import io.github.landwarderer.futon.core.db.migrations.Migration25To26
import io.github.landwarderer.futon.core.db.migrations.Migration26To27
import io.github.landwarderer.futon.core.db.migrations.Migration27To28
import io.github.landwarderer.futon.core.db.migrations.Migration28To29
import io.github.landwarderer.futon.core.db.migrations.Migration2To3
import io.github.landwarderer.futon.core.db.migrations.Migration3To4
import io.github.landwarderer.futon.core.db.migrations.Migration4To5
Expand All @@ -51,6 +54,8 @@ import io.github.landwarderer.futon.core.db.migrations.Migration7To8
import io.github.landwarderer.futon.core.db.migrations.Migration8To9
import io.github.landwarderer.futon.core.db.migrations.Migration9To10
import io.github.landwarderer.futon.core.util.ext.processLifecycleScope
import io.github.landwarderer.futon.download.data.dao.DownloadQueueDao
import io.github.landwarderer.futon.download.data.entity.DownloadQueueEntity
import io.github.landwarderer.futon.favourites.data.FavouriteCategoriesDao
import io.github.landwarderer.futon.favourites.data.FavouriteCategoryEntity
import io.github.landwarderer.futon.favourites.data.FavouriteEntity
Expand All @@ -73,17 +78,19 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch

const val DATABASE_VERSION = 28
const val DATABASE_VERSION = 29

@Database(
entities = [
MangaEntity::class, TagEntity::class, HistoryEntity::class, MangaTagsEntity::class, ChapterEntity::class,
FavouriteCategoryEntity::class, FavouriteEntity::class, MangaPrefsEntity::class, TrackEntity::class,
TrackLogEntity::class, SuggestionEntity::class, BookmarkEntity::class, ScrobblingEntity::class,
MangaSourceEntity::class, StatsEntity::class, LocalMangaIndexEntity::class, ExternalExtensionRepoEntity::class,
DownloadQueueEntity::class,
],
version = DATABASE_VERSION,
)
@TypeConverters(DataConverters::class)
abstract class MangaDatabase : RoomDatabase() {

abstract fun getHistoryDao(): HistoryDao
Expand Down Expand Up @@ -117,6 +124,8 @@ abstract class MangaDatabase : RoomDatabase() {
abstract fun getChaptersDao(): ChaptersDao

abstract fun getExternalExtensionRepoDao(): ExternalExtensionRepoDao

abstract fun getDownloadQueueDao(): DownloadQueueDao
}

fun getDatabaseMigrations(context: Context): Array<Migration> = arrayOf(
Expand Down Expand Up @@ -148,6 +157,7 @@ fun getDatabaseMigrations(context: Context): Array<Migration> = arrayOf(
Migration25To26(),
Migration26To27(),
Migration27To28(),
Migration28To29(),
)

fun MangaDatabase(context: Context): MangaDatabase = Room
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ const val TABLE_MANGA_TAGS = "manga_tags"
const val TABLE_SOURCES = "sources"
const val TABLE_CHAPTERS = "chapters"
const val TABLE_PREFERENCES = "preferences"
const val TABLE_DOWNLOAD_QUEUE = "download_queue"
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package io.github.landwarderer.futon.core.db.converters

import androidx.room.TypeConverter

class DataConverters {
@TypeConverter
fun fromLongArray(value: LongArray?): String? {
return value?.joinToString(",")
}

@TypeConverter
fun toLongArray(value: String?): LongArray? {
return value?.split(",")?.filter { it.isNotEmpty() }?.map { it.toLong() }?.toLongArray()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ abstract class TagsDao {
)
abstract suspend fun findTags(query: String, limit: Int): List<TagEntity>

@Query("SELECT * FROM tags WHERE title LIKE :query GROUP BY title LIMIT :limit")
abstract suspend fun searchAllTags(query: String, limit: Int): List<TagEntity>

@Query(
"""
SELECT tags.* FROM manga_tags
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,16 @@ package io.github.landwarderer.futon.core.db.entity

import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.Index
import androidx.room.PrimaryKey
import io.github.landwarderer.futon.core.db.TABLE_TAGS

@Entity(tableName = TABLE_TAGS)
@Entity(
tableName = TABLE_TAGS,
indices = [
Index(value = ["title"]),
],
)
data class TagEntity(
@PrimaryKey(autoGenerate = false)
@ColumnInfo(name = "tag_id") val id: Long,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package io.github.landwarderer.futon.core.db.migrations

import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase

class Migration28To29 : Migration(28, 29) {

override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL(
"""
CREATE TABLE IF NOT EXISTS `download_queue` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
`manga_id` INTEGER NOT NULL,
`chapters_ids` TEXT NOT NULL,
`priority` INTEGER NOT NULL,
`created_at` INTEGER NOT NULL,
`wifi_only` INTEGER NOT NULL,
`charging_only` INTEGER NOT NULL,
`off_peak_only` INTEGER NOT NULL,
FOREIGN KEY(`manga_id`) REFERENCES `manga`(`manga_id`) ON UPDATE NO ACTION ON DELETE CASCADE
)
""".trimIndent()
)
db.execSQL("CREATE INDEX IF NOT EXISTS `index_download_queue_manga_id` ON `download_queue` (`manga_id`)")
db.execSQL("CREATE INDEX IF NOT EXISTS `index_tags_title` ON `tags` (`title`)")
db.execSQL("CREATE INDEX IF NOT EXISTS `index_download_queue_priority` ON `download_queue` (`priority`)")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ import io.github.landwarderer.futon.settings.SettingsActivity
import io.github.landwarderer.futon.settings.about.AppUpdateActivity
import io.github.landwarderer.futon.settings.override.OverrideConfigActivity
import io.github.landwarderer.futon.settings.reader.ReaderTapGridConfigActivity
import io.github.landwarderer.futon.settings.sources.TagsBlacklistActivity
import io.github.landwarderer.futon.settings.sources.auth.SourceAuthActivity
import io.github.landwarderer.futon.settings.sources.catalog.SourcesCatalogActivity
import io.github.landwarderer.futon.settings.sources.extension.ExtensionDownloaderActivity
Expand Down Expand Up @@ -212,6 +213,10 @@ class AppRouter private constructor(

fun openDownloads() = startActivity(DownloadsActivity::class.java)

fun openDownloadQueue() {
startActivity(Intent(contextOrNull() ?: return, Class.forName("io.github.landwarderer.futon.download.ui.DownloadQueueActivity")))
}

fun openDirectoriesSettings() = startActivity(MangaDirectoriesActivity::class.java)

fun openBrowser(url: String, source: MangaSource?, title: String?) {
Expand Down Expand Up @@ -313,6 +318,8 @@ class AppRouter private constructor(
)
}

fun openTagsBlacklist() = startActivity(TagsBlacklistActivity::class.java)

fun openStatistic() = startActivity(StatsActivity::class.java)

@CheckResult
Expand Down
Loading