diff --git a/app/build.gradle b/app/build.gradle index 59657b0f8..ac5387bee 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -97,6 +97,9 @@ android { jvmTarget = "1.8" } buildToolsVersion '30.0.3' +// tasks.withType(JavaCompile) { +// options.compilerArgs << "-Xlint:unchecked" +// } } resourcePlaceholders { @@ -149,8 +152,6 @@ dependencies { //lifecycle def lifecycle_version = '2.3.1' - //2.3.0 弃用lifecycle-extensions - //implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version" implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version" //room @@ -159,9 +160,6 @@ dependencies { kapt "androidx.room:room-compiler:$room_version" testImplementation "androidx.room:room-testing:$room_version" - //paging - implementation 'androidx.paging:paging-runtime-ktx:2.1.2' - //liveEventBus implementation 'com.jeremyliao:live-event-bus-x:1.5.7' diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index 11a6dcfcd..4b204b77e 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -3,7 +3,30 @@ * 关注合作公众号 **[小说拾遗]** 获取好看的小说。 * 旧版数据导入教程:先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。 -**2021/05/04** +**2021/05/12** +* 修复预下载bug +* 添加显示未读开关,书架布局中 + +**2021/05/11** +* 修复部分webDav bug +* 修复图片太大时会崩溃的bug +* 修复其它一些bug + +**2021/05/10** +* 修复书签bug +* 修改弃用类ViewPager + +**2021/05/09** +* 网页访问404依然返回body +* 添加书籍批量导出 + +**2021/05/08** +* 预下载章节可调整数目 +* 修复低版本Android使用TTS闪退。 by ag2s20150909 +* 修复WebDav报错 +* 优化翻页动画点击翻页 + +**2021/05/06** * 修复bug * url参数添加重置次数,retry * 修改默认tts, 手动导入 diff --git a/app/src/main/java/io/legado/app/base/BaseActivity.kt b/app/src/main/java/io/legado/app/base/BaseActivity.kt index 4d2707444..8319d077b 100644 --- a/app/src/main/java/io/legado/app/base/BaseActivity.kt +++ b/app/src/main/java/io/legado/app/base/BaseActivity.kt @@ -33,7 +33,8 @@ abstract class BaseActivity( val fullScreen: Boolean = true, private val theme: Theme = Theme.Auto, private val toolBarTheme: Theme = Theme.Auto, - private val transparent: Boolean = false + private val transparent: Boolean = false, + private val imageBg: Boolean = true ) : AppCompatActivity(), CoroutineScope by MainScope() { @@ -173,9 +174,13 @@ abstract class BaseActivity( ATH.applyBackgroundTint(window.decorView) } } - ThemeConfig.getBgImage(this)?.let { - kotlin.runCatching { - window.decorView.background = it + if (imageBg) { + ThemeConfig.getBgImage(this)?.let { + try { + window.decorView.background = it + } catch (e: OutOfMemoryError) { + toastOnUi("Image Bg Out Of Memory") + } } } } diff --git a/app/src/main/java/io/legado/app/base/VMBaseActivity.kt b/app/src/main/java/io/legado/app/base/VMBaseActivity.kt index d86ba3699..3eeb5c91b 100644 --- a/app/src/main/java/io/legado/app/base/VMBaseActivity.kt +++ b/app/src/main/java/io/legado/app/base/VMBaseActivity.kt @@ -7,8 +7,10 @@ import io.legado.app.constant.Theme abstract class VMBaseActivity( fullScreen: Boolean = true, theme: Theme = Theme.Auto, - toolBarTheme: Theme = Theme.Auto -) : BaseActivity(fullScreen, theme, toolBarTheme) { + toolBarTheme: Theme = Theme.Auto, + transparent: Boolean = false, + imageBg: Boolean = true +) : BaseActivity(fullScreen, theme, toolBarTheme, transparent, imageBg) { protected abstract val viewModel: VM diff --git a/app/src/main/java/io/legado/app/constant/PreferKey.kt b/app/src/main/java/io/legado/app/constant/PreferKey.kt index bd0e86fc6..f60b7c361 100644 --- a/app/src/main/java/io/legado/app/constant/PreferKey.kt +++ b/app/src/main/java/io/legado/app/constant/PreferKey.kt @@ -4,6 +4,7 @@ object PreferKey { const val language = "language" const val themeMode = "themeMode" const val userAgent = "userAgent" + const val showUnread = "showUnread" const val hideStatusBar = "hideStatusBar" const val clickActionTL = "clickActionTopLeft" const val clickActionTC = "clickActionTopCenter" diff --git a/app/src/main/java/io/legado/app/data/dao/BookSourceDao.kt b/app/src/main/java/io/legado/app/data/dao/BookSourceDao.kt index 7a40d4cc8..2d2dcff40 100644 --- a/app/src/main/java/io/legado/app/data/dao/BookSourceDao.kt +++ b/app/src/main/java/io/legado/app/data/dao/BookSourceDao.kt @@ -1,7 +1,6 @@ package io.legado.app.data.dao import androidx.lifecycle.LiveData -import androidx.paging.DataSource import androidx.room.* import io.legado.app.data.entities.BookSource @@ -41,9 +40,6 @@ interface BookSourceDao { @Query("select distinct bookSourceGroup from book_sources where enabledExplore = 1 and trim(exploreUrl) <> '' and trim(bookSourceGroup) <> ''") fun liveExploreGroup(): LiveData> - @Query("select * from book_sources where enabledExplore = 1 order by customOrder asc") - fun observeFind(): DataSource.Factory - @Query("select * from book_sources where bookSourceGroup like '%' || :group || '%'") fun getByGroup(group: String): List diff --git a/app/src/main/java/io/legado/app/data/dao/BookmarkDao.kt b/app/src/main/java/io/legado/app/data/dao/BookmarkDao.kt index 16fdff455..633c1187c 100644 --- a/app/src/main/java/io/legado/app/data/dao/BookmarkDao.kt +++ b/app/src/main/java/io/legado/app/data/dao/BookmarkDao.kt @@ -1,6 +1,6 @@ package io.legado.app.data.dao -import androidx.paging.DataSource +import androidx.lifecycle.LiveData import androidx.room.* import io.legado.app.data.entities.Bookmark @@ -16,10 +16,10 @@ interface BookmarkDao { bookUrl: String, bookName: String, bookAuthor: String - ): DataSource.Factory + ): LiveData> @Query("SELECT * FROM bookmarks where bookUrl = :bookUrl and chapterName like '%'||:key||'%' or content like '%'||:key||'%'") - fun liveDataSearch(bookUrl: String, key: String): DataSource.Factory + fun liveDataSearch(bookUrl: String, key: String): LiveData> @Insert(onConflict = OnConflictStrategy.REPLACE) fun insert(vararg bookmark: Bookmark) diff --git a/app/src/main/java/io/legado/app/data/dao/SearchBookDao.kt b/app/src/main/java/io/legado/app/data/dao/SearchBookDao.kt index 9618ad317..8f53d0170 100644 --- a/app/src/main/java/io/legado/app/data/dao/SearchBookDao.kt +++ b/app/src/main/java/io/legado/app/data/dao/SearchBookDao.kt @@ -1,6 +1,5 @@ package io.legado.app.data.dao -import androidx.paging.DataSource import androidx.room.Dao import androidx.room.Insert import androidx.room.OnConflictStrategy @@ -10,12 +9,6 @@ import io.legado.app.data.entities.SearchBook @Dao interface SearchBookDao { - @Query("SELECT * FROM searchBooks") - fun observeAll(): DataSource.Factory - - @Query("SELECT * FROM searchBooks where time >= :time") - fun observeNew(time: Long): DataSource.Factory - @Query("select * from searchBooks where bookUrl = :bookUrl") fun getSearchBook(bookUrl: String): SearchBook? diff --git a/app/src/main/java/io/legado/app/help/AppConfig.kt b/app/src/main/java/io/legado/app/help/AppConfig.kt index a30f5c614..102f9507c 100644 --- a/app/src/main/java/io/legado/app/help/AppConfig.kt +++ b/app/src/main/java/io/legado/app/help/AppConfig.kt @@ -72,6 +72,12 @@ object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener { } } + var showUnread: Boolean + get() = appCtx.getPrefBoolean(PreferKey.showUnread, true) + set(value) { + appCtx.putPrefBoolean(PreferKey.showUnread, value) + } + val isTransparentStatusBar: Boolean get() = appCtx.getPrefBoolean(PreferKey.transparentStatusBar, true) diff --git a/app/src/main/java/io/legado/app/help/http/OkHttpUtils.kt b/app/src/main/java/io/legado/app/help/http/OkHttpUtils.kt index 4dec4bf3e..2c8206123 100644 --- a/app/src/main/java/io/legado/app/help/http/OkHttpUtils.kt +++ b/app/src/main/java/io/legado/app/help/http/OkHttpUtils.kt @@ -28,7 +28,7 @@ suspend fun OkHttpClient.newCall( return response.body!! } } - throw IOException(response!!.message) + return response!!.body ?: throw IOException(response.message) } suspend fun OkHttpClient.newCallStrResponse( @@ -45,7 +45,7 @@ suspend fun OkHttpClient.newCallStrResponse( return StrResponse(response, response.body!!.text()) } } - throw IOException(response!!.message) + return StrResponse(response!!, response.body?.text() ?: response.message) } suspend fun Call.await(): Response = suspendCancellableCoroutine { block -> diff --git a/app/src/main/java/io/legado/app/lib/theme/ATH.kt b/app/src/main/java/io/legado/app/lib/theme/ATH.kt index cbe1c2ebb..dcecc9577 100644 --- a/app/src/main/java/io/legado/app/lib/theme/ATH.kt +++ b/app/src/main/java/io/legado/app/lib/theme/ATH.kt @@ -16,7 +16,7 @@ import android.widget.ScrollView import androidx.annotation.ColorInt import androidx.appcompat.app.AlertDialog import androidx.recyclerview.widget.RecyclerView -import androidx.viewpager.widget.ViewPager +import androidx.viewpager2.widget.ViewPager2 import com.google.android.material.bottomnavigation.BottomNavigationView import io.legado.app.R import io.legado.app.help.AppConfig @@ -219,9 +219,9 @@ object ATH { } } - fun setEdgeEffectColor(viewPager: ViewPager?, @ColorInt color: Int) { + fun setEdgeEffectColor(viewPager: ViewPager2?, @ColorInt color: Int) { try { - val clazz = ViewPager::class.java + val clazz = ViewPager2::class.java for (name in arrayOf("mLeftEdge", "mRightEdge")) { val field = clazz.getDeclaredField(name) field.isAccessible = true @@ -280,7 +280,7 @@ object ATH { fun applyEdgeEffectColor(view: View?) { when (view) { is RecyclerView -> view.edgeEffectFactory = DEFAULT_EFFECT_FACTORY - is ViewPager -> setEdgeEffectColor(view, ThemeStore.primaryColor(view.context)) + is ViewPager2 -> setEdgeEffectColor(view, ThemeStore.primaryColor(view.context)) is ScrollView -> setEdgeEffectColor(view, ThemeStore.primaryColor(view.context)) } } diff --git a/app/src/main/java/io/legado/app/lib/webdav/WebDav.kt b/app/src/main/java/io/legado/app/lib/webdav/WebDav.kt index 36713320f..389c273a0 100644 --- a/app/src/main/java/io/legado/app/lib/webdav/WebDav.kt +++ b/app/src/main/java/io/legado/app/lib/webdav/WebDav.kt @@ -9,7 +9,6 @@ import okhttp3.RequestBody.Companion.asRequestBody import okhttp3.RequestBody.Companion.toRequestBody import org.jsoup.Jsoup import java.io.File -import java.io.IOException import java.io.InputStream import java.net.MalformedURLException import java.net.URL @@ -58,28 +57,25 @@ class WebDav(urlStr: String) { * @return 远程文件是否存在 */ suspend fun indexFileInfo(): Boolean { - propFindResponse(ArrayList())?.let { - if (it.text().isNotEmpty()) { - return true - } - } - return false + return !propFindResponse(ArrayList()).isNullOrEmpty() } /** * 列出当前路径下的文件 * - * @param propsList 指定列出文件的哪些属性 * @return 文件列表 */ - suspend fun listFiles(propsList: ArrayList = ArrayList()): List { - propFindResponse(propsList)?.text()?.let { body -> + suspend fun listFiles(): List { + propFindResponse()?.let { body -> return parseDir(body) } return ArrayList() } - private suspend fun propFindResponse(propsList: ArrayList): ResponseBody? { + /** + * @param propsList 指定列出文件的哪些属性 + */ + private suspend fun propFindResponse(propsList: List = emptyList()): String? { val requestProps = StringBuilder() for (p in propsList) { requestProps.append("\n") @@ -96,11 +92,12 @@ class WebDav(urlStr: String) { okHttpClient.newCall { url(url) addHeader("Authorization", Credentials.basic(auth.user, auth.pass)) + addHeader("Depth", "1") // 添加RequestBody对象,可以只返回的属性。如果设为null,则会返回全部属性 // 注意:尽量手动指定需要返回的属性。若返回全部属性,可能后由于Prop.java里没有该属性名,而崩溃。 val requestBody = requestPropsStr.toRequestBody("text/plain".toMediaType()) method("PROPFIND", requestBody) - } + }.text() }.onFailure { it.printStackTrace() }.getOrNull() @@ -144,19 +141,20 @@ class WebDav(urlStr: String) { /** * 根据自己的URL,在远程处创建对应的文件夹 - * * @return 是否创建成功 */ suspend fun makeAsDir(): Boolean { val url = httpUrl val auth = HttpAuth.auth if (url != null && auth != null) { - okHttpClient.newCall { - url(url) - method("MKCOL", null) - addHeader("Authorization", Credentials.basic(auth.user, auth.pass)) - } - return true + //防止报错 + return kotlin.runCatching { + okHttpClient.newCall { + url(url) + method("MKCOL", null) + addHeader("Authorization", Credentials.basic(auth.user, auth.pass)) + }.close() + }.isSuccess } return false } @@ -193,12 +191,13 @@ class WebDav(urlStr: String) { val url = httpUrl val auth = HttpAuth.auth if (url != null && auth != null) { - okHttpClient.newCall { - url(url) - put(fileBody) - addHeader("Authorization", Credentials.basic(auth.user, auth.pass)) - } - return true + return kotlin.runCatching { + okHttpClient.newCall { + url(url) + put(fileBody) + addHeader("Authorization", Credentials.basic(auth.user, auth.pass)) + }.close() + }.isSuccess } return false } @@ -209,25 +208,27 @@ class WebDav(urlStr: String) { val url = httpUrl val auth = HttpAuth.auth if (url != null && auth != null) { - okHttpClient.newCall { - url(url) - put(fileBody) - addHeader("Authorization", Credentials.basic(auth.user, auth.pass)) - } - return true + return kotlin.runCatching { + okHttpClient.newCall { + url(url) + put(fileBody) + addHeader("Authorization", Credentials.basic(auth.user, auth.pass)) + }.close() + }.isSuccess } return false } - @Throws(IOException::class) private suspend fun getInputStream(): InputStream? { val url = httpUrl val auth = HttpAuth.auth if (url != null && auth != null) { - return okHttpClient.newCall { - url(url) - addHeader("Authorization", Credentials.basic(auth.user, auth.pass)) - }.byteStream() + return kotlin.runCatching { + okHttpClient.newCall { + url(url) + addHeader("Authorization", Credentials.basic(auth.user, auth.pass)) + }.byteStream() + }.getOrNull() } return null } diff --git a/app/src/main/java/io/legado/app/model/analyzeRule/QueryTTF.java b/app/src/main/java/io/legado/app/model/analyzeRule/QueryTTF.java index 041a6539b..7fb875627 100644 --- a/app/src/main/java/io/legado/app/model/analyzeRule/QueryTTF.java +++ b/app/src/main/java/io/legado/app/model/analyzeRule/QueryTTF.java @@ -11,7 +11,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; -@SuppressWarnings({"FieldCanBeLocal", "rawtypes", "StatementWithEmptyBody", "unused"}) +@SuppressWarnings({"FieldCanBeLocal", "StatementWithEmptyBody", "unused"}) public class QueryTTF { private static class Header { public int majorVersion; @@ -223,7 +223,8 @@ public class QueryTTF { private final List loca = new LinkedList<>(); private final CmapLayout Cmap = new CmapLayout(); private final List glyf = new LinkedList<>(); - private final Pair[] pps = new Pair[]{ + @SuppressWarnings("unchecked") + private final Pair[] pps = new Pair[]{ Pair.of(3, 10), Pair.of(0, 4), Pair.of(3, 1), @@ -515,7 +516,7 @@ public class QueryTTF { private int getGlyfIndex(int code) { if (code == 0) return 0; int fmtKey = 0; - for (@SuppressWarnings("unchecked") Pair item : pps) { + for (Pair item : pps) { for (CmapRecord record : Cmap.records) { if ((item.getLeft() == record.platformID) && (item.getRight() == record.encodingID)) { fmtKey = record.offset; diff --git a/app/src/main/java/io/legado/app/service/help/ReadBook.kt b/app/src/main/java/io/legado/app/service/help/ReadBook.kt index 88e7e6fa3..31adc1e33 100644 --- a/app/src/main/java/io/legado/app/service/help/ReadBook.kt +++ b/app/src/main/java/io/legado/app/service/help/ReadBook.kt @@ -21,6 +21,7 @@ import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.delay import kotlinx.coroutines.launch import splitties.init.appCtx +import kotlin.math.max import kotlin.math.min @@ -141,9 +142,9 @@ object ReadBook { GlobalScope.launch(Dispatchers.IO) { val maxChapterIndex = min(chapterSize - 1, durChapterIndex + AppConfig.preDownloadNum) - for (i in 2..maxChapterIndex) { + for (i in durChapterIndex.plus(2)..maxChapterIndex) { delay(1000) - download(durChapterIndex + i) + download(i) } } } @@ -171,11 +172,10 @@ object ReadBook { } loadContent(durChapterIndex.minus(1), upContent, false) GlobalScope.launch(Dispatchers.IO) { - val maxChapterIndex = - min(chapterSize - 1, durChapterIndex + AppConfig.preDownloadNum) - for (i in 2..maxChapterIndex) { + val minChapterIndex = max(0, durChapterIndex - 5) + for (i in durChapterIndex.minus(2) downTo minChapterIndex) { delay(1000) - download(durChapterIndex - i) + download(i) } } } diff --git a/app/src/main/java/io/legado/app/ui/book/cache/CacheActivity.kt b/app/src/main/java/io/legado/app/ui/book/cache/CacheActivity.kt index fbdae644b..8b95ed50d 100644 --- a/app/src/main/java/io/legado/app/ui/book/cache/CacheActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/cache/CacheActivity.kt @@ -115,9 +115,13 @@ class CacheActivity : VMBaseActivity() CacheBook.stop(this@CacheActivity) } } + R.id.menu_export_all -> exportAll() R.id.menu_enable_replace -> AppConfig.exportUseReplace = !item.isChecked R.id.menu_export_web_dav -> AppConfig.exportToWebDav = !item.isChecked - R.id.menu_export_folder -> export(-1) + R.id.menu_export_folder -> { + exportPosition = -1 + selectExportFolder() + } R.id.menu_export_type -> showExportTypeConfig() R.id.menu_export_charset -> showCharsetConfig() R.id.menu_log -> @@ -212,7 +216,17 @@ class CacheActivity : VMBaseActivity() override fun export(position: Int) { exportPosition = position val path = ACache.get(this@CacheActivity).getAsString(exportBookPathKey) - if (path.isNullOrEmpty() || position < 0) { + if (path.isNullOrEmpty()) { + selectExportFolder() + } else { + startExport(path) + } + } + + private fun exportAll() { + exportPosition = -10 + val path = ACache.get(this@CacheActivity).getAsString(exportBookPathKey) + if (path.isNullOrEmpty()) { selectExportFolder() } else { startExport(path) @@ -233,15 +247,43 @@ class CacheActivity : VMBaseActivity() } private fun startExport(path: String) { - adapter.getItem(exportPosition)?.let { book -> - Snackbar.make(binding.titleBar, R.string.exporting, Snackbar.LENGTH_INDEFINITE) - .show() - when (AppConfig.exportType) { - 1 -> viewModel.exportEPUB(path, book) { - binding.titleBar.snackbar(it) + if (exportPosition == -10) { + if (adapter.getItems().isNotEmpty()) { + Snackbar.make(binding.titleBar, R.string.exporting, Snackbar.LENGTH_INDEFINITE) + .show() + var exportSize = adapter.getItems().size + adapter.getItems().forEach { book -> + when (AppConfig.exportType) { + 1 -> viewModel.exportEPUB(path, book) { + exportSize-- + toastOnUi(it) + if (exportSize <= 0) { + binding.titleBar.snackbar(R.string.complete) + } + } + else -> viewModel.export(path, book) { + exportSize-- + toastOnUi(it) + if (exportSize <= 0) { + binding.titleBar.snackbar(R.string.complete) + } + } + } } - else -> viewModel.export(path, book) { - binding.titleBar.snackbar(it) + } else { + toastOnUi(R.string.no_book) + } + } else if (exportPosition >= 0) { + adapter.getItem(exportPosition)?.let { book -> + Snackbar.make(binding.titleBar, R.string.exporting, Snackbar.LENGTH_INDEFINITE) + .show() + when (AppConfig.exportType) { + 1 -> viewModel.exportEPUB(path, book) { + binding.titleBar.snackbar(it) + } + else -> viewModel.export(path, book) { + binding.titleBar.snackbar(it) + } } } } diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookBaseActivity.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookBaseActivity.kt index 843995702..e2d10bf9b 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookBaseActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookBaseActivity.kt @@ -39,7 +39,7 @@ import io.legado.app.utils.requestInputMethod * 阅读界面 */ abstract class ReadBookBaseActivity : - VMBaseActivity() { + VMBaseActivity(imageBg = false) { override val viewModel: ReadBookViewModel by viewModels() diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt b/app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt index 314051adc..1be7ce084 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt @@ -23,6 +23,7 @@ import io.legado.app.ui.widget.dialog.PhotoDialog import io.legado.app.utils.activity import io.legado.app.utils.getCompatColor import io.legado.app.utils.getPrefBoolean +import io.legado.app.utils.toastOnUi import kotlinx.coroutines.CoroutineScope import kotlin.math.min @@ -178,7 +179,11 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at val div = (lineBottom - lineTop - h) / 2 RectF(textChar.start, lineTop + div, textChar.end, lineBottom - div) } - canvas.drawBitmap(it, null, rectF, null) + kotlin.runCatching { + canvas.drawBitmap(it, null, rectF, null) + }.onFailure { e -> + context.toastOnUi(e.localizedMessage) + } } } } diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt b/app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt index fa14fbcf2..7b621735f 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt @@ -237,15 +237,6 @@ class PageView(context: Context) : FrameLayout(context) { fun scroll(offset: Int) { binding.contentTextView.scroll(offset) -// if (offset > 0) { -// for (i in 1..offset) { -// binding.contentTextView.scroll(1) -// } -// } else { -// for (i in offset..-1) { -// binding.contentTextView.scroll(-1) -// } -// } } fun upSelectAble(selectAble: Boolean) { diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/ReadView.kt b/app/src/main/java/io/legado/app/ui/book/read/page/ReadView.kt index bba292427..42886de3c 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/ReadView.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/ReadView.kt @@ -102,8 +102,8 @@ class ReadView(context: Context, attrs: AttributeSet) : setRect9x() } - public fun setRect9x() { - val edge = if (AppConfig.fullScreenGesturesSupport) { 200f } else { 0f } + fun setRect9x() { + val edge = if (AppConfig.fullScreenGesturesSupport) 200f else 0f tlRect.set(0f + edge, 0f, width * 0.33f, height * 0.33f) tcRect.set(width * 0.33f, 0f, width * 0.66f, height * 0.33f) trRect.set(width * 0.36f, 0f, width - 0f - edge, height * 0.33f) diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/HorizontalPageDelegate.kt b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/HorizontalPageDelegate.kt index 11b469af1..3f8c29ca3 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/HorizontalPageDelegate.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/HorizontalPageDelegate.kt @@ -119,7 +119,7 @@ abstract class HorizontalPageDelegate(readView: ReadView) : PageDelegate(readVie abortAnim() if (!hasNext()) return setDirection(PageDirection.NEXT) - readView.setTouchPoint(viewWidth.toFloat(), 0f, false) + readView.setStartPoint(viewWidth.toFloat(), 0f, false) onAnimStart(animationSpeed) } @@ -127,7 +127,7 @@ abstract class HorizontalPageDelegate(readView: ReadView) : PageDelegate(readVie abortAnim() if (!hasPrev()) return setDirection(PageDirection.PREV) - readView.setTouchPoint(0f, 0f) + readView.setStartPoint(0f, 0f, false) onAnimStart(animationSpeed) } diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/PageDelegate.kt b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/PageDelegate.kt index a70961487..2b417240c 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/PageDelegate.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/PageDelegate.kt @@ -3,7 +3,7 @@ package io.legado.app.ui.book.read.page.delegate import android.content.Context import android.graphics.Canvas import android.view.MotionEvent -import android.view.animation.DecelerateInterpolator +import android.view.animation.LinearInterpolator import android.widget.Scroller import androidx.annotation.CallSuper import com.google.android.material.snackbar.Snackbar @@ -37,7 +37,7 @@ abstract class PageDelegate(protected val readView: ReadView) { protected var viewHeight: Int = readView.height protected val scroller: Scroller by lazy { - Scroller(readView.context, DecelerateInterpolator()) + Scroller(readView.context, LinearInterpolator()) } private val snackBar: Snackbar by lazy { diff --git a/app/src/main/java/io/legado/app/ui/book/toc/BookmarkAdapter.kt b/app/src/main/java/io/legado/app/ui/book/toc/BookmarkAdapter.kt index 8300aa3da..43a6ac72d 100644 --- a/app/src/main/java/io/legado/app/ui/book/toc/BookmarkAdapter.kt +++ b/app/src/main/java/io/legado/app/ui/book/toc/BookmarkAdapter.kt @@ -1,60 +1,48 @@ package io.legado.app.ui.book.toc -import android.view.LayoutInflater +import android.content.Context import android.view.ViewGroup -import androidx.paging.PagedListAdapter -import androidx.recyclerview.widget.DiffUtil -import androidx.recyclerview.widget.RecyclerView +import io.legado.app.base.adapter.ItemViewHolder +import io.legado.app.base.adapter.RecyclerAdapter import io.legado.app.data.entities.Bookmark import io.legado.app.databinding.ItemBookmarkBinding import splitties.views.onLongClick -class BookmarkAdapter(val callback: Callback) : PagedListAdapter(DIFF_CALLBACK) { +class BookmarkAdapter(context: Context, val callback: Callback) : + RecyclerAdapter(context) { - companion object { - - @JvmField - val DIFF_CALLBACK = object : DiffUtil.ItemCallback() { - override fun areItemsTheSame(oldItem: Bookmark, newItem: Bookmark): Boolean = - oldItem.time == newItem.time - - override fun areContentsTheSame(oldItem: Bookmark, newItem: Bookmark): Boolean = - oldItem.time == newItem.time - && oldItem.bookUrl == newItem.bookUrl - && oldItem.chapterName == newItem.chapterName - && oldItem.content == newItem.content - } - } - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { - val binding = - ItemBookmarkBinding.inflate(LayoutInflater.from(parent.context), parent, false) - return MyViewHolder(binding) + override fun getViewBinding(parent: ViewGroup): ItemBookmarkBinding { + return ItemBookmarkBinding.inflate(inflater, parent, false) } - override fun onBindViewHolder(holder: MyViewHolder, position: Int) { - getItem(position)?.let { - holder.bind(it, callback) - } + override fun convert( + holder: ItemViewHolder, + binding: ItemBookmarkBinding, + item: Bookmark, + payloads: MutableList + ) { + binding.tvChapterName.text = item.chapterName + binding.tvBookText.text = item.bookText + binding.tvContent.text = item.content } - class MyViewHolder(val binding: ItemBookmarkBinding) : RecyclerView.ViewHolder(binding.root) { - - fun bind(bookmark: Bookmark, callback: Callback?) = with(binding) { - tvChapterName.text = bookmark.chapterName - tvBookText.text = bookmark.bookText - tvContent.text = bookmark.content - itemView.setOnClickListener { - callback?.onClick(bookmark) + override fun registerListener(holder: ItemViewHolder, binding: ItemBookmarkBinding) { + binding.root.setOnClickListener { + getItem(holder.layoutPosition)?.let { bookmark -> + callback.onClick(bookmark) } - itemView.onLongClick { - callback?.onLongClick(bookmark) + } + binding.root.onLongClick { + getItem(holder.layoutPosition)?.let { bookmark -> + callback.onLongClick(bookmark) } } + } interface Callback { fun onClick(bookmark: Bookmark) fun onLongClick(bookmark: Bookmark) } + } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/book/toc/BookmarkFragment.kt b/app/src/main/java/io/legado/app/ui/book/toc/BookmarkFragment.kt index 73235d31a..976436e56 100644 --- a/app/src/main/java/io/legado/app/ui/book/toc/BookmarkFragment.kt +++ b/app/src/main/java/io/legado/app/ui/book/toc/BookmarkFragment.kt @@ -1,14 +1,11 @@ package io.legado.app.ui.book.toc -import android.annotation.SuppressLint import android.app.Activity import android.content.Intent import android.os.Bundle import android.view.View import androidx.fragment.app.activityViewModels import androidx.lifecycle.LiveData -import androidx.paging.LivePagedListBuilder -import androidx.paging.PagedList import androidx.recyclerview.widget.LinearLayoutManager import io.legado.app.R import io.legado.app.base.VMBaseFragment @@ -31,7 +28,7 @@ class BookmarkFragment : VMBaseFragment(R.layout.fragment_ override val viewModel: ChapterListViewModel by activityViewModels() private val binding by viewBinding(FragmentBookmarkBinding::bind) private lateinit var adapter: BookmarkAdapter - private var bookmarkLiveData: LiveData>? = null + private var bookmarkLiveData: LiveData>? = null override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) { viewModel.bookMarkCallBack = this @@ -43,7 +40,7 @@ class BookmarkFragment : VMBaseFragment(R.layout.fragment_ private fun initRecyclerView() { ATH.applyEdgeEffectColor(binding.recyclerView) - adapter = BookmarkAdapter(this) + adapter = BookmarkAdapter(requireContext(), this) binding.recyclerView.layoutManager = LinearLayoutManager(requireContext()) binding.recyclerView.addItemDecoration(VerticalDivider(requireContext())) binding.recyclerView.adapter = adapter @@ -51,11 +48,8 @@ class BookmarkFragment : VMBaseFragment(R.layout.fragment_ private fun initData(book: Book) { bookmarkLiveData?.removeObservers(viewLifecycleOwner) - bookmarkLiveData = - LivePagedListBuilder( - appDb.bookmarkDao.observeByBook(book.bookUrl, book.name, book.author), 20 - ).build() - bookmarkLiveData?.observe(viewLifecycleOwner, { adapter.submitList(it) }) + bookmarkLiveData = appDb.bookmarkDao.observeByBook(book.bookUrl, book.name, book.author) + bookmarkLiveData?.observe(viewLifecycleOwner, { adapter.setItems(it) }) } override fun startBookmarkSearch(newText: String?) { @@ -65,13 +59,8 @@ class BookmarkFragment : VMBaseFragment(R.layout.fragment_ } } else { bookmarkLiveData?.removeObservers(viewLifecycleOwner) - bookmarkLiveData = LivePagedListBuilder( - appDb.bookmarkDao.liveDataSearch( - viewModel.bookUrl, - newText - ), 20 - ).build() - bookmarkLiveData?.observe(viewLifecycleOwner, { adapter.submitList(it) }) + bookmarkLiveData = appDb.bookmarkDao.liveDataSearch(viewModel.bookUrl, newText) + bookmarkLiveData?.observe(viewLifecycleOwner, { adapter.setItems(it) }) } } @@ -84,7 +73,6 @@ class BookmarkFragment : VMBaseFragment(R.layout.fragment_ activity?.finish() } - @SuppressLint("InflateParams") override fun onLongClick(bookmark: Bookmark) { requireContext().alert(R.string.bookmark) { setMessage(bookmark.chapterName) 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 55a1498f4..65ca55b42 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 @@ -3,12 +3,10 @@ package io.legado.app.ui.main import android.os.Bundle import android.view.KeyEvent import android.view.MenuItem -import android.view.ViewGroup import androidx.activity.viewModels import androidx.fragment.app.Fragment -import androidx.fragment.app.FragmentManager -import androidx.fragment.app.FragmentStatePagerAdapter -import androidx.viewpager.widget.ViewPager +import androidx.viewpager2.adapter.FragmentStateAdapter +import androidx.viewpager2.widget.ViewPager2 import com.google.android.material.bottomnavigation.BottomNavigationView import io.legado.app.BuildConfig import io.legado.app.R @@ -36,30 +34,43 @@ import io.legado.app.utils.toastOnUi class MainActivity : VMBaseActivity(), BottomNavigationView.OnNavigationItemSelectedListener, - BottomNavigationView.OnNavigationItemReselectedListener, - ViewPager.OnPageChangeListener by ViewPager.SimpleOnPageChangeListener() { + BottomNavigationView.OnNavigationItemReselectedListener { override val viewModel: MainViewModel by viewModels() private var exitTime: Long = 0 private var bookshelfReselected: Long = 0 private var exploreReselected: Long = 0 private var pagePosition = 0 - private val fragmentMap = hashMapOf() + private val fragmentMap = hashMapOf() override fun getViewBinding(): ActivityMainBinding { return ActivityMainBinding.inflate(layoutInflater) } - override fun onActivityCreated(savedInstanceState: Bundle?) = with(binding) { - ATH.applyEdgeEffectColor(viewPagerMain) - ATH.applyBottomNavigationColor(bottomNavigationView) - viewPagerMain.offscreenPageLimit = 3 - viewPagerMain.adapter = TabFragmentPageAdapter(supportFragmentManager) - viewPagerMain.addOnPageChangeListener(this@MainActivity) - bottomNavigationView.elevation = - if (AppConfig.elevation < 0) elevation else AppConfig.elevation.toFloat() - bottomNavigationView.setOnNavigationItemSelectedListener(this@MainActivity) - bottomNavigationView.setOnNavigationItemReselectedListener(this@MainActivity) - bottomNavigationView.menu.findItem(R.id.menu_rss).isVisible = AppConfig.isShowRSS + override fun onActivityCreated(savedInstanceState: Bundle?) { + binding.apply { + ATH.applyEdgeEffectColor(viewPagerMain) + ATH.applyBottomNavigationColor(bottomNavigationView) + viewPagerMain.offscreenPageLimit = 3 + viewPagerMain.adapter = TabFragmentPageAdapter() + viewPagerMain.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { + override fun onPageSelected(position: Int) { + pagePosition = position + when (position) { + 0, 1, 3 -> bottomNavigationView.menu.getItem(position).isChecked = true + 2 -> if (AppConfig.isShowRSS) { + bottomNavigationView.menu.getItem(position).isChecked = true + } else { + bottomNavigationView.menu.getItem(3).isChecked = true + } + } + } + }) + bottomNavigationView.elevation = + if (AppConfig.elevation < 0) elevation else AppConfig.elevation.toFloat() + bottomNavigationView.setOnNavigationItemSelectedListener(this@MainActivity) + bottomNavigationView.setOnNavigationItemReselectedListener(this@MainActivity) + bottomNavigationView.menu.findItem(R.id.menu_rss).isVisible = AppConfig.isShowRSS + } } override fun onPostCreate(savedInstanceState: Bundle?) { @@ -105,6 +116,10 @@ class MainActivity : VMBaseActivity(), } } + fun getViewPager(): ViewPager2 { + return binding.viewPagerMain + } + private fun upVersion() { if (LocalConfig.versionCode != appInfo.versionCode) { LocalConfig.versionCode = appInfo.versionCode @@ -120,18 +135,6 @@ class MainActivity : VMBaseActivity(), } } - override fun onPageSelected(position: Int) = with(binding) { - pagePosition = position - when (position) { - 0, 1, 3 -> bottomNavigationView.menu.getItem(position).isChecked = true - 2 -> if (AppConfig.isShowRSS) { - bottomNavigationView.menu.getItem(position).isChecked = true - } else { - bottomNavigationView.menu.getItem(3).isChecked = true - } - } - } - override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean { event?.let { when (keyCode) { @@ -186,36 +189,33 @@ class MainActivity : VMBaseActivity(), } } - private inner class TabFragmentPageAdapter(fm: FragmentManager) : - FragmentStatePagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) { + private inner class TabFragmentPageAdapter : + FragmentStateAdapter(this) { - private fun getId(position: Int): Int { + override fun getItemId(position: Int): Long { return when (position) { 2 -> if (AppConfig.isShowRSS) 2 else 3 - else -> position + else -> position.toLong() } } - override fun getItemPosition(`object`: Any): Int { - return POSITION_NONE - } - - override fun getItem(position: Int): Fragment { - return when (getId(position)) { - 0 -> BookshelfFragment() - 1 -> ExploreFragment() - 2 -> RssFragment() - else -> MyFragment() - } + override fun containsItem(itemId: Long): Boolean { + return fragmentMap.containsKey(itemId) } - override fun getCount(): Int { + override fun getItemCount(): Int { return if (AppConfig.isShowRSS) 4 else 3 } - override fun instantiateItem(container: ViewGroup, position: Int): Any { - val fragment = super.instantiateItem(container, position) as Fragment - fragmentMap[getId(position)] = fragment + override fun createFragment(position: Int): Fragment { + val id = getItemId(position) + val fragment = when (id) { + 0L -> BookshelfFragment() + 1L -> ExploreFragment() + 2L -> RssFragment() + else -> MyFragment() + } + fragmentMap[id] = fragment return fragment } diff --git a/app/src/main/java/io/legado/app/ui/main/MainViewModel.kt b/app/src/main/java/io/legado/app/ui/main/MainViewModel.kt index 48afc90ce..692eb90f0 100644 --- a/app/src/main/java/io/legado/app/ui/main/MainViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/main/MainViewModel.kt @@ -119,11 +119,11 @@ class MainViewModel(application: Application) : BaseViewModel(application) { if (!BookHelp.hasContent(book, chapter)) { var addToCache = false while (!addToCache) { - if (CacheBook.downloadCount() < 5) { + if (CacheBook.downloadCount() < 10) { CacheBook.download(this, webBook, book, chapter) addToCache = true } else { - delay(1000) + delay(100) } } } diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt index 090edd420..4a3eab128 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt @@ -5,11 +5,14 @@ import android.os.Bundle import android.view.Menu import android.view.MenuItem import android.view.View -import android.view.ViewGroup import androidx.appcompat.widget.SearchView -import androidx.fragment.app.* +import androidx.fragment.app.Fragment +import androidx.fragment.app.activityViewModels +import androidx.fragment.app.viewModels import androidx.lifecycle.LiveData +import androidx.viewpager2.adapter.FragmentStateAdapter import com.google.android.material.tabs.TabLayout +import com.google.android.material.tabs.TabLayoutMediator import io.legado.app.R import io.legado.app.base.VMBaseFragment import io.legado.app.constant.AppConst @@ -19,6 +22,7 @@ import io.legado.app.data.entities.BookGroup import io.legado.app.databinding.DialogBookshelfConfigBinding import io.legado.app.databinding.DialogEditTextBinding import io.legado.app.databinding.FragmentBookshelfBinding +import io.legado.app.help.AppConfig import io.legado.app.lib.dialogs.alert import io.legado.app.lib.theme.ATH import io.legado.app.lib.theme.accentColor @@ -44,7 +48,7 @@ class BookshelfFragment : VMBaseFragment(R.layout.fragment_b private val binding by viewBinding(FragmentBookshelfBinding::bind) override val viewModel: BookshelfViewModel by viewModels() private val activityViewModel: MainViewModel by activityViewModels() - private lateinit var adapter: FragmentStatePagerAdapter + private lateinit var adapter: FragmentStateAdapter private lateinit var tabLayout: TabLayout private var bookGroupLiveData: LiveData>? = null private val bookGroups = mutableListOf() @@ -107,10 +111,12 @@ class BookshelfFragment : VMBaseFragment(R.layout.fragment_b tabLayout.isTabIndicatorFullWidth = false tabLayout.tabMode = TabLayout.MODE_SCROLLABLE tabLayout.setSelectedTabIndicatorColor(requireContext().accentColor) - tabLayout.setupWithViewPager(binding.viewPagerBookshelf) binding.viewPagerBookshelf.offscreenPageLimit = 1 - adapter = TabFragmentPageAdapter(childFragmentManager) + adapter = TabFragmentPageAdapter() binding.viewPagerBookshelf.adapter = adapter + TabLayoutMediator(tabLayout, binding.viewPagerBookshelf) { tab, i -> + tab.text = bookGroups[i].groupName + }.attach() } private fun initBookGroupData() { @@ -165,6 +171,7 @@ class BookshelfFragment : VMBaseFragment(R.layout.fragment_b .apply { rgLayout.checkByIndex(bookshelfLayout) rgSort.checkByIndex(bookshelfSort) + swShowUnread.isChecked = AppConfig.showUnread } customView { alertBinding.root } okButton { @@ -178,6 +185,10 @@ class BookshelfFragment : VMBaseFragment(R.layout.fragment_b putPrefInt(PreferKey.bookshelfSort, rgSort.getCheckedIndex()) changed = true } + if (AppConfig.showUnread != swShowUnread.isChecked) { + AppConfig.showUnread = swShowUnread.isChecked + changed = true + } if (changed) { activity?.recreate() } @@ -240,29 +251,25 @@ class BookshelfFragment : VMBaseFragment(R.layout.fragment_b }.show() } - private inner class TabFragmentPageAdapter(fm: FragmentManager) : - FragmentStatePagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) { - - override fun getPageTitle(position: Int): CharSequence { - return bookGroups[position].groupName - } + private inner class TabFragmentPageAdapter : + FragmentStateAdapter(this) { - override fun getItemPosition(`object`: Any): Int { - return POSITION_NONE + override fun getItemId(position: Int): Long { + val group = bookGroups[position] + return group.groupId } - override fun getItem(position: Int): Fragment { - val group = bookGroups[position] - return BooksFragment.newInstance(position, group.groupId) + override fun containsItem(itemId: Long): Boolean { + return fragmentMap.containsKey(itemId) } - override fun getCount(): Int { + override fun getItemCount(): Int { return bookGroups.size } - override fun instantiateItem(container: ViewGroup, position: Int): Any { - val fragment = super.instantiateItem(container, position) as BooksFragment + override fun createFragment(position: Int): Fragment { val group = bookGroups[position] + val fragment = BooksFragment.newInstance(position, group.groupId) fragmentMap[group.groupId] = fragment return fragment } diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/RootView.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/RootView.kt new file mode 100644 index 000000000..4162e6e6e --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/RootView.kt @@ -0,0 +1,82 @@ +package io.legado.app.ui.main.bookshelf + +import android.content.Context +import android.gesture.GestureOverlayView.ORIENTATION_HORIZONTAL +import android.util.AttributeSet +import android.view.MotionEvent +import android.view.View +import android.view.ViewConfiguration +import android.widget.LinearLayout +import androidx.viewpager2.widget.ViewPager2 +import io.legado.app.R +import io.legado.app.ui.main.MainActivity +import io.legado.app.utils.activity +import kotlin.math.absoluteValue +import kotlin.math.sign + +class RootView : LinearLayout { + + constructor(context: Context) : super(context) + constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) + + private val touchSlop = ViewConfiguration.get(context).scaledTouchSlop + private var initialX = 0f + private var initialY = 0f + + private val parentViewPager: ViewPager2? + get() = (activity as? MainActivity)?.getViewPager() + private val childViewPager: View? + get() = findViewById(R.id.view_pager_bookshelf) + + private fun canChildScroll(orientation: Int, delta: Float): Boolean { + val direction = -delta.sign.toInt() + return when (orientation) { + 0 -> childViewPager?.canScrollHorizontally(direction) ?: false + 1 -> childViewPager?.canScrollVertically(direction) ?: false + else -> throw IllegalArgumentException() + } + } + + override fun onInterceptTouchEvent(e: MotionEvent): Boolean { + handleInterceptTouchEvent(e) + return super.onInterceptTouchEvent(e) + } + + private fun handleInterceptTouchEvent(e: MotionEvent) { + val orientation = parentViewPager?.orientation ?: return + // Early return if child can't scroll in same direction as parent + if (!canChildScroll(orientation, -1f) && !canChildScroll(orientation, 1f)) { + return + } + + if (e.action == MotionEvent.ACTION_DOWN) { + initialX = e.x + initialY = e.y + parent.requestDisallowInterceptTouchEvent(true) + } else if (e.action == MotionEvent.ACTION_MOVE) { + val dx = e.x - initialX + val dy = e.y - initialY + val isVpHorizontal = orientation == ORIENTATION_HORIZONTAL + // assuming ViewPager2 touch-slop is 2x touch-slop of child + val scaledDx = dx.absoluteValue * if (isVpHorizontal) .5f else 1f + val scaledDy = dy.absoluteValue * if (isVpHorizontal) 1f else .5f + + if (scaledDx > touchSlop || scaledDy > touchSlop) { + + if (isVpHorizontal == (scaledDy > scaledDx)) { + // Gesture is perpendicular, allow all parents to intercept + parent.requestDisallowInterceptTouchEvent(false) + } else { + // Gesture is parallel, query child if movement in that direction is possible + if (canChildScroll(orientation, if (isVpHorizontal) dx else dy)) { + // Child can scroll, disallow all parents to intercept + parent.requestDisallowInterceptTouchEvent(true) + } else { + // Child cannot scroll, allow all parents to intercept + parent.requestDisallowInterceptTouchEvent(false) + } + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksAdapterGrid.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksAdapterGrid.kt index 286c13b74..9eabfbd45 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksAdapterGrid.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksAdapterGrid.kt @@ -7,8 +7,7 @@ import io.legado.app.base.adapter.ItemViewHolder import io.legado.app.constant.BookType import io.legado.app.data.entities.Book import io.legado.app.databinding.ItemBookshelfGridBinding -import io.legado.app.lib.theme.backgroundColor -import io.legado.app.utils.ColorUtils +import io.legado.app.help.AppConfig import io.legado.app.utils.invisible import splitties.views.onLongClick @@ -27,7 +26,6 @@ class BooksAdapterGrid(context: Context, private val callBack: CallBack) : ) = with(binding) { val bundle = payloads.getOrNull(0) as? Bundle if (bundle == null) { - root.setBackgroundColor(ColorUtils.withAlpha(context.backgroundColor, 0.5f)) tvName.text = item.name ivCover.load(item.getDisplayCover(), item.name, item.author) upRefresh(binding, item) @@ -48,8 +46,12 @@ class BooksAdapterGrid(context: Context, private val callBack: CallBack) : binding.rlLoading.show() } else { binding.rlLoading.hide() - binding.bvUnread.setBadgeCount(item.getUnreadChapterNum()) - binding.bvUnread.setHighlight(item.lastCheckCount > 0) + if (AppConfig.showUnread) { + binding.bvUnread.setBadgeCount(item.getUnreadChapterNum()) + binding.bvUnread.setHighlight(item.lastCheckCount > 0) + } else { + binding.bvUnread.invisible() + } } } diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksAdapterList.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksAdapterList.kt index 95d027042..b93d3430a 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksAdapterList.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksAdapterList.kt @@ -7,8 +7,7 @@ import io.legado.app.base.adapter.ItemViewHolder import io.legado.app.constant.BookType import io.legado.app.data.entities.Book import io.legado.app.databinding.ItemBookshelfListBinding -import io.legado.app.lib.theme.backgroundColor -import io.legado.app.utils.ColorUtils +import io.legado.app.help.AppConfig import io.legado.app.utils.invisible import splitties.views.onLongClick @@ -27,7 +26,6 @@ class BooksAdapterList(context: Context, private val callBack: CallBack) : ) = with(binding) { val bundle = payloads.getOrNull(0) as? Bundle if (bundle == null) { - root.setBackgroundColor(ColorUtils.withAlpha(context.backgroundColor, 0.5f)) tvName.text = item.name tvAuthor.text = item.author tvRead.text = item.durChapterTitle @@ -54,8 +52,12 @@ class BooksAdapterList(context: Context, private val callBack: CallBack) : binding.rlLoading.show() } else { binding.rlLoading.hide() - binding.bvUnread.setHighlight(item.lastCheckCount > 0) - binding.bvUnread.setBadgeCount(item.getUnreadChapterNum()) + if (AppConfig.showUnread) { + binding.bvUnread.setHighlight(item.lastCheckCount > 0) + binding.bvUnread.setBadgeCount(item.getUnreadChapterNum()) + } else { + binding.bvUnread.invisible() + } } } diff --git a/app/src/main/java/io/legado/app/ui/rss/article/RssSortActivity.kt b/app/src/main/java/io/legado/app/ui/rss/article/RssSortActivity.kt index b2bdb2920..eb3f34b9f 100644 --- a/app/src/main/java/io/legado/app/ui/rss/article/RssSortActivity.kt +++ b/app/src/main/java/io/legado/app/ui/rss/article/RssSortActivity.kt @@ -22,6 +22,7 @@ class RssSortActivity : VMBaseActivity() private lateinit var adapter: TabFragmentPageAdapter + private val fragmentMap = hashMapOf() private val upSourceResult = registerForActivityResult( ActivityResultContracts.StartActivityForResult() ) { @@ -99,12 +100,20 @@ class RssSortActivity : VMBaseActivity { - startX = ev.x.toInt() - startY = ev.y.toInt() - parent.requestDisallowInterceptTouchEvent(true) - } - MotionEvent.ACTION_MOVE -> { - val endX = ev.x.toInt() - val endY = ev.y.toInt() - val disX = abs(endX - startX) - val disY = abs(endY - startY) - if (disX > disY) { - parent.requestDisallowInterceptTouchEvent(canScrollHorizontally(startX - endX)) - } else { - parent.requestDisallowInterceptTouchEvent(canScrollVertically(startY - endY)) - } - } - MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> parent.requestDisallowInterceptTouchEvent( - false - ) - } - return super.dispatchTouchEvent(ev) - } - - -} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index a387ecb36..80c952ab8 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -5,7 +5,7 @@ android:layout_height="match_parent" android:orientation="vertical"> - + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_bookshelf.xml b/app/src/main/res/layout/fragment_bookshelf.xml index 4fbff35c3..b0a2f6250 100644 --- a/app/src/main/res/layout/fragment_bookshelf.xml +++ b/app/src/main/res/layout/fragment_bookshelf.xml @@ -1,5 +1,5 @@ - + app:contentLayout="@layout/view_tab_layout_min" + app:title="@string/bookshelf" /> - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/item_bookshelf_grid.xml b/app/src/main/res/layout/item_bookshelf_grid.xml index 0816a9a83..648d3b85f 100644 --- a/app/src/main/res/layout/item_bookshelf_grid.xml +++ b/app/src/main/res/layout/item_bookshelf_grid.xml @@ -1,67 +1,90 @@ - + android:clickable="true" + android:focusable="true" + tools:ignore="UnusedAttribute"> - + android:layout_height="wrap_content" + android:orientation="vertical" + android:paddingTop="8dp" + android:paddingLeft="8dp" + android:paddingRight="8dp" + app:layout_constraintTop_toTopOf="parent"> - + android:layout_height="wrap_content"> + + + + + + - + + - - + android:lines="2" + android:text="@string/book_name" + android:textColor="@color/primaryText" + android:textSize="12sp" + tools:ignore="RtlHardcoded,RtlSymmetry" /> - + - + android:layout_height="0dp" + android:background="?android:attr/selectableItemBackground" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintVertical_bias="0.0" + tools:layout_editor_absoluteX="0dp" /> + + - diff --git a/app/src/main/res/layout/item_bookshelf_list.xml b/app/src/main/res/layout/item_bookshelf_list.xml index 5a9fefbd2..00eb4966f 100644 --- a/app/src/main/res/layout/item_bookshelf_list.xml +++ b/app/src/main/res/layout/item_bookshelf_list.xml @@ -167,13 +167,4 @@ app:layout_constraintVertical_bias="0.0" tools:layout_editor_absoluteX="0dp" /> - - - \ No newline at end of file diff --git a/app/src/main/res/menu/book_cache.xml b/app/src/main/res/menu/book_cache.xml index 42c873c82..71598a744 100644 --- a/app/src/main/res/menu/book_cache.xml +++ b/app/src/main/res/menu/book_cache.xml @@ -20,6 +20,11 @@ + + 此url已订阅 高刷 使用屏幕最高刷新率 + 导出所有 + 完成 + 显示未读标志 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index de3414c63..b826956ca 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -826,5 +826,8 @@ 此url已訂閱 高刷 使用螢幕最高刷新率 + 导出所有 + 完成 + 显示未读标志 diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index e25c7283c..1f17b8764 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -826,5 +826,8 @@ 此url已订阅 高刷 使用屏幕最高刷新率 + 导出所有 + 完成 + 显示未读标志 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2b302583a..cd76c1764 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -827,5 +827,8 @@ 此url已订阅 高刷 使用屏幕最高刷新率 + export_all + complete + Show unread diff --git a/build.gradle b/build.gradle index f826dc95c..e9650f9c8 100644 --- a/build.gradle +++ b/build.gradle @@ -11,7 +11,8 @@ buildscript { } dependencies { classpath 'com.android.tools.build:gradle:4.2.0' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.0" + //noinspection DifferentKotlinGradleVersion + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "de.timfreiheit.resourceplaceholders:placeholders:0.3" } } @@ -19,7 +20,7 @@ buildscript { allprojects { repositories { google() - maven { url 'https://maven.aliyun.com/repository/public/'} + maven { url 'https://maven.aliyun.com/repository/public/' } maven { url "https://jitpack.io" } maven { url "https://maven.google.com/" } }