diff --git a/app/src/main/java/io/legado/app/base/BaseViewModel.kt b/app/src/main/java/io/legado/app/base/BaseViewModel.kt index f21cbdf06..f24e5876c 100644 --- a/app/src/main/java/io/legado/app/base/BaseViewModel.kt +++ b/app/src/main/java/io/legado/app/base/BaseViewModel.kt @@ -12,12 +12,12 @@ import org.jetbrains.anko.AnkoLogger open class BaseViewModel(application: Application) : AndroidViewModel(application), CoroutineScope by MainScope(), AnkoLogger { - fun execute(block: suspend CoroutineScope.() -> T): Coroutine { - return Coroutine.async(this) { block() } + fun execute(scope: CoroutineScope = this, block: suspend CoroutineScope.() -> T): Coroutine { + return Coroutine.async(scope) { block() } } - fun submit(block: suspend CoroutineScope.() -> Deferred): Coroutine { - return Coroutine.async(this) { block().await() } + fun submit(scope: CoroutineScope = this, block: suspend CoroutineScope.() -> Deferred): Coroutine { + return Coroutine.async(scope) { block().await() } } fun plus(coroutine: Coroutine): Coroutine { diff --git a/app/src/main/java/io/legado/app/help/coroutine/Coroutine.kt b/app/src/main/java/io/legado/app/help/coroutine/Coroutine.kt index 030295275..087107d4a 100644 --- a/app/src/main/java/io/legado/app/help/coroutine/Coroutine.kt +++ b/app/src/main/java/io/legado/app/help/coroutine/Coroutine.kt @@ -7,7 +7,7 @@ class Coroutine() { companion object { - private val DEFAULT = MainScope() + val DEFAULT = MainScope() fun async(scope: CoroutineScope = DEFAULT, block: suspend CoroutineScope.() -> T): Coroutine { return Coroutine(scope, block) @@ -22,6 +22,7 @@ class Coroutine() { private var job: Job? = null private var start: (suspend CoroutineScope.() -> Unit)? = null + private var execute: (suspend CoroutineScope.(T?) -> Unit)? = null private var success: (suspend CoroutineScope.(T?) -> Unit)? = null private var error: (suspend CoroutineScope.(Throwable) -> Unit)? = null private var finally: (suspend CoroutineScope.() -> Unit)? = null @@ -80,6 +81,15 @@ class Coroutine() { return this@Coroutine } + fun onExecute(execute: suspend CoroutineScope.(T?) -> Unit): Coroutine { + if (this.interceptor != null) { + this.interceptor!!.execute = execute + } else { + this.execute = execute + } + return this@Coroutine + } + fun onSuccess(success: suspend CoroutineScope.(T?) -> Unit): Coroutine { if (this.interceptor != null) { this.interceptor!!.success = success @@ -117,7 +127,7 @@ class Coroutine() { { start?.invoke(this) - val result = executeBlock(block, timeMillis ?: 0L) + val result = executeBlockIO(block, timeMillis ?: 0L) success?.invoke(this, result) }, @@ -136,11 +146,15 @@ class Coroutine() { }) } - private suspend fun executeBlock(block: suspend CoroutineScope.() -> T, timeMillis: Long): T? { - val asyncBlock = withContext(Dispatchers.IO) { - block() + private suspend fun executeBlockIO(block: suspend CoroutineScope.() -> T, timeMillis: Long): T? { + val result: T = withContext(Dispatchers.IO) { + val b = block() + + execute?.invoke(this, b) + + b } - return if (timeMillis > 0L) withTimeout(timeMillis) { asyncBlock } else asyncBlock + return if (timeMillis > 0L) withTimeout(timeMillis) { result } else result } private suspend fun tryCatch( diff --git a/app/src/main/java/io/legado/app/help/storage/Restore.kt b/app/src/main/java/io/legado/app/help/storage/Restore.kt index 62342bb9c..28cfec374 100644 --- a/app/src/main/java/io/legado/app/help/storage/Restore.kt +++ b/app/src/main/java/io/legado/app/help/storage/Restore.kt @@ -102,58 +102,61 @@ object Restore { source.bookSourceUrl = jsonItem.readString("bookSourceUrl") ?: "" if (source.bookSourceUrl.isBlank()) continue if (source.bookSourceUrl in existingSources) continue - source.bookSourceName = jsonItem.readString("bookSourceName") ?: "" - source.bookSourceGroup = jsonItem.readString("bookSourceGroup") ?: "" - source.loginUrl = jsonItem.readString("loginUrl") - source.bookUrlPattern = jsonItem.readString("ruleBookUrlPattern") - source.customOrder = jsonItem.readInt("serialNumber") ?: 0 - val searchRule = SearchRule( - searchUrl = OldRule.toNewUrl(jsonItem.readString("ruleSearchUrl")), - bookList = jsonItem.readString("ruleSearchList"), - name = jsonItem.readString("ruleSearchName"), - author = jsonItem.readString("ruleSearchAuthor"), - intro = jsonItem.readString("ruleSearchIntroduce"), - kind = jsonItem.readString("ruleSearchKind"), - bookUrl = jsonItem.readString("ruleSearchNoteUrl"), - coverUrl = jsonItem.readString("ruleSearchCoverUrl"), - lastChapter = jsonItem.readString("ruleSearchLastChapter") - ) - source.ruleSearch = GSON.toJson(searchRule) - val exploreRule = ExploreRule( - exploreUrl = OldRule.toNewUrl(jsonItem.readString("ruleFindUrl")), - bookList = jsonItem.readString("ruleFindList"), - name = jsonItem.readString("ruleFindName"), - author = jsonItem.readString("ruleFindAuthor"), - intro = jsonItem.readString("ruleFindIntroduce"), - kind = jsonItem.readString("ruleFindKind"), - bookUrl = jsonItem.readString("ruleFindNoteUrl"), - coverUrl = jsonItem.readString("ruleFindCoverUrl"), - lastChapter = jsonItem.readString("ruleFindLastChapter") - ) - source.ruleExplore = GSON.toJson(exploreRule) - val bookInfoRule = BookInfoRule( - init = jsonItem.readString("ruleBookInfoInit"), - name = jsonItem.readString("ruleBookName"), - author = jsonItem.readString("ruleBookAuthor"), - intro = jsonItem.readString("ruleIntroduce"), - kind = jsonItem.readString("ruleBookKind"), - coverUrl = jsonItem.readString("ruleCoverUrl"), - lastChapter = jsonItem.readString("ruleBookLastChapter"), - tocUrl = jsonItem.readString("ruleChapterUrl") - ) - source.ruleBookInfo = GSON.toJson(bookInfoRule) - val chapterRule = TocRule( - chapterList = jsonItem.readString("ruleChapterList"), - chapterName = jsonItem.readString("ruleChapterName"), - chapterUrl = jsonItem.readString("ruleContentUrl"), - nextTocUrl = jsonItem.readString("ruleChapterUrlNext") - ) - source.ruleToc = GSON.toJson(chapterRule) - val contentRule = ContentRule( - content = jsonItem.readString("ruleBookContent"), - nextContentUrl = jsonItem.readString("ruleContentUrlNext") - ) - source.ruleContent = GSON.toJson(contentRule) + runCatching { + source.bookSourceName = jsonItem.readString("bookSourceName") ?: "" + source.bookSourceGroup = jsonItem.readString("bookSourceGroup") ?: "" + source.loginUrl = jsonItem.readString("loginUrl") + source.bookUrlPattern = jsonItem.readString("ruleBookUrlPattern") + source.customOrder = jsonItem.readInt("serialNumber") ?: 0 + val searchRule = SearchRule( + searchUrl = OldRule.toNewUrl(jsonItem.readString("ruleSearchUrl")), + bookList = jsonItem.readString("ruleSearchList"), + name = jsonItem.readString("ruleSearchName"), + author = jsonItem.readString("ruleSearchAuthor"), + intro = jsonItem.readString("ruleSearchIntroduce"), + kind = jsonItem.readString("ruleSearchKind"), + bookUrl = jsonItem.readString("ruleSearchNoteUrl"), + coverUrl = jsonItem.readString("ruleSearchCoverUrl"), + lastChapter = jsonItem.readString("ruleSearchLastChapter") + ) + source.ruleSearch = GSON.toJson(searchRule) + val exploreRule = ExploreRule( + exploreUrl = OldRule.toNewUrl(jsonItem.readString("ruleFindUrl")), + bookList = jsonItem.readString("ruleFindList"), + name = jsonItem.readString("ruleFindName"), + author = jsonItem.readString("ruleFindAuthor"), + intro = jsonItem.readString("ruleFindIntroduce"), + kind = jsonItem.readString("ruleFindKind"), + bookUrl = jsonItem.readString("ruleFindNoteUrl"), + coverUrl = jsonItem.readString("ruleFindCoverUrl"), + lastChapter = jsonItem.readString("ruleFindLastChapter") + ) + source.ruleExplore = GSON.toJson(exploreRule) + val bookInfoRule = BookInfoRule( + init = jsonItem.readString("ruleBookInfoInit"), + name = jsonItem.readString("ruleBookName"), + author = jsonItem.readString("ruleBookAuthor"), + intro = jsonItem.readString("ruleIntroduce"), + kind = jsonItem.readString("ruleBookKind"), + coverUrl = jsonItem.readString("ruleCoverUrl"), + lastChapter = jsonItem.readString("ruleBookLastChapter"), + tocUrl = jsonItem.readString("ruleChapterUrl") + ) + source.ruleBookInfo = GSON.toJson(bookInfoRule) + val chapterRule = TocRule( + chapterList = jsonItem.readString("ruleChapterList"), + chapterName = jsonItem.readString("ruleChapterName"), + chapterUrl = jsonItem.readString("ruleContentUrl"), + nextTocUrl = jsonItem.readString("ruleChapterUrlNext") + ) + source.ruleToc = GSON.toJson(chapterRule) + val contentRule = ContentRule( + content = jsonItem.readString("ruleBookContent"), + nextContentUrl = jsonItem.readString("ruleContentUrlNext") + ) + source.ruleContent = GSON.toJson(contentRule) + } + bookSources.add(source) } App.db.bookSourceDao().insert(*bookSources.toTypedArray()) diff --git a/app/src/main/java/io/legado/app/model/WebBook.kt b/app/src/main/java/io/legado/app/model/WebBook.kt index b8bafd83a..0b764b898 100644 --- a/app/src/main/java/io/legado/app/model/WebBook.kt +++ b/app/src/main/java/io/legado/app/model/WebBook.kt @@ -10,14 +10,15 @@ import io.legado.app.model.webbook.BookChapterList import io.legado.app.model.webbook.BookContent import io.legado.app.model.webbook.BookInfo import io.legado.app.model.webbook.BookList +import kotlinx.coroutines.CoroutineScope class WebBook(private val bookSource: BookSource) { val sourceUrl: String get() = bookSource.bookSourceUrl - fun searchBook(key: String, page: Int?, isSearch: Boolean = true): Coroutine> { - return Coroutine.async { + fun searchBook(key: String, page: Int?, isSearch: Boolean = true, scope: CoroutineScope = Coroutine.DEFAULT): Coroutine> { + return Coroutine.async(scope) { bookSource.getSearchRule().searchUrl?.let { searchUrl -> val analyzeUrl = AnalyzeUrl(searchUrl, key, page, baseUrl = sourceUrl) val response = analyzeUrl.getResponseAsync().await() diff --git a/app/src/main/java/io/legado/app/model/webbook/SourceDebug.kt b/app/src/main/java/io/legado/app/model/webbook/SourceDebug.kt index a7cd9c5e7..a51922f7b 100644 --- a/app/src/main/java/io/legado/app/model/webbook/SourceDebug.kt +++ b/app/src/main/java/io/legado/app/model/webbook/SourceDebug.kt @@ -5,6 +5,7 @@ import io.legado.app.data.entities.Book import io.legado.app.data.entities.BookChapter import io.legado.app.help.BookHelp import io.legado.app.help.coroutine.CompositeCoroutine +import io.legado.app.help.coroutine.Coroutine import io.legado.app.model.WebBook import io.legado.app.utils.htmlFormat import io.legado.app.utils.isAbsUrl diff --git a/app/src/main/java/io/legado/app/ui/main/MainActivity.kt b/app/src/main/java/io/legado/app/ui/main/MainActivity.kt index 774c6f329..41dd8574a 100644 --- a/app/src/main/java/io/legado/app/ui/main/MainActivity.kt +++ b/app/src/main/java/io/legado/app/ui/main/MainActivity.kt @@ -14,13 +14,14 @@ import io.legado.app.constant.Bus import io.legado.app.help.permission.Permissions import io.legado.app.help.permission.PermissionsCompat import io.legado.app.lib.theme.ATH -import io.legado.app.lib.theme.Selector -import io.legado.app.lib.theme.ThemeStore import io.legado.app.ui.main.bookshelf.BookshelfFragment import io.legado.app.ui.main.findbook.FindBookFragment import io.legado.app.ui.main.my.MyFragment import io.legado.app.ui.main.rss.RssFragment -import io.legado.app.utils.* +import io.legado.app.utils.getPrefInt +import io.legado.app.utils.getViewModel +import io.legado.app.utils.observeEvent +import io.legado.app.utils.putPrefInt import kotlinx.android.synthetic.main.activity_main.* import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.launch diff --git a/app/src/main/java/io/legado/app/ui/search/SearchViewModel.kt b/app/src/main/java/io/legado/app/ui/search/SearchViewModel.kt index e81d5c1b0..ca48a9312 100644 --- a/app/src/main/java/io/legado/app/ui/search/SearchViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/search/SearchViewModel.kt @@ -4,37 +4,32 @@ import android.app.Application import io.legado.app.App import io.legado.app.base.BaseViewModel import io.legado.app.help.coroutine.CompositeCoroutine +import io.legado.app.help.coroutine.Coroutine import io.legado.app.model.WebBook class SearchViewModel(application: Application) : BaseViewModel(application) { - private val tasks: CompositeCoroutine = CompositeCoroutine() + private var task: Coroutine<*>? = null + var searchPage = 0 fun search(key: String, start: ((startTime: Long) -> Unit)? = null, finally: (() -> Unit)? = null) { if (key.isEmpty()) return - tasks.clear() + task?.cancel() start?.invoke(System.currentTimeMillis()) - execute { + task = execute {//onCleared时自动取消 val bookSourceList = App.db.bookSourceDao().allEnabled for (item in bookSourceList) { - val search = WebBook(item).searchBook(key, searchPage) - .onSuccess { searchBookS -> + //task取消时自动取消 by (scope = this@execute) + WebBook(item).searchBook(key, searchPage, scope = this) + .onExecute { searchBookS -> searchBookS?.let { - execute { - App.db.searchBookDao().insert(*it.toTypedArray()) - } + App.db.searchBookDao().insert(*it.toTypedArray()) } } - tasks.add(search) } }.onError { it.printStackTrace() } } - - override fun onCleared() { - super.onCleared() - tasks.clear() - } } diff --git a/app/src/main/java/io/legado/app/ui/sourcedebug/SourceDebugModel.kt b/app/src/main/java/io/legado/app/ui/sourcedebug/SourceDebugModel.kt index e5c800b20..6d5689ad4 100644 --- a/app/src/main/java/io/legado/app/ui/sourcedebug/SourceDebugModel.kt +++ b/app/src/main/java/io/legado/app/ui/sourcedebug/SourceDebugModel.kt @@ -44,4 +44,5 @@ class SourceDebugModel(application: Application) : BaseViewModel(application), S super.onCleared() SourceDebug.cancelDebug(true) } + } diff --git a/app/src/main/java/io/legado/app/ui/widget/PageView.kt b/app/src/main/java/io/legado/app/ui/widget/PageView.kt deleted file mode 100644 index 7844f7e3f..000000000 --- a/app/src/main/java/io/legado/app/ui/widget/PageView.kt +++ /dev/null @@ -1,77 +0,0 @@ -package io.legado.app.ui.widget - -import android.content.Context -import android.graphics.Bitmap -import android.graphics.Canvas -import android.graphics.Rect -import android.graphics.drawable.GradientDrawable -import android.util.AttributeSet -import android.util.Log -import android.view.MotionEvent -import androidx.appcompat.widget.AppCompatTextView -import io.legado.app.utils.screenshot -import kotlin.math.abs - -class PageView(context: Context, attrs: AttributeSet) : AppCompatTextView(context, attrs) { - - private var bitmap: Bitmap? = null - - private var downX: Float = 0.toFloat() - private var offset: Float = 0.toFloat() - - private val srcRect: Rect = Rect() - private val destRect: Rect = Rect() - private val shadowDrawable: GradientDrawable - - init { - val shadowColors = intArrayOf(0x66111111, 0x00000000) - shadowDrawable = GradientDrawable( - GradientDrawable.Orientation.LEFT_RIGHT, shadowColors - ) - shadowDrawable.gradientType = GradientDrawable.LINEAR_GRADIENT - } - - override fun onDraw(canvas: Canvas?) { - canvas?.save() - super.onDraw(canvas) - canvas?.restore() - - - bitmap?.let { - srcRect.set(0, 0, width, height) - destRect.set(-width + offset.toInt(), 0, offset.toInt(), height) - canvas?.drawBitmap(it, srcRect, destRect, null) - addShadow(offset.toInt(), canvas) - } - } - - //添加阴影 - private fun addShadow(left: Int, canvas: Canvas?) { - canvas?.let { - shadowDrawable.setBounds(left, 0, left + 30, height) - shadowDrawable.draw(it) - } - } - - override fun onTouchEvent(event: MotionEvent?): Boolean { - when (event?.action) { - MotionEvent.ACTION_DOWN -> { - bitmap = screenshot() - Log.e("TAG", "bitmap == null: " + (bitmap == null)) - downX = event.x - offset = 0.toFloat() - invalidate() - } - MotionEvent.ACTION_MOVE -> { - offset = abs(event.x - downX) - invalidate() - } - MotionEvent.ACTION_UP -> { - bitmap = null - invalidate() - } - } - - return true - } -} diff --git a/app/src/main/java/io/legado/app/ui/widget/page/DataSource.kt b/app/src/main/java/io/legado/app/ui/widget/page/DataSource.kt new file mode 100644 index 000000000..f4cb075ed --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/widget/page/DataSource.kt @@ -0,0 +1,12 @@ +package io.legado.app.ui.widget.page + +interface DataSource { + + fun getChapterPosition() + + fun getChapter(position: Int): TextChapter + + fun getNextChapter(): TextChapter + + fun getPreviousChapter(): TextChapter +} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/widget/page/PageAnimDelegate.kt b/app/src/main/java/io/legado/app/ui/widget/page/PageAnimDelegate.kt new file mode 100644 index 000000000..eb4301588 --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/widget/page/PageAnimDelegate.kt @@ -0,0 +1,39 @@ +package io.legado.app.ui.widget.page + +import android.widget.Scroller +import androidx.interpolator.view.animation.FastOutLinearInInterpolator + +abstract class PageAnimDelegate(protected val pageView: PageView) { + + protected val scroller: Scroller = Scroller(pageView.context, FastOutLinearInInterpolator()) + + //起始点 + protected var startX: Float = 0.toFloat() + protected var startY: Float = 0.toFloat() + //触碰点 + protected var touchX: Float = 0.toFloat() + protected var touchY: Float = 0.toFloat() + //上一个触碰点 + protected var lastX: Float = 0.toFloat() + protected var lastY: Float = 0.toFloat() + + protected var isRunning = false + protected var isStarted = false + + + fun setStartPoint(x: Float, y: Float) { + startX = x + startY = y + + lastX = startX + lastY = startY + } + + fun setTouchPoint(x: Float, y: Float) { + lastX = touchX + lastY = touchY + + touchX = x + touchY = y + } +} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/widget/page/PageFactory.kt b/app/src/main/java/io/legado/app/ui/widget/page/PageFactory.kt new file mode 100644 index 000000000..f2bebfbf5 --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/widget/page/PageFactory.kt @@ -0,0 +1,11 @@ +package io.legado.app.ui.widget.page + +abstract class PageFactory(protected val dataSource: DataSource) { + + abstract fun pageAt(index: Int): DATA + + abstract fun nextPage(): DATA + + abstract fun previousPage(): DATA + +} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/widget/page/PageView.kt b/app/src/main/java/io/legado/app/ui/widget/page/PageView.kt new file mode 100644 index 000000000..2ff709124 --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/widget/page/PageView.kt @@ -0,0 +1,55 @@ +package io.legado.app.ui.widget.page + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.Rect +import android.graphics.drawable.GradientDrawable +import android.util.AttributeSet +import android.util.Log +import android.view.MotionEvent +import android.view.View +import android.widget.FrameLayout +import androidx.appcompat.widget.AppCompatTextView +import io.legado.app.R +import io.legado.app.utils.screenshot +import kotlin.math.abs + +class PageView(context: Context, attrs: AttributeSet) : FrameLayout(context, attrs) { + + private var bitmap: Bitmap? = null + + private var downX: Float = 0.toFloat() + private var offset: Float = 0.toFloat() + + private val srcRect: Rect = Rect() + private val destRect: Rect = Rect() + private val shadowDrawable: GradientDrawable + + init { + val shadowColors = intArrayOf(0x66111111, 0x00000000) + shadowDrawable = GradientDrawable( + GradientDrawable.Orientation.LEFT_RIGHT, shadowColors + ) + shadowDrawable.gradientType = GradientDrawable.LINEAR_GRADIENT + + inflate(context, R.layout.page_view, this) + } + + override fun onDraw(canvas: Canvas?) { + canvas?.save() + super.onDraw(canvas) + canvas?.restore() + + + } + + override fun onTouchEvent(event: MotionEvent?): Boolean { + + return true + } + + fun setPageFactory(factory: PageFactory<*>){ + + } +} diff --git a/app/src/main/java/io/legado/app/ui/widget/page/TextChapter.kt b/app/src/main/java/io/legado/app/ui/widget/page/TextChapter.kt new file mode 100644 index 000000000..b4414c19b --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/widget/page/TextChapter.kt @@ -0,0 +1,3 @@ +package io.legado.app.ui.widget.page + +data class TextChapter(val position: Int, val pages: List) diff --git a/app/src/main/java/io/legado/app/ui/widget/page/TextPage.kt b/app/src/main/java/io/legado/app/ui/widget/page/TextPage.kt new file mode 100644 index 000000000..186eb757e --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/widget/page/TextPage.kt @@ -0,0 +1,3 @@ +package io.legado.app.ui.widget.page + +data class TextPage(val index: Int, val text: String) diff --git a/app/src/main/java/io/legado/app/ui/widget/page/TextPageFactory.kt b/app/src/main/java/io/legado/app/ui/widget/page/TextPageFactory.kt new file mode 100644 index 000000000..2fd3d951c --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/widget/page/TextPageFactory.kt @@ -0,0 +1,26 @@ +package io.legado.app.ui.widget.page + +class TextPageFactory private constructor(dataSource: DataSource) : PageFactory(dataSource) { + + companion object{ + fun create(dataSource: DataSource): TextPageFactory{ + return TextPageFactory(dataSource) + } + } + + private var index: Int = 0 + + override fun pageAt(index: Int): TextPage { + TODO("todo...") + } + + override fun nextPage(): TextPage { + return TextPage(index.plus(1), "index:$index") + } + + override fun previousPage(): TextPage { + return TextPage(index.minus(1), "index:$index") + } + + +} diff --git a/app/src/main/res/layout/activity_read.xml b/app/src/main/res/layout/activity_read.xml index fc9704d8b..72ce083ea 100644 --- a/app/src/main/res/layout/activity_read.xml +++ b/app/src/main/res/layout/activity_read.xml @@ -4,7 +4,7 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - + + + + + + + + + + + + + + + + \ No newline at end of file