Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2374c96009 | |||
| 2dd51117e9 | |||
| 6c5f3c7d97 | |||
| 626bb20edb | |||
| d363869dab | |||
| 774f33c63d | |||
| 079427346a | |||
| a1a3125834 |
@@ -16,7 +16,7 @@ android {
|
|||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 29
|
targetSdkVersion 29
|
||||||
versionCode gitCommits
|
versionCode gitCommits
|
||||||
versionName '0.5-rc1'
|
versionName '0.5'
|
||||||
|
|
||||||
kapt {
|
kapt {
|
||||||
arguments {
|
arguments {
|
||||||
@@ -65,13 +65,15 @@ dependencies {
|
|||||||
implementation 'androidx.activity:activity-ktx:1.2.0-alpha06'
|
implementation 'androidx.activity:activity-ktx:1.2.0-alpha06'
|
||||||
implementation 'androidx.fragment:fragment-ktx:1.3.0-alpha06'
|
implementation 'androidx.fragment:fragment-ktx:1.3.0-alpha06'
|
||||||
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.0-alpha05'
|
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.0-alpha05'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta7'
|
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta8'
|
||||||
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
||||||
implementation 'androidx.recyclerview:recyclerview:1.2.0-alpha04'
|
implementation 'androidx.recyclerview:recyclerview:1.2.0-alpha04'
|
||||||
implementation 'androidx.viewpager2:viewpager2:1.1.0-alpha01'
|
implementation 'androidx.viewpager2:viewpager2:1.1.0-alpha01'
|
||||||
implementation 'androidx.preference:preference-ktx:1.1.1'
|
implementation 'androidx.preference:preference-ktx:1.1.1'
|
||||||
implementation 'androidx.work:work-runtime-ktx:2.4.0-rc01'
|
implementation 'androidx.work:work-runtime-ktx:2.4.0-rc01'
|
||||||
implementation 'com.google.android.material:material:1.3.0-alpha01'
|
implementation 'com.google.android.material:material:1.3.0-alpha01'
|
||||||
|
//noinspection LifecycleAnnotationProcessorWithJava8
|
||||||
|
kapt 'androidx.lifecycle:lifecycle-compiler:2.3.0-alpha05'
|
||||||
|
|
||||||
implementation 'androidx.room:room-runtime:2.2.5'
|
implementation 'androidx.room:room-runtime:2.2.5'
|
||||||
implementation 'androidx.room:room-ktx:2.2.5'
|
implementation 'androidx.room:room-ktx:2.2.5'
|
||||||
@@ -83,8 +85,8 @@ dependencies {
|
|||||||
implementation 'com.github.moxy-community:moxy-ktx:2.1.2'
|
implementation 'com.github.moxy-community:moxy-ktx:2.1.2'
|
||||||
kapt 'com.github.moxy-community:moxy-compiler:2.1.2'
|
kapt 'com.github.moxy-community:moxy-compiler:2.1.2'
|
||||||
|
|
||||||
implementation 'com.squareup.okhttp3:okhttp:4.7.2'
|
implementation 'com.squareup.okhttp3:okhttp:4.8.0'
|
||||||
implementation 'com.squareup.okio:okio:2.6.0'
|
implementation 'com.squareup.okio:okio:2.7.0'
|
||||||
implementation 'org.jsoup:jsoup:1.13.1'
|
implementation 'org.jsoup:jsoup:1.13.1'
|
||||||
|
|
||||||
implementation 'org.koin:koin-android:2.1.5'
|
implementation 'org.koin:koin-android:2.1.5'
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package org.koitharu.kotatsu.core.parser
|
|||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.webkit.MimeTypeMap
|
||||||
import androidx.collection.ArraySet
|
import androidx.collection.ArraySet
|
||||||
import androidx.core.net.toFile
|
import androidx.core.net.toFile
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
@@ -91,7 +92,7 @@ class LocalMangaRepository : MangaRepository, KoinComponent {
|
|||||||
coverUrl = zipUri(
|
coverUrl = zipUri(
|
||||||
file,
|
file,
|
||||||
entryName = index.getCoverEntry()
|
entryName = index.getCoverEntry()
|
||||||
?: findFirstEntry(zip.entries())?.name.orEmpty()
|
?: findFirstEntry(zip.entries(), isImage = true)?.name.orEmpty()
|
||||||
),
|
),
|
||||||
chapters = info.chapters?.map { c -> c.copy(url = fileUri) }
|
chapters = info.chapters?.map { c -> c.copy(url = fileUri) }
|
||||||
)
|
)
|
||||||
@@ -110,7 +111,7 @@ class LocalMangaRepository : MangaRepository, KoinComponent {
|
|||||||
title = title,
|
title = title,
|
||||||
url = fileUri,
|
url = fileUri,
|
||||||
source = MangaSource.LOCAL,
|
source = MangaSource.LOCAL,
|
||||||
coverUrl = zipUri(file, findFirstEntry(zip.entries())?.name.orEmpty()),
|
coverUrl = zipUri(file, findFirstEntry(zip.entries(), isImage = true)?.name.orEmpty()),
|
||||||
chapters = chapters.sortedWith(AlphanumComparator()).mapIndexed { i, s ->
|
chapters = chapters.sortedWith(AlphanumComparator()).mapIndexed { i, s ->
|
||||||
MangaChapter(
|
MangaChapter(
|
||||||
id = "$i$s".longHashCode(),
|
id = "$i$s".longHashCode(),
|
||||||
@@ -136,11 +137,19 @@ class LocalMangaRepository : MangaRepository, KoinComponent {
|
|||||||
private fun zipUri(file: File, entryName: String) =
|
private fun zipUri(file: File, entryName: String) =
|
||||||
Uri.fromParts("cbz", file.path, entryName).toString()
|
Uri.fromParts("cbz", file.path, entryName).toString()
|
||||||
|
|
||||||
private fun findFirstEntry(entries: Enumeration<out ZipEntry>): ZipEntry? {
|
private fun findFirstEntry(entries: Enumeration<out ZipEntry>, isImage: Boolean): ZipEntry? {
|
||||||
val list = entries.toList()
|
val list = entries.toList()
|
||||||
.filterNot { it.isDirectory }
|
.filterNot { it.isDirectory }
|
||||||
.sortedWith(compareBy(AlphanumComparator()) { x -> x.name })
|
.sortedWith(compareBy(AlphanumComparator()) { x -> x.name })
|
||||||
return list.firstOrNull()
|
return if (isImage) {
|
||||||
|
val map = MimeTypeMap.getSingleton()
|
||||||
|
list.firstOrNull {
|
||||||
|
map.getMimeTypeFromExtension(it.name.substringAfterLast('.'))
|
||||||
|
?.startsWith("image/") == true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
list.firstOrNull()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override val sortOrders = emptySet<SortOrder>()
|
override val sortOrders = emptySet<SortOrder>()
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ abstract class GroupleRepository(loaderContext: MangaLoaderContext) :
|
|||||||
val doc = when {
|
val doc = when {
|
||||||
!query.isNullOrEmpty() -> loaderContext.httpPost(
|
!query.isNullOrEmpty() -> loaderContext.httpPost(
|
||||||
"https://$domain/search",
|
"https://$domain/search",
|
||||||
mapOf("q" to query, "offset" to offset.toString())
|
mapOf("q" to query.urlEncoded(), "offset" to offset.toString())
|
||||||
)
|
)
|
||||||
tag == null -> loaderContext.httpGet(
|
tag == null -> loaderContext.httpGet(
|
||||||
"https://$domain/list?sortType=${getSortKey(
|
"https://$domain/list?sortType=${getSortKey(
|
||||||
|
|||||||
@@ -5,6 +5,6 @@ import org.koitharu.kotatsu.domain.MangaLoaderContext
|
|||||||
|
|
||||||
class ReadmangaRepository(loaderContext: MangaLoaderContext) : GroupleRepository(loaderContext) {
|
class ReadmangaRepository(loaderContext: MangaLoaderContext) : GroupleRepository(loaderContext) {
|
||||||
|
|
||||||
override val defaultDomain = "readmanga.me"
|
override val defaultDomain = "readmanga.live"
|
||||||
override val source = MangaSource.READMANGA_RU
|
override val source = MangaSource.READMANGA_RU
|
||||||
}
|
}
|
||||||
@@ -18,7 +18,12 @@ class MangaSearchRepository : KoinComponent {
|
|||||||
var isEmitted = false
|
var isEmitted = false
|
||||||
for (source in sources) {
|
for (source in sources) {
|
||||||
val list = lists.getOrPut(source) {
|
val list = lists.getOrPut(source) {
|
||||||
MangaProviderFactory.create(source).getList(0, query, SortOrder.POPULARITY)
|
try {
|
||||||
|
MangaProviderFactory.create(source).getList(0, query, SortOrder.POPULARITY)
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
e.printStackTrace()
|
||||||
|
emptyList<Manga>()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (i < list.size) {
|
if (i < list.size) {
|
||||||
emit(list.subList(i, (i + batchSize).coerceAtMost(list.lastIndex)))
|
emit(list.subList(i, (i + batchSize).coerceAtMost(list.lastIndex)))
|
||||||
|
|||||||
@@ -6,10 +6,8 @@ import org.koin.core.inject
|
|||||||
import org.koitharu.kotatsu.core.db.MangaDatabase
|
import org.koitharu.kotatsu.core.db.MangaDatabase
|
||||||
import org.koitharu.kotatsu.core.db.entity.TrackEntity
|
import org.koitharu.kotatsu.core.db.entity.TrackEntity
|
||||||
import org.koitharu.kotatsu.core.db.entity.TrackLogEntity
|
import org.koitharu.kotatsu.core.db.entity.TrackLogEntity
|
||||||
import org.koitharu.kotatsu.core.model.Manga
|
import org.koitharu.kotatsu.core.model.*
|
||||||
import org.koitharu.kotatsu.core.model.MangaChapter
|
import org.koitharu.kotatsu.domain.MangaProviderFactory
|
||||||
import org.koitharu.kotatsu.core.model.MangaTracking
|
|
||||||
import org.koitharu.kotatsu.core.model.TrackingLogItem
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class TrackingRepository : KoinComponent {
|
class TrackingRepository : KoinComponent {
|
||||||
@@ -24,12 +22,16 @@ class TrackingRepository : KoinComponent {
|
|||||||
suspend fun getAllTracks(): List<MangaTracking> {
|
suspend fun getAllTracks(): List<MangaTracking> {
|
||||||
val favourites = db.favouritesDao.findAllManga()
|
val favourites = db.favouritesDao.findAllManga()
|
||||||
val history = db.historyDao.findAllManga()
|
val history = db.historyDao.findAllManga()
|
||||||
val manga = (favourites + history).distinctBy { it.id }
|
val mangas = (favourites + history).distinctBy { it.id }
|
||||||
val tracks = db.tracksDao.findAll().groupBy { it.mangaId }
|
val tracks = db.tracksDao.findAll().groupBy { it.mangaId }
|
||||||
return manga.map { m ->
|
return mangas.mapNotNull { me ->
|
||||||
val track = tracks[m.id]?.singleOrNull()
|
var manga = me.toManga()
|
||||||
|
if (manga.source == MangaSource.LOCAL) {
|
||||||
|
manga = MangaProviderFactory.createLocal().getRemoteManga(manga) ?: return@mapNotNull null
|
||||||
|
}
|
||||||
|
val track = tracks[manga.id]?.singleOrNull()
|
||||||
MangaTracking(
|
MangaTracking(
|
||||||
manga = m.toManga(),
|
manga = manga,
|
||||||
knownChaptersCount = track?.totalChapters ?: -1,
|
knownChaptersCount = track?.totalChapters ?: -1,
|
||||||
lastChapterId = track?.lastChapterId ?: 0L,
|
lastChapterId = track?.lastChapterId ?: 0L,
|
||||||
lastNotifiedChapterId = track?.lastNotifiedChapterId ?: 0L,
|
lastNotifiedChapterId = track?.lastNotifiedChapterId ?: 0L,
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ class ChapterHolder(parent: ViewGroup) :
|
|||||||
}
|
}
|
||||||
ChapterExtra.CURRENT -> {
|
ChapterExtra.CURRENT -> {
|
||||||
textView_number.setBackgroundResource(R.drawable.bg_badge_outline_accent)
|
textView_number.setBackgroundResource(R.drawable.bg_badge_outline_accent)
|
||||||
textView_number.setTextColor(context.getThemeColor(R.attr.colorAccent))
|
textView_number.setTextColor(context.getThemeColor(androidx.appcompat.R.attr.colorAccent))
|
||||||
}
|
}
|
||||||
ChapterExtra.NEW -> {
|
ChapterExtra.NEW -> {
|
||||||
textView_number.setBackgroundResource(R.drawable.bg_badge_accent)
|
textView_number.setBackgroundResource(R.drawable.bg_badge_accent)
|
||||||
|
|||||||
@@ -206,7 +206,7 @@ class MangaDetailsActivity : BaseActivity(), MangaDetailsView,
|
|||||||
override fun onSupportActionModeFinished(mode: ActionMode) {
|
override fun onSupportActionModeFinished(mode: ActionMode) {
|
||||||
super.onSupportActionModeFinished(mode)
|
super.onSupportActionModeFinished(mode)
|
||||||
pager.isUserInputEnabled = true
|
pager.isUserInputEnabled = true
|
||||||
window?.statusBarColor = getThemeColor(R.attr.colorPrimaryDark)
|
window?.statusBarColor = getThemeColor(androidx.appcompat.R.attr.colorPrimaryDark)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@@ -77,9 +77,9 @@ class DownloadService : BaseService() {
|
|||||||
return launch(Dispatchers.IO) {
|
return launch(Dispatchers.IO) {
|
||||||
mutex.lock()
|
mutex.lock()
|
||||||
wakeLock.acquire(TimeUnit.HOURS.toMillis(1))
|
wakeLock.acquire(TimeUnit.HOURS.toMillis(1))
|
||||||
|
notification.fillFrom(manga)
|
||||||
|
notification.setCancelId(startId)
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
notification.fillFrom(manga)
|
|
||||||
notification.setCancelId(startId)
|
|
||||||
startForeground(DownloadNotification.NOTIFICATION_ID, notification())
|
startForeground(DownloadNotification.NOTIFICATION_ID, notification())
|
||||||
}
|
}
|
||||||
val destination = settings.getStorageDir(this@DownloadService)
|
val destination = settings.getStorageDir(this@DownloadService)
|
||||||
@@ -94,10 +94,8 @@ class DownloadService : BaseService() {
|
|||||||
.build()
|
.build()
|
||||||
).drawable
|
).drawable
|
||||||
}
|
}
|
||||||
withContext(Dispatchers.Main) {
|
notification.setLargeIcon(cover)
|
||||||
notification.setLargeIcon(cover)
|
notification.update()
|
||||||
notification.update()
|
|
||||||
}
|
|
||||||
val data = if (manga.chapters == null) repo.getDetails(manga) else manga
|
val data = if (manga.chapters == null) repo.getDetails(manga) else manga
|
||||||
output = MangaZip.findInDir(destination, data)
|
output = MangaZip.findInDir(destination, data)
|
||||||
output.prepare(data)
|
output.prepare(data)
|
||||||
@@ -141,31 +139,25 @@ class DownloadService : BaseService() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
withContext(Dispatchers.Main) {
|
notification.setCancelId(0)
|
||||||
notification.setCancelId(0)
|
notification.setPostProcessing()
|
||||||
notification.setPostProcessing()
|
notification.update()
|
||||||
notification.update()
|
|
||||||
}
|
|
||||||
output.compress()
|
output.compress()
|
||||||
val result = MangaProviderFactory.createLocal().getFromFile(output.file)
|
val result = MangaProviderFactory.createLocal().getFromFile(output.file)
|
||||||
withContext(Dispatchers.Main) {
|
notification.setDone(result)
|
||||||
notification.setDone(result)
|
notification.dismiss()
|
||||||
notification.dismiss()
|
notification.update(manga.id.toInt().absoluteValue)
|
||||||
notification.update(manga.id.toInt().absoluteValue)
|
|
||||||
}
|
|
||||||
} catch (_: CancellationException) {
|
} catch (_: CancellationException) {
|
||||||
withContext(Dispatchers.Main + NonCancellable) {
|
withContext(NonCancellable) {
|
||||||
notification.setCancelling()
|
notification.setCancelling()
|
||||||
notification.setCancelId(0)
|
notification.setCancelId(0)
|
||||||
notification.update()
|
notification.update()
|
||||||
}
|
}
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
withContext(Dispatchers.Main) {
|
notification.setError(e)
|
||||||
notification.setError(e)
|
notification.setCancelId(0)
|
||||||
notification.setCancelId(0)
|
notification.dismiss()
|
||||||
notification.dismiss()
|
notification.update(manga.id.toInt().absoluteValue)
|
||||||
notification.update(manga.id.toInt().absoluteValue)
|
|
||||||
}
|
|
||||||
} finally {
|
} finally {
|
||||||
withContext(NonCancellable) {
|
withContext(NonCancellable) {
|
||||||
jobs.remove(startId)
|
jobs.remove(startId)
|
||||||
|
|||||||
@@ -17,17 +17,13 @@ class SourceSettingsFragment : PreferenceFragmentCompat(), KoinComponent {
|
|||||||
requireArguments().getParcelable<MangaSource>(EXTRA_SOURCE)!!
|
requireArguments().getParcelable<MangaSource>(EXTRA_SOURCE)!!
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
preferenceManager.sharedPreferencesName = source.name
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
activity?.title = source.title
|
activity?.title = source.title
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
|
preferenceManager.sharedPreferencesName = source.name
|
||||||
val repo = MangaProviderFactory.create(source) as? RemoteMangaRepository ?: return
|
val repo = MangaProviderFactory.create(source) as? RemoteMangaRepository ?: return
|
||||||
val keys = repo.onCreatePreferences().map(::getString)
|
val keys = repo.onCreatePreferences().map(::getString)
|
||||||
addPreferencesFromResource(R.xml.pref_source)
|
addPreferencesFromResource(R.xml.pref_source)
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 403 B |
Binary file not shown.
|
Before Width: | Height: | Size: 358 B |
Binary file not shown.
|
Before Width: | Height: | Size: 591 B |
Binary file not shown.
|
Before Width: | Height: | Size: 824 B |
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 KiB |
@@ -1,12 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<vector
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:tint="?attr/colorControlNormal"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24">
|
|
||||||
<path
|
|
||||||
android:fillColor="#000"
|
|
||||||
android:pathData="M13,5V11H14.17L12,13.17L9.83,11H11V5H13M15,3H9V9H5L12,16L19,9H15V3M19,18H5V20H19V18Z" />
|
|
||||||
</vector>
|
|
||||||
@@ -6,7 +6,7 @@ buildscript {
|
|||||||
jcenter()
|
jcenter()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:4.2.0-alpha03'
|
classpath 'com.android.tools.build:gradle:4.2.0-alpha04'
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
|
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
|
|||||||
@@ -18,4 +18,5 @@ android.useAndroidX=true
|
|||||||
# Automatically convert third-party libraries to use AndroidX
|
# Automatically convert third-party libraries to use AndroidX
|
||||||
android.enableJetifier=true
|
android.enableJetifier=true
|
||||||
# Kotlin code style for this project: "official" or "obsolete":
|
# Kotlin code style for this project: "official" or "obsolete":
|
||||||
kotlin.code.style=official
|
kotlin.code.style=official
|
||||||
|
android.nonTransitiveRClass=true
|
||||||
Reference in New Issue
Block a user