DI refactoring

pull/26/head
Koitharu 6 years ago
parent fb60b26f08
commit 56e145420c

@ -95,7 +95,7 @@ dependencies {
implementation 'org.jsoup:jsoup:1.13.1'
implementation 'org.koin:koin-android:2.2.0-rc-2'
implementation 'io.coil-kt:coil:1.0.0-rc3'
implementation 'io.coil-kt:coil-base:1.0.0-rc3'
implementation 'com.davemorrissey.labs:subsampling-scale-image-view:3.10.0'
implementation 'com.tomclaw.cache:cache:1.0'

@ -3,37 +3,25 @@ package org.koitharu.kotatsu
import android.app.Application
import android.os.StrictMode
import androidx.appcompat.app.AppCompatDelegate
import androidx.room.Room
import coil.Coil
import coil.ComponentRegistry
import coil.ImageLoader
import coil.util.CoilUtils
import okhttp3.CookieJar
import okhttp3.OkHttpClient
import org.koin.android.ext.android.get
import org.koin.android.ext.koin.androidContext
import org.koin.android.ext.koin.androidLogger
import org.koin.core.context.startKoin
import org.koin.core.logger.Level
import org.koin.dsl.module
import org.koitharu.kotatsu.core.db.DatabasePrePopulateCallback
import org.koitharu.kotatsu.core.db.MangaDatabase
import org.koitharu.kotatsu.core.db.migrations.*
import org.koitharu.kotatsu.core.local.CbzFetcher
import org.koitharu.kotatsu.core.db.databaseModule
import org.koitharu.kotatsu.core.github.githubModule
import org.koitharu.kotatsu.core.local.PagesCache
import org.koitharu.kotatsu.core.local.cookies.PersistentCookieJar
import org.koitharu.kotatsu.core.local.cookies.cache.SetCookieCache
import org.koitharu.kotatsu.core.local.cookies.persistence.SharedPrefsCookiePersistor
import org.koitharu.kotatsu.core.network.networkModule
import org.koitharu.kotatsu.core.parser.LocalMangaRepository
import org.koitharu.kotatsu.core.parser.UserAgentInterceptor
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.domain.MangaDataRepository
import org.koitharu.kotatsu.domain.MangaLoaderContext
import org.koitharu.kotatsu.domain.MangaSearchRepository
import org.koitharu.kotatsu.domain.favourites.FavouritesRepository
import org.koitharu.kotatsu.domain.history.HistoryRepository
import org.koitharu.kotatsu.domain.tracking.TrackingRepository
import org.koitharu.kotatsu.ui.base.uiModule
import org.koitharu.kotatsu.ui.utils.AppCrashHandler
import org.koitharu.kotatsu.ui.widget.WidgetUpdater
import org.koitharu.kotatsu.utils.CacheUtils
import java.util.concurrent.TimeUnit
class KotatsuApp : Application() {
@ -57,9 +45,8 @@ class KotatsuApp : Application() {
)
}
initKoin()
initCoil(get())
Thread.setDefaultUncaughtExceptionHandler(AppCrashHandler(applicationContext))
AppCompatDelegate.setDefaultNightMode(AppSettings(this).theme)
AppCompatDelegate.setDefaultNightMode(get<AppSettings>().theme)
val widgetUpdater = WidgetUpdater(applicationContext)
FavouritesRepository.subscribe(widgetUpdater)
HistoryRepository.subscribe(widgetUpdater)
@ -67,66 +54,23 @@ class KotatsuApp : Application() {
private fun initKoin() {
startKoin {
androidLogger(Level.ERROR)
androidContext(applicationContext)
androidContext(this@KotatsuApp)
modules(
networkModule,
databaseModule,
githubModule,
uiModule,
module {
single<CookieJar> {
PersistentCookieJar(
SetCookieCache(),
SharedPrefsCookiePersistor(applicationContext)
)
}
factory {
okHttp(get())
.cache(CacheUtils.createHttpCache(applicationContext))
.build()
}
single {
mangaDb().build()
}
single {
MangaLoaderContext()
}
single {
AppSettings(applicationContext)
}
single {
PagesCache(applicationContext)
}
single { FavouritesRepository(get()) }
single { HistoryRepository(get()) }
single { TrackingRepository(get()) }
single { MangaDataRepository(get()) }
single { MangaSearchRepository() }
single { MangaLoaderContext() }
single { AppSettings(get()) }
single { PagesCache(get()) }
}
)
}
}
private fun initCoil(cookieJar: CookieJar) {
Coil.setImageLoader(
ImageLoader.Builder(applicationContext)
.okHttpClient(
okHttp(cookieJar)
.cache(CoilUtils.createDefaultCache(applicationContext))
.build()
).componentRegistry(
ComponentRegistry.Builder()
.add(CbzFetcher())
.build()
)
.build()
)
}
private fun okHttp(cookieJar: CookieJar) = OkHttpClient.Builder().apply {
connectTimeout(20, TimeUnit.SECONDS)
readTimeout(60, TimeUnit.SECONDS)
writeTimeout(20, TimeUnit.SECONDS)
cookieJar(cookieJar)
addInterceptor(UserAgentInterceptor)
}
private fun mangaDb() = Room.databaseBuilder(
applicationContext,
MangaDatabase::class.java,
"kotatsu-db"
).addMigrations(Migration1To2, Migration2To3, Migration3To4, Migration4To5, Migration5To6)
.addCallback(DatabasePrePopulateCallback(resources))
}

@ -0,0 +1,25 @@
package org.koitharu.kotatsu.core.db
import androidx.room.Room
import org.koin.android.ext.koin.androidContext
import org.koin.dsl.module
import org.koitharu.kotatsu.core.db.migrations.*
val databaseModule
get() = module {
single {
Room.databaseBuilder(
androidContext(),
MangaDatabase::class.java,
"kotatsu-db"
).addMigrations(
Migration1To2(),
Migration2To3(),
Migration3To4(),
Migration4To5(),
Migration5To6()
).addCallback(
DatabasePrePopulateCallback(androidContext().resources)
).build()
}
}

@ -1,47 +0,0 @@
package org.koitharu.kotatsu.core.db
import androidx.room.*
import org.koitharu.kotatsu.core.db.entity.HistoryEntity
import org.koitharu.kotatsu.core.db.entity.HistoryWithManga
import org.koitharu.kotatsu.core.db.entity.MangaEntity
@Dao
abstract class HistoryDao {
/**
* @hide
*/
@Transaction
@Query("SELECT * FROM history ORDER BY updated_at DESC LIMIT :limit OFFSET :offset")
abstract suspend fun findAll(offset: Int, limit: Int): List<HistoryWithManga>
@Query("SELECT * FROM manga WHERE manga_id IN (SELECT manga_id FROM history)")
abstract suspend fun findAllManga(): List<MangaEntity>
@Query("SELECT * FROM history WHERE manga_id = :id")
abstract suspend fun find(id: Long): HistoryEntity?
@Query("DELETE FROM history")
abstract suspend fun clear()
@Insert(onConflict = OnConflictStrategy.IGNORE)
abstract suspend fun insert(entity: HistoryEntity): Long
@Query("UPDATE history SET page = :page, chapter_id = :chapterId, scroll = :scroll, updated_at = :updatedAt WHERE manga_id = :mangaId")
abstract suspend fun update(mangaId: Long, page: Int, chapterId: Long, scroll: Float, updatedAt: Long): Int
@Query("DELETE FROM history WHERE manga_id = :mangaId")
abstract suspend fun delete(mangaId: Long)
suspend fun update(entity: HistoryEntity) = update(entity.mangaId, entity.page, entity.chapterId, entity.scroll, entity.updatedAt)
@Transaction
open suspend fun upsert(entity: HistoryEntity): Boolean {
return if (update(entity) == 0) {
insert(entity)
true
} else false
}
}

@ -2,6 +2,7 @@ package org.koitharu.kotatsu.core.db
import androidx.room.Database
import androidx.room.RoomDatabase
import org.koitharu.kotatsu.core.db.dao.*
import org.koitharu.kotatsu.core.db.entity.*
@Database(

@ -1,4 +1,4 @@
package org.koitharu.kotatsu.core.db
package org.koitharu.kotatsu.core.db.dao
import androidx.room.Dao
import androidx.room.Insert

@ -1,4 +1,4 @@
package org.koitharu.kotatsu.core.db
package org.koitharu.kotatsu.core.db.dao
import androidx.room.*
import org.koitharu.kotatsu.core.db.entity.FavouriteEntity

@ -0,0 +1,54 @@
package org.koitharu.kotatsu.core.db.dao
import androidx.room.*
import org.koitharu.kotatsu.core.db.entity.HistoryEntity
import org.koitharu.kotatsu.core.db.entity.HistoryWithManga
import org.koitharu.kotatsu.core.db.entity.MangaEntity
@Dao
abstract class HistoryDao {
/**
* @hide
*/
@Transaction
@Query("SELECT * FROM history ORDER BY updated_at DESC LIMIT :limit OFFSET :offset")
abstract suspend fun findAll(offset: Int, limit: Int): List<HistoryWithManga>
@Query("SELECT * FROM manga WHERE manga_id IN (SELECT manga_id FROM history)")
abstract suspend fun findAllManga(): List<MangaEntity>
@Query("SELECT * FROM history WHERE manga_id = :id")
abstract suspend fun find(id: Long): HistoryEntity?
@Query("DELETE FROM history")
abstract suspend fun clear()
@Insert(onConflict = OnConflictStrategy.IGNORE)
abstract suspend fun insert(entity: HistoryEntity): Long
@Query("UPDATE history SET page = :page, chapter_id = :chapterId, scroll = :scroll, updated_at = :updatedAt WHERE manga_id = :mangaId")
abstract suspend fun update(
mangaId: Long,
page: Int,
chapterId: Long,
scroll: Float,
updatedAt: Long
): Int
@Query("DELETE FROM history WHERE manga_id = :mangaId")
abstract suspend fun delete(mangaId: Long)
suspend fun update(entity: HistoryEntity) =
update(entity.mangaId, entity.page, entity.chapterId, entity.scroll, entity.updatedAt)
@Transaction
open suspend fun upsert(entity: HistoryEntity): Boolean {
return if (update(entity) == 0) {
insert(entity)
true
} else false
}
}

@ -1,4 +1,4 @@
package org.koitharu.kotatsu.core.db
package org.koitharu.kotatsu.core.db.dao
import androidx.room.*
import org.koitharu.kotatsu.core.db.entity.MangaEntity

@ -1,4 +1,4 @@
package org.koitharu.kotatsu.core.db
package org.koitharu.kotatsu.core.db.dao
import androidx.room.*
import org.koitharu.kotatsu.core.db.entity.MangaPrefsEntity

@ -1,4 +1,4 @@
package org.koitharu.kotatsu.core.db
package org.koitharu.kotatsu.core.db.dao
import androidx.room.*
import org.koitharu.kotatsu.core.db.entity.TagEntity

@ -1,4 +1,4 @@
package org.koitharu.kotatsu.core.db
package org.koitharu.kotatsu.core.db.dao
import androidx.room.*
import org.koitharu.kotatsu.core.db.entity.TrackLogEntity

@ -1,4 +1,4 @@
package org.koitharu.kotatsu.core.db
package org.koitharu.kotatsu.core.db.dao
import androidx.room.*
import org.koitharu.kotatsu.core.db.entity.TrackEntity

@ -3,7 +3,7 @@ package org.koitharu.kotatsu.core.db.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
object Migration1To2 : Migration(1, 2) {
class Migration1To2 : Migration(1, 2) {
/**
* Adding foreign keys
*/

@ -3,7 +3,7 @@ package org.koitharu.kotatsu.core.db.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
object Migration2To3 : Migration(2, 3) {
class Migration2To3 : Migration(2, 3) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE history ADD COLUMN scroll REAL NOT NULL DEFAULT 0")

@ -3,7 +3,7 @@ package org.koitharu.kotatsu.core.db.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
object Migration3To4 : Migration(3, 4) {
class Migration3To4 : Migration(3, 4) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("CREATE TABLE IF NOT EXISTS tracks (manga_id INTEGER NOT NULL, chapters_total INTEGER NOT NULL, last_chapter_id INTEGER NOT NULL, chapters_new INTEGER NOT NULL, last_check INTEGER NOT NULL, last_notified_id INTEGER NOT NULL, PRIMARY KEY(manga_id), FOREIGN KEY(manga_id) REFERENCES manga(manga_id) ON UPDATE NO ACTION ON DELETE CASCADE )")

@ -3,7 +3,7 @@ package org.koitharu.kotatsu.core.db.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
object Migration4To5 : Migration(4, 5) {
class Migration4To5 : Migration(4, 5) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE favourite_categories ADD COLUMN sort_key INTEGER NOT NULL DEFAULT 0")

@ -3,7 +3,7 @@ package org.koitharu.kotatsu.core.db.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
object Migration5To6 : Migration(5, 6) {
class Migration5To6 : Migration(5, 6) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("CREATE TABLE IF NOT EXISTS track_logs (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, manga_id INTEGER NOT NULL, chapters TEXT NOT NULL, created_at INTEGER NOT NULL, FOREIGN KEY(manga_id) REFERENCES manga(manga_id) ON UPDATE NO ACTION ON DELETE CASCADE)")

@ -0,0 +1,10 @@
package org.koitharu.kotatsu.core.github
import org.koin.dsl.module
val githubModule
get() = module {
single {
GithubRepository(get())
}
}

@ -2,14 +2,10 @@ package org.koitharu.kotatsu.core.github
import okhttp3.OkHttpClient
import okhttp3.Request
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
import org.koitharu.kotatsu.utils.ext.await
import org.koitharu.kotatsu.utils.ext.parseJson
class GithubRepository : KoinComponent {
private val okHttp by inject<OkHttpClient>()
class GithubRepository(private val okHttp: OkHttpClient) {
suspend fun getLatestVersion(): AppVersion {
val request = Request.Builder()

@ -2,9 +2,12 @@ package org.koitharu.kotatsu.core.local
import java.io.File
import java.io.FilenameFilter
import java.util.*
class CbzFilter : FilenameFilter {
override fun accept(dir: File, name: String) =
name.endsWith(".cbz", ignoreCase = true) || name.endsWith(".zip", ignoreCase = true)
override fun accept(dir: File, name: String): Boolean {
val ext = name.substringAfterLast('.', "").toLowerCase(Locale.ROOT)
return ext == "cbz" || ext == "zip"
}
}

@ -0,0 +1,32 @@
package org.koitharu.kotatsu.core.network
import okhttp3.CookieJar
import okhttp3.OkHttpClient
import org.koin.android.ext.koin.androidContext
import org.koin.dsl.module
import org.koitharu.kotatsu.core.network.cookies.PersistentCookieJar
import org.koitharu.kotatsu.core.network.cookies.cache.SetCookieCache
import org.koitharu.kotatsu.core.network.cookies.persistence.SharedPrefsCookiePersistor
import org.koitharu.kotatsu.core.parser.UserAgentInterceptor
import org.koitharu.kotatsu.utils.CacheUtils
import java.util.concurrent.TimeUnit
val networkModule
get() = module {
single<CookieJar> {
PersistentCookieJar(
SetCookieCache(),
SharedPrefsCookiePersistor(androidContext())
)
}
single {
OkHttpClient.Builder().apply {
connectTimeout(20, TimeUnit.SECONDS)
readTimeout(60, TimeUnit.SECONDS)
writeTimeout(20, TimeUnit.SECONDS)
cookieJar(get())
cache(CacheUtils.createHttpCache(androidContext()))
addInterceptor(UserAgentInterceptor())
}.build()
}
}

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.koitharu.kotatsu.core.local.cookies
package org.koitharu.kotatsu.core.network.cookies
import okhttp3.CookieJar

@ -13,12 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.koitharu.kotatsu.core.local.cookies
package org.koitharu.kotatsu.core.network.cookies
import okhttp3.Cookie
import okhttp3.HttpUrl
import org.koitharu.kotatsu.core.local.cookies.cache.CookieCache
import org.koitharu.kotatsu.core.local.cookies.persistence.CookiePersistor
import org.koitharu.kotatsu.core.network.cookies.cache.CookieCache
import org.koitharu.kotatsu.core.network.cookies.persistence.CookiePersistor
import java.util.*
class PersistentCookieJar(

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.koitharu.kotatsu.core.local.cookies.cache
package org.koitharu.kotatsu.core.network.cookies.cache
import okhttp3.Cookie

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.koitharu.kotatsu.core.local.cookies.cache
package org.koitharu.kotatsu.core.network.cookies.cache
import okhttp3.Cookie
import java.util.*

@ -13,16 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.koitharu.kotatsu.core.local.cookies.cache
package org.koitharu.kotatsu.core.network.cookies.cache
import okhttp3.Cookie
import org.koitharu.kotatsu.core.local.cookies.cache.IdentifiableCookie.Companion.decorateAll
import org.koitharu.kotatsu.core.network.cookies.cache.IdentifiableCookie.Companion.decorateAll
import java.util.*
import java.util.concurrent.ConcurrentHashMap
class SetCookieCache : CookieCache {
private val cookies: MutableSet<IdentifiableCookie> = Collections.newSetFromMap(ConcurrentHashMap())
private val cookies: MutableSet<IdentifiableCookie> =
Collections.newSetFromMap(ConcurrentHashMap())
override fun addAll(newCookies: Collection<Cookie>) {
for (cookie in decorateAll(newCookies)) {

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.koitharu.kotatsu.core.local.cookies.persistence
package org.koitharu.kotatsu.core.network.cookies.persistence
import okhttp3.Cookie
@ -23,6 +23,7 @@ import okhttp3.Cookie
interface CookiePersistor {
fun loadAll(): List<Cookie>
/**
* Persist all cookies, existing cookies will be overwritten.
*

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.koitharu.kotatsu.core.local.cookies.persistence
package org.koitharu.kotatsu.core.network.cookies.persistence
import android.util.Log
import okhttp3.Cookie
@ -49,7 +49,8 @@ class SerializableCookie : Serializable {
fun decode(encodedCookie: String): Cookie? {
val bytes = hexStringToByteArray(encodedCookie)
val byteArrayInputStream = ByteArrayInputStream(
bytes)
bytes
)
var cookie: Cookie? = null
var objectInputStream: ObjectInputStream? = null
try {
@ -107,6 +108,7 @@ class SerializableCookie : Serializable {
const val serialVersionUID = -8594045714036645534L
private const val NON_VALID_EXPIRES_AT = -1L
/**
* Using some super basic byte array &lt;-&gt; hex conversions so we don't
* have to rely on any large Base64 libraries. Can be overridden if you
@ -141,7 +143,7 @@ class SerializableCookie : Serializable {
var i = 0
while (i < len) {
data[i / 2] = ((Character.digit(hexString[i], 16) shl 4) + Character
.digit(hexString[i + 1], 16)).toByte()
.digit(hexString[i + 1], 16)).toByte()
i += 2
}
return data

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.koitharu.kotatsu.core.local.cookies.persistence
package org.koitharu.kotatsu.core.network.cookies.persistence
import android.annotation.SuppressLint
import android.content.Context
@ -25,7 +25,12 @@ import java.util.*
class SharedPrefsCookiePersistor(private val sharedPreferences: SharedPreferences) :
CookiePersistor {
constructor(context: Context) : this(context.getSharedPreferences("cookies", Context.MODE_PRIVATE))
constructor(context: Context) : this(
context.getSharedPreferences(
"cookies",
Context.MODE_PRIVATE
)
)
override fun loadAll(): List<Cookie> {
val cookies: MutableList<Cookie> = ArrayList(sharedPreferences.all.size)

@ -0,0 +1,9 @@
package org.koitharu.kotatsu.core.parser
import org.koin.dsl.bind
import org.koin.dsl.module
val parserModule
get() = module {
single { LocalMangaRepository() } bind MangaRepository::class
}

@ -1,13 +1,11 @@
package org.koitharu.kotatsu.core.parser
import android.annotation.SuppressLint
import android.os.Build
import okhttp3.Interceptor
import org.koitharu.kotatsu.BuildConfig
import java.util.*
@SuppressLint("ConstantLocale")
object UserAgentInterceptor : Interceptor {
class UserAgentInterceptor : Interceptor {
private val userAgent = "Kotatsu/%s (Android %s; %s; %s %s; %s)".format(
BuildConfig.VERSION_NAME,

@ -1,8 +1,6 @@
package org.koitharu.kotatsu.domain
import androidx.room.withTransaction
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
import org.koitharu.kotatsu.core.db.MangaDatabase
import org.koitharu.kotatsu.core.db.entity.MangaEntity
import org.koitharu.kotatsu.core.db.entity.MangaPrefsEntity
@ -10,9 +8,7 @@ import org.koitharu.kotatsu.core.db.entity.TagEntity
import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.core.prefs.ReaderMode
class MangaDataRepository : KoinComponent {
private val db: MangaDatabase by inject()
class MangaDataRepository(private val db: MangaDatabase) {
suspend fun savePreferences(manga: Manga, mode: ReaderMode) {
val tags = manga.tags.map(TagEntity.Companion::fromMangaTag)

@ -34,6 +34,7 @@ object MangaProviderFactory : KoinComponent {
}
}
@Deprecated("Use DI")
fun createLocal(): LocalMangaRepository {
var instance = cache[MangaSource.LOCAL]?.get()
if (instance == null) {

@ -2,13 +2,12 @@ package org.koitharu.kotatsu.domain
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import org.koin.core.component.KoinComponent
import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.core.model.SortOrder
import java.util.*
class MangaSearchRepository : KoinComponent {
class MangaSearchRepository {
fun globalSearch(query: String, batchSize: Int = 4): Flow<List<Manga>> = flow {
val sources = MangaProviderFactory.getSources(false)

@ -4,8 +4,6 @@ import androidx.collection.ArraySet
import androidx.room.withTransaction
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
import org.koitharu.kotatsu.core.db.MangaDatabase
import org.koitharu.kotatsu.core.db.entity.FavouriteCategoryEntity
import org.koitharu.kotatsu.core.db.entity.FavouriteEntity
@ -14,9 +12,7 @@ import org.koitharu.kotatsu.core.db.entity.TagEntity
import org.koitharu.kotatsu.core.model.FavouriteCategory
import org.koitharu.kotatsu.core.model.Manga
class FavouritesRepository : KoinComponent {
private val db: MangaDatabase by inject()
class FavouritesRepository(private val db: MangaDatabase) {
suspend fun getAllManga(): List<Manga> {
val entities = db.favouritesDao.findAll()

@ -1,8 +1,8 @@
package org.koitharu.kotatsu.domain.favourites
interface OnFavouritesChangeListener {
fun interface OnFavouritesChangeListener {
fun onFavouritesChanged(mangaId: Long)
fun onCategoriesChanged()
fun onCategoriesChanged() = Unit
}

@ -14,10 +14,9 @@ import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.core.model.MangaHistory
import org.koitharu.kotatsu.domain.tracking.TrackingRepository
class HistoryRepository : KoinComponent {
class HistoryRepository(private val db: MangaDatabase) : KoinComponent {
private val db: MangaDatabase by inject()
private val trackingRepository by lazy(::TrackingRepository)
private val trackingRepository by inject<TrackingRepository>()
suspend fun getList(offset: Int, limit: Int = 20): List<Manga> {
val entities = db.historyDao.findAll(offset, limit)

@ -1,8 +1,6 @@
package org.koitharu.kotatsu.domain.tracking
import androidx.room.withTransaction
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
import org.koitharu.kotatsu.core.db.MangaDatabase
import org.koitharu.kotatsu.core.db.entity.TrackEntity
import org.koitharu.kotatsu.core.db.entity.TrackLogEntity
@ -10,9 +8,7 @@ import org.koitharu.kotatsu.core.model.*
import org.koitharu.kotatsu.domain.MangaProviderFactory
import java.util.*
class TrackingRepository : KoinComponent {
private val db: MangaDatabase by inject()
class TrackingRepository(private val db: MangaDatabase) {
suspend fun getNewChaptersCount(mangaId: Long): Int {
val entity = db.tracksDao.find(mangaId) ?: return 0

@ -1,14 +1,17 @@
package org.koitharu.kotatsu.ui.common
package org.koitharu.kotatsu.ui.base
import android.app.Dialog
import android.os.Bundle
import android.view.View
import androidx.annotation.CallSuper
import androidx.annotation.LayoutRes
import androidx.appcompat.app.AlertDialog
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import moxy.MvpAppCompatDialogFragment
abstract class AlertDialogFragment(@LayoutRes private val layoutResId: Int) : MvpAppCompatDialogFragment() {
abstract class AlertDialogFragment(
@LayoutRes private val layoutResId: Int
) : MvpAppCompatDialogFragment() {
private var rootView: View? = null
@ -24,12 +27,13 @@ abstract class AlertDialogFragment(@LayoutRes private val layoutResId: Int) : Mv
.create()
}
@CallSuper
override fun onDestroyView() {
rootView = null
super.onDestroyView()
}
override fun getView(): View? {
final override fun getView(): View? {
return rootView
}

@ -1,4 +1,4 @@
package org.koitharu.kotatsu.ui.common
package org.koitharu.kotatsu.ui.base
import android.view.MenuItem
import android.view.View

@ -1,4 +1,4 @@
package org.koitharu.kotatsu.ui.common
package org.koitharu.kotatsu.ui.base
import android.app.Dialog
import android.os.Bundle
@ -13,7 +13,7 @@ import org.koitharu.kotatsu.utils.UiUtils
abstract class BaseBottomSheet(@LayoutRes private val layoutResId: Int) :
MvpBottomSheetDialogFragment() {
override fun onCreateView(
final override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?

@ -1,14 +1,19 @@
package org.koitharu.kotatsu.ui.common
package org.koitharu.kotatsu.ui.base
import android.content.Context
import android.os.Parcelable
import androidx.annotation.LayoutRes
import coil.ImageLoader
import moxy.MvpAppCompatFragment
import org.koin.android.ext.android.inject
import org.koitharu.kotatsu.utils.delegates.ParcelableArgumentDelegate
import org.koitharu.kotatsu.utils.delegates.StringArgumentDelegate
abstract class BaseFragment(@LayoutRes contentLayoutId: Int) :
MvpAppCompatFragment(contentLayoutId) {
abstract class BaseFragment(
@LayoutRes contentLayoutId: Int
) : MvpAppCompatFragment(contentLayoutId) {
protected val coil by inject<ImageLoader>()
fun stringArg(name: String) = StringArgumentDelegate(name)

@ -1,4 +1,4 @@
package org.koitharu.kotatsu.ui.common
package org.koitharu.kotatsu.ui.base
import android.graphics.Color
import android.os.Build
@ -12,11 +12,11 @@ abstract class BaseFullscreenActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
with(window) {
// addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
statusBarColor = Color.TRANSPARENT
navigationBarColor = Color.TRANSPARENT
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
attributes.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
attributes.layoutInDisplayCutoutMode =
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
}
}
showSystemUI()

@ -1,4 +1,4 @@
package org.koitharu.kotatsu.ui.common
package org.koitharu.kotatsu.ui.base
import moxy.MvpView
import moxy.viewstate.strategy.alias.AddToEndSingle

@ -1,4 +1,4 @@
package org.koitharu.kotatsu.ui.common
package org.koitharu.kotatsu.ui.base
import androidx.annotation.StringRes
import androidx.preference.Preference

@ -1,4 +1,4 @@
package org.koitharu.kotatsu.ui.common
package org.koitharu.kotatsu.ui.base
import kotlinx.coroutines.*
import moxy.MvpPresenter

@ -1,4 +1,4 @@
package org.koitharu.kotatsu.ui.common
package org.koitharu.kotatsu.ui.base
import android.app.Service
import android.content.Intent
@ -7,18 +7,16 @@ import androidx.annotation.CallSuper
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.cancel
abstract class BaseService : Service(), CoroutineScope {
abstract class BaseService : Service() {
private val job = SupervisorJob()
final override val coroutineContext: CoroutineContext
get() = Dispatchers.Main.immediate + job
@Suppress("MemberVisibilityCanBePrivate")
val serviceScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
@CallSuper
override fun onDestroy() {
job.cancel()
serviceScope.cancel()
super.onDestroy()
}

@ -1,4 +1,4 @@
package org.koitharu.kotatsu.ui.common
package org.koitharu.kotatsu.ui.base
import android.content.Context
import android.view.View
@ -8,8 +8,10 @@ import org.koitharu.kotatsu.utils.ext.getThemeColor
class ChipsFactory(val context: Context) {
fun create(convertView: Chip? = null, text: CharSequence, @DrawableRes iconRes: Int = 0,
tag: Any? = null, onClickListener: View.OnClickListener? = null): Chip {
fun create(
convertView: Chip? = null, text: CharSequence, @DrawableRes iconRes: Int = 0,
tag: Any? = null, onClickListener: View.OnClickListener? = null
): Chip {
val chip = convertView ?: Chip(context).apply {
setTextColor(context.getThemeColor(android.R.attr.textColorPrimary))
isCloseIconVisible = false

@ -1,4 +1,4 @@
package org.koitharu.kotatsu.ui.common
package org.koitharu.kotatsu.ui.base
import android.util.ArrayMap
import moxy.MvpPresenter

@ -1,4 +1,4 @@
package org.koitharu.kotatsu.ui.common.dialog
package org.koitharu.kotatsu.ui.base.dialog
import android.annotation.SuppressLint
import android.content.Context

@ -1,4 +1,4 @@
package org.koitharu.kotatsu.ui.common.dialog
package org.koitharu.kotatsu.ui.base.dialog
import android.content.Context
import android.content.DialogInterface

@ -1,4 +1,4 @@
package org.koitharu.kotatsu.ui.common.dialog
package org.koitharu.kotatsu.ui.base.dialog
import android.annotation.SuppressLint
import android.content.Context
@ -11,15 +11,17 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.android.synthetic.main.dialog_input.view.*
import org.koitharu.kotatsu.R
class TextInputDialog private constructor(private val delegate: AlertDialog) :
DialogInterface by delegate {
class TextInputDialog private constructor(
private val delegate: AlertDialog
) : DialogInterface by delegate {
fun show() = delegate.show()
class Builder(context: Context) {
@SuppressLint("InflateParams")
private val view = LayoutInflater.from(context).inflate(R.layout.dialog_input, null, false)
private val view = LayoutInflater.from(context)
.inflate(R.layout.dialog_input, null, false)
private val delegate = MaterialAlertDialogBuilder(context)
.setView(view)

@ -1,4 +1,4 @@
package org.koitharu.kotatsu.ui.common.list
package org.koitharu.kotatsu.ui.base.list
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView

@ -1,8 +1,7 @@
package org.koitharu.kotatsu.ui.common.list
package org.koitharu.kotatsu.ui.base.list
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import okhttp3.internal.toImmutableList
import org.koin.core.component.KoinComponent
import org.koitharu.kotatsu.utils.ext.replaceWith
@ -12,7 +11,7 @@ abstract class BaseRecyclerAdapter<T, E>(private val onItemClickListener: OnRecy
protected val dataSet = ArrayList<T>() //TODO make private
val items get() = dataSet.toImmutableList()
val items get() = dataSet as List<T>
val hasItems get() = dataSet.isNotEmpty()

@ -1,4 +1,4 @@
package org.koitharu.kotatsu.ui.common.list
package org.koitharu.kotatsu.ui.base.list
import android.view.View
import android.view.ViewGroup

@ -1,4 +1,4 @@
package org.koitharu.kotatsu.ui.common.list
package org.koitharu.kotatsu.ui.base.list
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView

@ -1,4 +1,4 @@
package org.koitharu.kotatsu.ui.common.list
package org.koitharu.kotatsu.ui.base.list
import android.view.View

@ -1,4 +1,4 @@
package org.koitharu.kotatsu.ui.common.list
package org.koitharu.kotatsu.ui.base.list
import androidx.recyclerview.widget.RecyclerView

@ -1,4 +1,4 @@
package org.koitharu.kotatsu.ui.common.list
package org.koitharu.kotatsu.ui.base.list
import android.view.ViewGroup

@ -1,4 +1,4 @@
package org.koitharu.kotatsu.ui.common.list
package org.koitharu.kotatsu.ui.base.list
import android.view.View
import android.view.ViewGroup

@ -1,4 +1,4 @@
package org.koitharu.kotatsu.ui.common.list.decor
package org.koitharu.kotatsu.ui.base.list.decor
import android.content.Context
import android.graphics.Canvas

@ -1,4 +1,4 @@
package org.koitharu.kotatsu.ui.common.list.decor
package org.koitharu.kotatsu.ui.base.list.decor
import android.graphics.Canvas
import android.graphics.Rect

@ -1,4 +1,4 @@
package org.koitharu.kotatsu.ui.common.list.decor
package org.koitharu.kotatsu.ui.base.list.decor
import android.graphics.Rect
import android.view.View

@ -0,0 +1,21 @@
package org.koitharu.kotatsu.ui.base
import coil.ComponentRegistry
import coil.ImageLoader
import okhttp3.OkHttpClient
import org.koin.android.ext.koin.androidContext
import org.koin.dsl.module
import org.koitharu.kotatsu.core.local.CbzFetcher
val uiModule
get() = module {
single {
ImageLoader.Builder(androidContext())
.okHttpClient(get<OkHttpClient>())
.componentRegistry(
ComponentRegistry.Builder()
.add(CbzFetcher())
.build()
).build()
}
}

@ -1,4 +1,4 @@
package org.koitharu.kotatsu.ui.common.widgets
package org.koitharu.kotatsu.ui.base.widgets
import android.content.Context
import android.util.AttributeSet
@ -61,7 +61,6 @@ class CheckableImageView @JvmOverloads constructor(
private companion object {
@JvmStatic
private val CHECKED_STATE_SET = intArrayOf(android.R.attr.state_checked)
}
}

@ -1,10 +1,10 @@
package org.koitharu.kotatsu.ui.common.widgets
package org.koitharu.kotatsu.ui.base.widgets
import android.content.Context
import android.util.AttributeSet
import android.widget.LinearLayout
import androidx.appcompat.widget.AppCompatImageView
import androidx.core.content.res.use
import androidx.core.content.withStyledAttributes
import org.koitharu.kotatsu.R
@ -15,10 +15,9 @@ class CoverImageView @JvmOverloads constructor(
private var orientation: Int = HORIZONTAL
init {
context.theme.obtainStyledAttributes(attrs, R.styleable.CoverImageView, defStyleAttr, 0)
.use {
orientation = it.getInt(R.styleable.CoverImageView_android_orientation, HORIZONTAL)
}
context.withStyledAttributes(attrs, R.styleable.CoverImageView, defStyleAttr) {
orientation = getInt(R.styleable.CoverImageView_android_orientation, HORIZONTAL)
}
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {

@ -1,4 +1,4 @@
package org.koitharu.kotatsu.ui.common.widgets
package org.koitharu.kotatsu.ui.base.widgets
import android.content.Context
import android.util.AttributeSet

@ -11,7 +11,7 @@ import android.view.MenuItem
import androidx.core.view.isVisible
import kotlinx.android.synthetic.main.activity_browser.*
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.ui.common.BaseActivity
import org.koitharu.kotatsu.ui.base.BaseActivity
@SuppressLint("SetJavaScriptEnabled")
class BrowserActivity : BaseActivity(), BrowserCallback {

@ -7,7 +7,7 @@ import kotlinx.android.synthetic.main.item_chapter.*
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.model.MangaChapter
import org.koitharu.kotatsu.domain.history.ChapterExtra
import org.koitharu.kotatsu.ui.common.list.BaseViewHolder
import org.koitharu.kotatsu.ui.base.list.BaseViewHolder
import org.koitharu.kotatsu.utils.ext.getThemeColor
class ChapterHolder(parent: ViewGroup) :

@ -4,8 +4,8 @@ import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import org.koitharu.kotatsu.core.model.MangaChapter
import org.koitharu.kotatsu.domain.history.ChapterExtra
import org.koitharu.kotatsu.ui.common.list.BaseRecyclerAdapter
import org.koitharu.kotatsu.ui.common.list.OnRecyclerItemClickListener
import org.koitharu.kotatsu.ui.base.list.BaseRecyclerAdapter
import org.koitharu.kotatsu.ui.base.list.OnRecyclerItemClickListener
class ChaptersAdapter(onItemClickListener: OnRecyclerItemClickListener<MangaChapter>) :
BaseRecyclerAdapter<MangaChapter, ChapterExtra>(onItemClickListener) {

@ -15,8 +15,8 @@ import kotlinx.android.synthetic.main.fragment_chapters.*
import moxy.ktx.moxyPresenter
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.model.*
import org.koitharu.kotatsu.ui.common.BaseFragment
import org.koitharu.kotatsu.ui.common.list.OnRecyclerItemClickListener
import org.koitharu.kotatsu.ui.base.BaseFragment
import org.koitharu.kotatsu.ui.base.list.OnRecyclerItemClickListener
import org.koitharu.kotatsu.ui.download.DownloadService
import org.koitharu.kotatsu.ui.reader.ReaderActivity
import org.koitharu.kotatsu.utils.ext.resolveDp

@ -26,8 +26,8 @@ import org.koitharu.kotatsu.core.model.FavouriteCategory
import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.core.model.MangaHistory
import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.ui.base.BaseActivity
import org.koitharu.kotatsu.ui.browser.BrowserActivity
import org.koitharu.kotatsu.ui.common.BaseActivity
import org.koitharu.kotatsu.ui.download.DownloadService
import org.koitharu.kotatsu.utils.MangaShortcut
import org.koitharu.kotatsu.utils.ShareHelper

@ -6,7 +6,6 @@ import androidx.core.net.toUri
import androidx.core.text.parseAsHtml
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import coil.load
import com.google.android.material.chip.Chip
import kotlinx.android.synthetic.main.fragment_details.*
import kotlinx.coroutines.Dispatchers
@ -17,15 +16,12 @@ import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.model.FavouriteCategory
import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.core.model.MangaHistory
import org.koitharu.kotatsu.ui.common.BaseFragment
import org.koitharu.kotatsu.ui.base.BaseFragment
import org.koitharu.kotatsu.ui.list.favourites.categories.select.FavouriteCategoriesDialog
import org.koitharu.kotatsu.ui.reader.ReaderActivity
import org.koitharu.kotatsu.ui.search.MangaSearchSheet
import org.koitharu.kotatsu.utils.FileSizeUtils
import org.koitharu.kotatsu.utils.ext.addChips
import org.koitharu.kotatsu.utils.ext.showPopupMenu
import org.koitharu.kotatsu.utils.ext.textAndVisible
import org.koitharu.kotatsu.utils.ext.toFileOrNull
import org.koitharu.kotatsu.utils.ext.*
import kotlin.math.roundToInt
class MangaDetailsFragment : BaseFragment(R.layout.fragment_details), MangaDetailsView,
@ -42,11 +38,11 @@ class MangaDetailsFragment : BaseFragment(R.layout.fragment_details), MangaDetai
override fun onMangaUpdated(manga: Manga) {
this.manga = manga
imageView_cover.load(manga.largeCoverUrl ?: manga.coverUrl) {
fallback(R.drawable.ic_placeholder)
crossfade(true)
lifecycle(this@MangaDetailsFragment)
}
imageView_cover.newImageRequest(manga.largeCoverUrl ?: manga.coverUrl)
.fallback(R.drawable.ic_placeholder)
.crossfade(true)
.lifecycle(this)
.enqueueWith(coil)
textView_title.text = manga.title
textView_subtitle.textAndVisible = manga.altTitle
textView_description.text = manga.description?.parseAsHtml()?.takeUnless(Spanned::isBlank)
@ -138,9 +134,11 @@ class MangaDetailsFragment : BaseFragment(R.layout.fragment_details), MangaDetai
}
v is Chip -> {
when (val tag = v.tag) {
is String -> MangaSearchSheet.show(activity?.supportFragmentManager
?: childFragmentManager,
manga?.source ?: return, tag)
is String -> MangaSearchSheet.show(
activity?.supportFragmentManager
?: childFragmentManager,
manga?.source ?: return, tag
)
}
}
}

@ -7,6 +7,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import moxy.InjectViewState
import moxy.presenterScope
import org.koin.core.component.inject
import org.koitharu.kotatsu.BuildConfig
import org.koitharu.kotatsu.core.exceptions.MangaNotFoundException
import org.koitharu.kotatsu.core.model.Manga
@ -20,8 +21,8 @@ import org.koitharu.kotatsu.domain.favourites.OnFavouritesChangeListener
import org.koitharu.kotatsu.domain.history.HistoryRepository
import org.koitharu.kotatsu.domain.history.OnHistoryChangeListener
import org.koitharu.kotatsu.domain.tracking.TrackingRepository
import org.koitharu.kotatsu.ui.common.BasePresenter
import org.koitharu.kotatsu.ui.common.SharedPresenterHolder
import org.koitharu.kotatsu.ui.base.BasePresenter
import org.koitharu.kotatsu.ui.base.SharedPresenterHolder
import org.koitharu.kotatsu.utils.ext.safe
import java.io.IOException
@ -29,18 +30,15 @@ import java.io.IOException
class MangaDetailsPresenter private constructor(private val key: Int) :
BasePresenter<MangaDetailsView>(), OnHistoryChangeListener, OnFavouritesChangeListener {
private lateinit var historyRepository: HistoryRepository
private lateinit var favouritesRepository: FavouritesRepository
private lateinit var trackingRepository: TrackingRepository
private lateinit var searchRepository: MangaSearchRepository
private val historyRepository by inject<HistoryRepository>()
private val favouritesRepository by inject<FavouritesRepository>()
private val trackingRepository by inject<TrackingRepository>()
private val searchRepository by inject<MangaSearchRepository>()
private val mangaDataRepository by inject<MangaDataRepository>()
private var manga: Manga? = null
override fun onFirstViewAttach() {
historyRepository = HistoryRepository()
favouritesRepository = FavouritesRepository()
trackingRepository = TrackingRepository()
searchRepository = MangaSearchRepository()
super.onFirstViewAttach()
HistoryRepository.subscribe(this)
FavouritesRepository.subscribe(this)
@ -51,7 +49,7 @@ class MangaDetailsPresenter private constructor(private val key: Int) :
viewState.onLoadingStateChanged(true)
try {
val manga = withContext(Dispatchers.IO) {
MangaDataRepository().findMangaById(id)
mangaDataRepository.findMangaById(id)
} ?: throw MangaNotFoundException("Cannot find manga by id")
viewState.onMangaUpdated(manga)
loadDetails(manga, true)
@ -105,7 +103,7 @@ class MangaDetailsPresenter private constructor(private val key: Int) :
val original = repository.getRemoteManga(manga)
repository.delete(manga) || throw IOException("Unable to delete file")
safe {
HistoryRepository().deleteOrSwap(manga, original)
historyRepository.deleteOrSwap(manga, original)
}
}
viewState.onMangaRemoved(manga)
@ -193,8 +191,6 @@ class MangaDetailsPresenter private constructor(private val key: Int) :
}
}
override fun onCategoriesChanged() = Unit
override fun onDestroy() {
HistoryRepository.unsubscribe(this)
FavouritesRepository.unsubscribe(this)

@ -8,7 +8,7 @@ import moxy.viewstate.strategy.alias.SingleState
import org.koitharu.kotatsu.core.model.FavouriteCategory
import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.core.model.MangaHistory
import org.koitharu.kotatsu.ui.common.BaseMvpView
import org.koitharu.kotatsu.ui.base.BaseMvpView
interface MangaDetailsView : BaseMvpView {

@ -7,7 +7,7 @@ import android.os.PowerManager
import android.webkit.MimeTypeMap
import android.widget.Toast
import androidx.core.content.ContextCompat
import coil.Coil
import coil.ImageLoader
import coil.request.ImageRequest
import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex
@ -22,8 +22,8 @@ import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.domain.MangaProviderFactory
import org.koitharu.kotatsu.domain.local.MangaZip
import org.koitharu.kotatsu.ui.common.BaseService
import org.koitharu.kotatsu.ui.common.dialog.CheckBoxAlertDialog
import org.koitharu.kotatsu.ui.base.BaseService
import org.koitharu.kotatsu.ui.base.dialog.CheckBoxAlertDialog
import org.koitharu.kotatsu.utils.CacheUtils
import org.koitharu.kotatsu.utils.ext.*
import java.io.File
@ -40,6 +40,7 @@ class DownloadService : BaseService() {
private val okHttp by inject<OkHttpClient>()
private val cache by inject<PagesCache>()
private val settings by inject<AppSettings>()
private val imageLoader by inject<ImageLoader>()
private val jobs = HashMap<Int, Job>()
private val mutex = Mutex()
@ -74,7 +75,7 @@ class DownloadService : BaseService() {
}
private fun downloadManga(manga: Manga, chaptersIds: Set<Long>?, startId: Int): Job {
return launch(Dispatchers.Default) {
return serviceScope.launch(Dispatchers.Default) {
mutex.lock()
wakeLock.acquire(TimeUnit.HOURS.toMillis(1))
notification.fillFrom(manga)
@ -88,7 +89,7 @@ class DownloadService : BaseService() {
try {
val repo = MangaProviderFactory.create(manga.source)
val cover = safe {
Coil.execute(
imageLoader.execute(
ImageRequest.Builder(this@DownloadService)
.data(manga.coverUrl)
.build()

@ -9,7 +9,7 @@ import org.koin.android.ext.android.inject
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.prefs.ListMode
import org.koitharu.kotatsu.ui.common.AlertDialogFragment
import org.koitharu.kotatsu.ui.base.AlertDialogFragment
class ListModeSelectDialog : AlertDialogFragment(R.layout.dialog_list_mode), View.OnClickListener {
@ -56,7 +56,8 @@ class ListModeSelectDialog : AlertDialogFragment(R.layout.dialog_list_mode), Vie
private const val TAG = "ListModeSelectDialog"
fun show(fm: FragmentManager) = ListModeSelectDialog()
.show(fm,
.show(
fm,
TAG
)
}

@ -24,7 +24,7 @@ import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.core.prefs.AppSection
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.domain.MangaProviderFactory
import org.koitharu.kotatsu.ui.common.BaseActivity
import org.koitharu.kotatsu.ui.base.BaseActivity
import org.koitharu.kotatsu.ui.list.favourites.FavouritesContainerFragment
import org.koitharu.kotatsu.ui.list.feed.FeedFragment
import org.koitharu.kotatsu.ui.list.history.HistoryListFragment

@ -1,18 +1,20 @@
package org.koitharu.kotatsu.ui.list
import moxy.InjectViewState
import org.koin.core.component.inject
import org.koitharu.kotatsu.core.exceptions.EmptyHistoryException
import org.koitharu.kotatsu.domain.MangaProviderFactory
import org.koitharu.kotatsu.domain.history.HistoryRepository
import org.koitharu.kotatsu.ui.common.BasePresenter
import org.koitharu.kotatsu.ui.base.BasePresenter
import org.koitharu.kotatsu.ui.reader.ReaderState
@InjectViewState
class MainPresenter : BasePresenter<MainView>() {
private val historyRepository by inject<HistoryRepository>()
fun openLastReader() {
launchLoadingJob {
val historyRepository = HistoryRepository()
val manga = historyRepository.getList(0, 1).firstOrNull()
?: throw EmptyHistoryException()
val history = historyRepository.getOne(manga) ?: throw EmptyHistoryException()

@ -1,7 +1,7 @@
package org.koitharu.kotatsu.ui.list
import moxy.viewstate.strategy.alias.OneExecution
import org.koitharu.kotatsu.ui.common.BaseMvpView
import org.koitharu.kotatsu.ui.base.BaseMvpView
import org.koitharu.kotatsu.ui.reader.ReaderState
interface MainView : BaseMvpView {

@ -1,27 +1,35 @@
package org.koitharu.kotatsu.ui.list
import android.view.ViewGroup
import coil.clear
import coil.load
import coil.ImageLoader
import coil.request.Disposable
import kotlinx.android.synthetic.main.item_manga_grid.*
import org.koin.core.component.inject
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.core.model.MangaHistory
import org.koitharu.kotatsu.ui.common.list.BaseViewHolder
import org.koitharu.kotatsu.ui.base.list.BaseViewHolder
import org.koitharu.kotatsu.utils.ext.enqueueWith
import org.koitharu.kotatsu.utils.ext.newImageRequest
class MangaGridHolder(parent: ViewGroup) : BaseViewHolder<Manga, MangaHistory?>(parent, R.layout.item_manga_grid) {
class MangaGridHolder(parent: ViewGroup) :
BaseViewHolder<Manga, MangaHistory?>(parent, R.layout.item_manga_grid) {
private val coil by inject<ImageLoader>()
private var imageRequest: Disposable? = null
override fun onBind(data: Manga, extra: MangaHistory?) {
imageView_cover.clear()
textView_title.text = data.title
imageView_cover.load(data.coverUrl) {
placeholder(R.drawable.ic_placeholder)
fallback(R.drawable.ic_placeholder)
error(R.drawable.ic_placeholder)
}
imageRequest?.dispose()
imageRequest = imageView_cover.newImageRequest(data.coverUrl)
.placeholder(R.drawable.ic_placeholder)
.fallback(R.drawable.ic_placeholder)
.error(R.drawable.ic_placeholder)
.enqueueWith(coil)
}
override fun onRecycled() {
imageView_cover.clear()
imageRequest?.dispose()
imageView_cover.setImageDrawable(null)
}
}

@ -4,15 +4,15 @@ import android.view.ViewGroup
import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.core.model.MangaHistory
import org.koitharu.kotatsu.core.prefs.ListMode
import org.koitharu.kotatsu.ui.common.list.BaseRecyclerAdapter
import org.koitharu.kotatsu.ui.common.list.OnRecyclerItemClickListener
import org.koitharu.kotatsu.ui.base.list.BaseRecyclerAdapter
import org.koitharu.kotatsu.ui.base.list.OnRecyclerItemClickListener
class MangaListAdapter(onItemClickListener: OnRecyclerItemClickListener<Manga>) :
BaseRecyclerAdapter<Manga, MangaHistory?>(onItemClickListener) {
var listMode: ListMode = ListMode.LIST
override fun onCreateViewHolder(parent: ViewGroup) = when(listMode) {
override fun onCreateViewHolder(parent: ViewGroup) = when (listMode) {
ListMode.LIST -> MangaListHolder(parent)
ListMode.DETAILED_LIST -> MangaListDetailsHolder(
parent

@ -3,29 +3,37 @@ package org.koitharu.kotatsu.ui.list
import android.annotation.SuppressLint
import android.view.ViewGroup
import androidx.core.view.isVisible
import coil.clear
import coil.load
import coil.ImageLoader
import coil.request.Disposable
import kotlinx.android.synthetic.main.item_manga_list_details.*
import org.koin.core.component.inject
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.core.model.MangaHistory
import org.koitharu.kotatsu.ui.common.list.BaseViewHolder
import org.koitharu.kotatsu.ui.base.list.BaseViewHolder
import org.koitharu.kotatsu.utils.ext.enqueueWith
import org.koitharu.kotatsu.utils.ext.newImageRequest
import org.koitharu.kotatsu.utils.ext.textAndVisible
import kotlin.math.roundToInt
class MangaListDetailsHolder(parent: ViewGroup) : BaseViewHolder<Manga, MangaHistory?>(parent, R.layout.item_manga_list_details) {
class MangaListDetailsHolder(
parent: ViewGroup
) : BaseViewHolder<Manga, MangaHistory?>(parent, R.layout.item_manga_list_details) {
private val coil by inject<ImageLoader>()
private var imageRequest: Disposable? = null
@SuppressLint("SetTextI18n")
override fun onBind(data: Manga, extra: MangaHistory?) {
imageView_cover.clear()
imageRequest?.dispose()
textView_title.text = data.title
textView_subtitle.textAndVisible = data.altTitle
imageView_cover.load(data.coverUrl) {
placeholder(R.drawable.ic_placeholder)
fallback(R.drawable.ic_placeholder)
error(R.drawable.ic_placeholder)
}
if(data.rating == Manga.NO_RATING) {
imageView_cover.newImageRequest(data.coverUrl)
.placeholder(R.drawable.ic_placeholder)
.fallback(R.drawable.ic_placeholder)
.error(R.drawable.ic_placeholder)
.enqueueWith(coil)
if (data.rating == Manga.NO_RATING) {
textView_rating.isVisible = false
} else {
textView_rating.text = "${(data.rating * 10).roundToInt()}/10"
@ -37,6 +45,7 @@ class MangaListDetailsHolder(parent: ViewGroup) : BaseViewHolder<Manga, MangaHis
}
override fun onRecycled() {
imageView_cover.clear()
imageRequest?.dispose()
imageView_cover.setImageDrawable(null)
}
}

@ -22,13 +22,13 @@ import org.koitharu.kotatsu.core.model.MangaTag
import org.koitharu.kotatsu.core.model.SortOrder
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.prefs.ListMode
import org.koitharu.kotatsu.ui.common.BaseFragment
import org.koitharu.kotatsu.ui.common.list.OnRecyclerItemClickListener
import org.koitharu.kotatsu.ui.common.list.PaginationScrollListener
import org.koitharu.kotatsu.ui.common.list.ProgressBarAdapter
import org.koitharu.kotatsu.ui.common.list.decor.ItemTypeDividerDecoration
import org.koitharu.kotatsu.ui.common.list.decor.SectionItemDecoration
import org.koitharu.kotatsu.ui.common.list.decor.SpacingItemDecoration
import org.koitharu.kotatsu.ui.base.BaseFragment
import org.koitharu.kotatsu.ui.base.list.OnRecyclerItemClickListener
import org.koitharu.kotatsu.ui.base.list.PaginationScrollListener
import org.koitharu.kotatsu.ui.base.list.ProgressBarAdapter
import org.koitharu.kotatsu.ui.base.list.decor.ItemTypeDividerDecoration
import org.koitharu.kotatsu.ui.base.list.decor.SectionItemDecoration
import org.koitharu.kotatsu.ui.base.list.decor.SpacingItemDecoration
import org.koitharu.kotatsu.ui.details.MangaDetailsActivity
import org.koitharu.kotatsu.ui.list.filter.FilterAdapter
import org.koitharu.kotatsu.ui.list.filter.OnFilterChangedListener
@ -48,7 +48,7 @@ abstract class MangaListFragment<E> : BaseFragment(R.layout.fragment_list),
private var adapter: MangaListAdapter? = null
private var progressAdapter: ProgressBarAdapter? = null
private var paginationListener : PaginationScrollListener? = null
private var paginationListener: PaginationScrollListener? = null
protected var isSwipeRefreshEnabled = true
override fun onCreate(savedInstanceState: Bundle?) {

@ -1,30 +1,38 @@
package org.koitharu.kotatsu.ui.list
import android.view.ViewGroup
import coil.clear
import coil.load
import coil.ImageLoader
import coil.request.Disposable
import kotlinx.android.synthetic.main.item_manga_list.*
import org.koin.core.component.inject
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.core.model.MangaHistory
import org.koitharu.kotatsu.ui.common.list.BaseViewHolder
import org.koitharu.kotatsu.ui.base.list.BaseViewHolder
import org.koitharu.kotatsu.utils.ext.enqueueWith
import org.koitharu.kotatsu.utils.ext.newImageRequest
import org.koitharu.kotatsu.utils.ext.textAndVisible
class MangaListHolder(parent: ViewGroup) :
BaseViewHolder<Manga, MangaHistory?>(parent, R.layout.item_manga_list) {
class MangaListHolder(
parent: ViewGroup
) : BaseViewHolder<Manga, MangaHistory?>(parent, R.layout.item_manga_list) {
private val coil by inject<ImageLoader>()
private var imageRequest: Disposable? = null
override fun onBind(data: Manga, extra: MangaHistory?) {
imageView_cover.clear()
imageRequest?.dispose()
textView_title.text = data.title
textView_subtitle.textAndVisible = data.tags.joinToString(", ") { it.title }
imageView_cover.load(data.coverUrl) {
placeholder(R.drawable.ic_placeholder)
fallback(R.drawable.ic_placeholder)
error(R.drawable.ic_placeholder)
}
imageRequest = imageView_cover.newImageRequest(data.coverUrl)
.placeholder(R.drawable.ic_placeholder)
.fallback(R.drawable.ic_placeholder)
.error(R.drawable.ic_placeholder)
.enqueueWith(coil)
}
override fun onRecycled() {
imageView_cover.clear()
imageRequest?.dispose()
imageView_cover.setImageDrawable(null)
}
}

@ -21,11 +21,11 @@ import org.koitharu.kotatsu.core.model.MangaTag
import org.koitharu.kotatsu.core.model.SortOrder
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.prefs.ListMode
import org.koitharu.kotatsu.ui.common.BaseBottomSheet
import org.koitharu.kotatsu.ui.common.list.OnRecyclerItemClickListener
import org.koitharu.kotatsu.ui.common.list.PaginationScrollListener
import org.koitharu.kotatsu.ui.common.list.ProgressBarAdapter
import org.koitharu.kotatsu.ui.common.list.decor.SpacingItemDecoration
import org.koitharu.kotatsu.ui.base.BaseBottomSheet
import org.koitharu.kotatsu.ui.base.list.OnRecyclerItemClickListener
import org.koitharu.kotatsu.ui.base.list.PaginationScrollListener
import org.koitharu.kotatsu.ui.base.list.ProgressBarAdapter
import org.koitharu.kotatsu.ui.base.list.decor.SpacingItemDecoration
import org.koitharu.kotatsu.ui.details.MangaDetailsActivity
import org.koitharu.kotatsu.utils.UiUtils
import org.koitharu.kotatsu.utils.ext.*

@ -9,7 +9,7 @@ import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.core.model.MangaFilter
import org.koitharu.kotatsu.core.model.MangaTag
import org.koitharu.kotatsu.core.model.SortOrder
import org.koitharu.kotatsu.ui.common.BaseMvpView
import org.koitharu.kotatsu.ui.base.BaseMvpView
interface MangaListView<E> : BaseMvpView {

@ -13,14 +13,15 @@ import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.model.FavouriteCategory
import org.koitharu.kotatsu.domain.favourites.FavouritesRepository
import org.koitharu.kotatsu.domain.favourites.OnFavouritesChangeListener
import org.koitharu.kotatsu.ui.common.BaseFragment
import org.koitharu.kotatsu.ui.base.BaseFragment
import org.koitharu.kotatsu.ui.list.favourites.categories.CategoriesActivity
import org.koitharu.kotatsu.ui.list.favourites.categories.FavouriteCategoriesPresenter
import org.koitharu.kotatsu.ui.list.favourites.categories.FavouriteCategoriesView
import java.util.*
import kotlin.collections.ArrayList
class FavouritesContainerFragment : BaseFragment(R.layout.fragment_favourites), FavouriteCategoriesView,
class FavouritesContainerFragment : BaseFragment(R.layout.fragment_favourites),
FavouriteCategoriesView,
OnFavouritesChangeListener {
private val presenter by moxyPresenter(factory = ::FavouriteCategoriesPresenter)

@ -1,36 +1,28 @@
package org.koitharu.kotatsu.ui.list.favourites
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import moxy.InjectViewState
import moxy.presenterScope
import org.koin.core.component.get
import org.koitharu.kotatsu.BuildConfig
import org.koitharu.kotatsu.domain.favourites.FavouritesRepository
import org.koitharu.kotatsu.ui.common.BasePresenter
import org.koitharu.kotatsu.ui.base.BasePresenter
import org.koitharu.kotatsu.ui.list.MangaListView
@InjectViewState
class FavouritesListPresenter : BasePresenter<MangaListView<Unit>>() {
private lateinit var repository: FavouritesRepository
override fun onFirstViewAttach() {
repository = FavouritesRepository()
super.onFirstViewAttach()
}
private val repository = get<FavouritesRepository>()
fun loadList(categoryId: Long, offset: Int) {
presenterScope.launch {
viewState.onLoadingStateChanged(true)
try {
val list = withContext(Dispatchers.IO) {
if (categoryId == 0L) {
repository.getAllManga(offset = offset)
} else {
repository.getManga(categoryId = categoryId, offset = offset)
}
val list = if (categoryId == 0L) {
repository.getAllManga(offset = offset)
} else {
repository.getManga(categoryId = categoryId, offset = offset)
}
if (offset == 0) {
viewState.onListChanged(list)

@ -5,10 +5,11 @@ import androidx.viewpager2.adapter.FragmentStateAdapter
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayoutMediator
import org.koitharu.kotatsu.core.model.FavouriteCategory
import org.koitharu.kotatsu.ui.common.list.AdapterUpdater
import org.koitharu.kotatsu.ui.base.list.AdapterUpdater
import org.koitharu.kotatsu.utils.ext.replaceWith
class FavouritesPagerAdapter(fragment: Fragment) : FragmentStateAdapter(fragment), TabLayoutMediator.TabConfigurationStrategy {
class FavouritesPagerAdapter(fragment: Fragment) : FragmentStateAdapter(fragment),
TabLayoutMediator.TabConfigurationStrategy {
private val dataSet = ArrayList<FavouriteCategory>()

@ -17,9 +17,9 @@ import kotlinx.android.synthetic.main.activity_categories.*
import moxy.ktx.moxyPresenter
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.model.FavouriteCategory
import org.koitharu.kotatsu.ui.common.BaseActivity
import org.koitharu.kotatsu.ui.common.dialog.TextInputDialog
import org.koitharu.kotatsu.ui.common.list.OnRecyclerItemClickListener
import org.koitharu.kotatsu.ui.base.BaseActivity
import org.koitharu.kotatsu.ui.base.dialog.TextInputDialog
import org.koitharu.kotatsu.ui.base.list.OnRecyclerItemClickListener
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
import org.koitharu.kotatsu.utils.ext.showPopupMenu
@ -61,7 +61,9 @@ class CategoriesActivity : BaseActivity(), OnRecyclerItemClickListener<Favourite
}
override fun onItemLongClick(item: FavouriteCategory, position: Int, view: View): Boolean {
reorderHelper.startDrag(recyclerView.findViewHolderForAdapterPosition(position) ?: return false)
reorderHelper.startDrag(
recyclerView.findViewHolderForAdapterPosition(position) ?: return false
)
return true
}

@ -5,9 +5,9 @@ import android.view.MotionEvent
import android.view.ViewGroup
import kotlinx.android.synthetic.main.item_category.*
import org.koitharu.kotatsu.core.model.FavouriteCategory
import org.koitharu.kotatsu.ui.common.list.BaseRecyclerAdapter
import org.koitharu.kotatsu.ui.common.list.BaseViewHolder
import org.koitharu.kotatsu.ui.common.list.OnRecyclerItemClickListener
import org.koitharu.kotatsu.ui.base.list.BaseRecyclerAdapter
import org.koitharu.kotatsu.ui.base.list.BaseViewHolder
import org.koitharu.kotatsu.ui.base.list.OnRecyclerItemClickListener
class CategoriesAdapter(private val onItemClickListener: OnRecyclerItemClickListener<FavouriteCategory>) :
BaseRecyclerAdapter<FavouriteCategory, Unit>() {
@ -25,7 +25,11 @@ class CategoriesAdapter(private val onItemClickListener: OnRecyclerItemClickList
}
holder.imageView_handle.setOnTouchListener { v, event ->
if (event.actionMasked == MotionEvent.ACTION_DOWN) {
onItemClickListener.onItemLongClick(holder.requireData(), holder.bindingAdapterPosition, v)
onItemClickListener.onItemLongClick(
holder.requireData(),
holder.bindingAdapterPosition,
v
)
} else {
false
}

@ -4,7 +4,7 @@ import android.view.ViewGroup
import kotlinx.android.synthetic.main.item_category.*
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.model.FavouriteCategory
import org.koitharu.kotatsu.ui.common.list.BaseViewHolder
import org.koitharu.kotatsu.ui.base.list.BaseViewHolder
class CategoryHolder(parent: ViewGroup) :
BaseViewHolder<FavouriteCategory, Unit>(parent, R.layout.item_category) {

@ -3,20 +3,18 @@ package org.koitharu.kotatsu.ui.list.favourites.categories
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import moxy.InjectViewState
import org.koin.core.component.get
import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.domain.favourites.FavouritesRepository
import org.koitharu.kotatsu.ui.common.BasePresenter
import org.koitharu.kotatsu.ui.base.BasePresenter
@InjectViewState
class FavouriteCategoriesPresenter : BasePresenter<FavouriteCategoriesView>() {
private lateinit var repository: FavouritesRepository
private val reorderMutex by lazy {
Mutex()
}
private val repository = get<FavouritesRepository>()
private val reorderMutex by lazy(LazyThreadSafetyMode.NONE) { Mutex() }
override fun onFirstViewAttach() {
repository = FavouritesRepository()
super.onFirstViewAttach()
loadAllCategories()
}

@ -4,7 +4,7 @@ import moxy.viewstate.strategy.AddToEndSingleStrategy
import moxy.viewstate.strategy.StateStrategyType
import moxy.viewstate.strategy.alias.AddToEndSingle
import org.koitharu.kotatsu.core.model.FavouriteCategory
import org.koitharu.kotatsu.ui.common.BaseMvpView
import org.koitharu.kotatsu.ui.base.BaseMvpView
interface FavouriteCategoriesView : BaseMvpView {

@ -5,8 +5,8 @@ import android.view.ViewGroup
import android.widget.Checkable
import androidx.core.util.set
import org.koitharu.kotatsu.core.model.FavouriteCategory
import org.koitharu.kotatsu.ui.common.list.BaseRecyclerAdapter
import org.koitharu.kotatsu.ui.common.list.BaseViewHolder
import org.koitharu.kotatsu.ui.base.list.BaseRecyclerAdapter
import org.koitharu.kotatsu.ui.base.list.BaseViewHolder
class CategoriesSelectAdapter(private val listener: OnCategoryCheckListener) :
BaseRecyclerAdapter<FavouriteCategory, Boolean>() {

@ -4,7 +4,7 @@ import android.view.ViewGroup
import kotlinx.android.synthetic.main.item_category_checkable.*
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.model.FavouriteCategory
import org.koitharu.kotatsu.ui.common.list.BaseViewHolder
import org.koitharu.kotatsu.ui.base.list.BaseViewHolder
class CategoryCheckableHolder(parent: ViewGroup) :
BaseViewHolder<FavouriteCategory, Boolean>(parent, R.layout.item_category_checkable) {

@ -10,8 +10,8 @@ import moxy.ktx.moxyPresenter
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.model.FavouriteCategory
import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.ui.common.BaseBottomSheet
import org.koitharu.kotatsu.ui.common.dialog.TextInputDialog
import org.koitharu.kotatsu.ui.base.BaseBottomSheet
import org.koitharu.kotatsu.ui.base.dialog.TextInputDialog
import org.koitharu.kotatsu.ui.list.favourites.categories.FavouriteCategoriesPresenter
import org.koitharu.kotatsu.ui.list.favourites.categories.FavouriteCategoriesView
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
@ -31,7 +31,8 @@ class FavouriteCategoriesDialog : BaseBottomSheet(R.layout.dialog_favorite_categ
super.onViewCreated(view, savedInstanceState)
adapter =
CategoriesSelectAdapter(
this)
this
)
recyclerView_categories.adapter = adapter
textView_add.setOnClickListener {
createCategory()
@ -86,8 +87,10 @@ class FavouriteCategoriesDialog : BaseBottomSheet(R.layout.dialog_favorite_categ
fun show(fm: FragmentManager, manga: Manga) = FavouriteCategoriesDialog()
.withArgs(1) {
putParcelable(ARG_MANGA, manga)
}.show(fm,
TAG)
putParcelable(ARG_MANGA, manga)
}.show(
fm,
TAG
)
}
}

@ -2,9 +2,9 @@ package org.koitharu.kotatsu.ui.list.feed
import android.view.ViewGroup
import org.koitharu.kotatsu.core.model.TrackingLogItem
import org.koitharu.kotatsu.ui.common.list.BaseRecyclerAdapter
import org.koitharu.kotatsu.ui.common.list.BaseViewHolder
import org.koitharu.kotatsu.ui.common.list.OnRecyclerItemClickListener
import org.koitharu.kotatsu.ui.base.list.BaseRecyclerAdapter
import org.koitharu.kotatsu.ui.base.list.BaseViewHolder
import org.koitharu.kotatsu.ui.base.list.OnRecyclerItemClickListener
class FeedAdapter(onItemClickListener: OnRecyclerItemClickListener<TrackingLogItem>? = null) :
BaseRecyclerAdapter<TrackingLogItem, Unit>(onItemClickListener) {

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

Loading…
Cancel
Save