diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f4d3af897..5256b0ebc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -138,8 +138,10 @@ android:launchMode="singleTop"> + + @@ -186,6 +188,8 @@ android:launchMode="singleTop" /> + @@ -219,7 +227,6 @@ - diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index cd31303d4..7ace22ee4 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -2,11 +2,19 @@ * 旧版数据导入教程:先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。 * 请关注[开源阅读软件]()支持我,同时关注合作公众号[小说拾遗](),阅读公众号小编。 +**2020/03/06** +* 添加隐藏标题 +* 行距段距改成倍距,根据字体大小变化 +* 修复翻页时右下角页数闪烁 +* 修复朗读错行 +* 添加底部分隔线,开关在边距设置里 + **2020/03/05** * 修复翻页动画 * 修复主题模式跟随 * 修复滚动翻页切换章节时跳动 * 适配阅读3.0的web做源 +* 本地目录规则网络导入 **2020/03/04** * 修复仿真翻页动画 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 b427f687b..1ba9ba6cf 100644 --- a/app/src/main/java/io/legado/app/constant/PreferKey.kt +++ b/app/src/main/java/io/legado/app/constant/PreferKey.kt @@ -27,6 +27,7 @@ object PreferKey { const val fontFolder = "fontFolder" const val backupPath = "backupUri" const val threadCount = "threadCount" + const val webPort = "webPort" const val keepLight = "keep_light" const val webService = "webService" const val webDavUrl = "web_dav_url" diff --git a/app/src/main/java/io/legado/app/data/dao/TxtTocRuleDao.kt b/app/src/main/java/io/legado/app/data/dao/TxtTocRuleDao.kt index 5812b419c..1bd502a09 100644 --- a/app/src/main/java/io/legado/app/data/dao/TxtTocRuleDao.kt +++ b/app/src/main/java/io/legado/app/data/dao/TxtTocRuleDao.kt @@ -16,6 +16,9 @@ interface TxtTocRuleDao { @get:Query("select * from txtTocRules where enable = 1 order by serialNumber") val enabled: List + @get:Query("select ifNull(max(serialNumber), 0) from txtTocRules") + val lastOrderNum: Int + @Insert(onConflict = OnConflictStrategy.REPLACE) fun insert(vararg rule: TxtTocRule) diff --git a/app/src/main/java/io/legado/app/data/entities/ReplaceRule.kt b/app/src/main/java/io/legado/app/data/entities/ReplaceRule.kt index 8744ce2cb..dc1364559 100644 --- a/app/src/main/java/io/legado/app/data/entities/ReplaceRule.kt +++ b/app/src/main/java/io/legado/app/data/entities/ReplaceRule.kt @@ -1,11 +1,14 @@ package io.legado.app.data.entities import android.os.Parcelable +import android.text.TextUtils import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.Index import androidx.room.PrimaryKey import kotlinx.android.parcel.Parcelize +import java.util.regex.Pattern +import java.util.regex.PatternSyntaxException @Parcelize @Entity( @@ -37,4 +40,20 @@ data class ReplaceRule( override fun hashCode(): Int { return id.hashCode() } + + fun isValid(): Boolean{ + if (TextUtils.isEmpty(pattern)){ + return false; + } + //判断正则表达式是否正确 + if (isRegex){ + try { + Pattern.compile(pattern); + } + catch (ex: PatternSyntaxException){ + return false; + } + } + return true; + } } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/help/BlurTransformation.kt b/app/src/main/java/io/legado/app/help/BlurTransformation.kt index fec7d0eef..4493228c4 100644 --- a/app/src/main/java/io/legado/app/help/BlurTransformation.kt +++ b/app/src/main/java/io/legado/app/help/BlurTransformation.kt @@ -8,10 +8,8 @@ import android.renderscript.Allocation import android.renderscript.Element import android.renderscript.RenderScript import android.renderscript.ScriptIntrinsicBlur - import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool -import com.bumptech.glide.load.resource.bitmap.BitmapTransformation - +import com.bumptech.glide.load.resource.bitmap.CenterCrop import java.security.MessageDigest import kotlin.math.min import kotlin.math.roundToInt @@ -21,15 +19,16 @@ import kotlin.math.roundToInt * 模糊 * @radius: 0..25 */ -class BlurTransformation(context: Context, private val radius: Int) : BitmapTransformation() { +class BlurTransformation(context: Context, private val radius: Int) : CenterCrop() { private val rs: RenderScript = RenderScript.create(context) @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) override fun transform(pool: BitmapPool, toTransform: Bitmap, outWidth: Int, outHeight: Int): Bitmap { + val transform = super.transform(pool, toTransform, outWidth, outHeight) //图片缩小1/2 - val width = (min(outWidth, toTransform.width) / 2f).roundToInt() - val height = (min(outHeight, toTransform.height) / 2f).roundToInt() - val blurredBitmap = Bitmap.createScaledBitmap(toTransform, width, height, false); + val width = (min(outWidth, transform.width) / 2f).roundToInt() + val height = (min(outHeight, transform.height) / 2f).roundToInt() + val blurredBitmap = Bitmap.createScaledBitmap(transform, width, height, false); // Allocate memory for Renderscript to work with //分配用于渲染脚本的内存 val input = Allocation.createFromBitmap( diff --git a/app/src/main/java/io/legado/app/help/ReadBookConfig.kt b/app/src/main/java/io/legado/app/help/ReadBookConfig.kt index dcef91c72..77dc45e9a 100644 --- a/app/src/main/java/io/legado/app/help/ReadBookConfig.kt +++ b/app/src/main/java/io/legado/app/help/ReadBookConfig.kt @@ -165,10 +165,21 @@ object ReadBookConfig { if (shareLayout) shareConfig.paragraphSpacing = value else durConfig.paragraphSpacing = value - var titleCenter: Boolean - get() = if (shareLayout) shareConfig.titleCenter else durConfig.titleCenter + var titleMode: Int + get() = if (shareLayout) shareConfig.titleMode else durConfig.titleMode set(value) = - if (shareLayout) shareConfig.titleCenter = value else durConfig.titleCenter = value + if (shareLayout) shareConfig.titleMode = value else durConfig.titleMode = value + + var titleTopSpacing: Int + get() = if (shareLayout) shareConfig.titleTopSpacing else durConfig.titleTopSpacing + set(value) = + if (shareLayout) shareConfig.titleTopSpacing = value + else durConfig.titleTopSpacing = value + var titleBottomSpacing: Int + get() = if (shareLayout) shareConfig.titleBottomSpacing else durConfig.titleBottomSpacing + set(value) = + if (shareLayout) shareConfig.titleBottomSpacing = value + else durConfig.titleBottomSpacing = value var paddingBottom: Int get() = if (shareLayout) shareConfig.paddingBottom else durConfig.paddingBottom @@ -238,6 +249,12 @@ object ReadBookConfig { if (shareLayout) shareConfig.footerPaddingTop = value else durConfig.footerPaddingTop = value + var showFooterLine: Boolean + get() = if (shareLayout) shareConfig.showFooterLine else durConfig.showFooterLine + set(value) = + if (shareLayout) shareConfig.showFooterLine = value + else durConfig.showFooterLine = value + @Keep class Config( private var bgStr: String = "#EEEEEE",//白天背景 @@ -253,7 +270,9 @@ object ReadBookConfig { var letterSpacing: Float = 0.5f,//字间距 var lineSpacingExtra: Int = 12,//行间距 var paragraphSpacing: Int = 12,//段距 - var titleCenter: Boolean = true,//标题居中 + var titleMode: Int = 0,//标题居中 + var titleTopSpacing: Int = 0, + var titleBottomSpacing: Int = 0, var paddingBottom: Int = 6, var paddingLeft: Int = 16, var paddingRight: Int = 16, @@ -265,7 +284,8 @@ object ReadBookConfig { var footerPaddingBottom: Int = 6, var footerPaddingLeft: Int = 16, var footerPaddingRight: Int = 16, - var footerPaddingTop: Int = 6 + var footerPaddingTop: Int = 6, + var showFooterLine: Boolean = true ) { fun setBg(bgType: Int, bg: String) { if (AppConfig.isNightTheme) { diff --git a/app/src/main/java/io/legado/app/help/storage/ImportOldData.kt b/app/src/main/java/io/legado/app/help/storage/ImportOldData.kt index 8d0c9cd2c..4af39cbb2 100644 --- a/app/src/main/java/io/legado/app/help/storage/ImportOldData.kt +++ b/app/src/main/java/io/legado/app/help/storage/ImportOldData.kt @@ -136,13 +136,17 @@ object ImportOldData { return bookSources.size } + + fun importOldReplaceRule(json: String): Int { val replaceRules = mutableListOf() val items: List> = Restore.jsonPath.parse(json).read("$") for (item in items) { val jsonItem = Restore.jsonPath.parse(item) OldRule.jsonToReplaceRule(jsonItem.jsonString())?.let { - replaceRules.add(it) + if (it.isValid()){ + replaceRules.add(it) + } } } App.db.replaceRuleDao().insert(*replaceRules.toTypedArray()) diff --git a/app/src/main/java/io/legado/app/service/WebService.kt b/app/src/main/java/io/legado/app/service/WebService.kt index e9d223956..bbf317066 100644 --- a/app/src/main/java/io/legado/app/service/WebService.kt +++ b/app/src/main/java/io/legado/app/service/WebService.kt @@ -3,12 +3,12 @@ package io.legado.app.service import android.content.Context import android.content.Intent import androidx.core.app.NotificationCompat -import io.legado.app.App import io.legado.app.R import io.legado.app.base.BaseService -import io.legado.app.constant.IntentAction import io.legado.app.constant.AppConst import io.legado.app.constant.EventBus +import io.legado.app.constant.IntentAction +import io.legado.app.constant.PreferKey import io.legado.app.help.IntentHelp import io.legado.app.utils.NetworkUtils import io.legado.app.utils.getPrefInt @@ -96,7 +96,7 @@ class WebService : BaseService() { } private fun getPort(): Int { - var port = App.INSTANCE.getPrefInt("webPort", 1122) + var port = getPrefInt(PreferKey.webPort, 1122) if (port > 65530 || port < 1024) { port = 1122 } diff --git a/app/src/main/java/io/legado/app/ui/audio/AudioPlayActivity.kt b/app/src/main/java/io/legado/app/ui/audio/AudioPlayActivity.kt index 502e49986..bece64c93 100644 --- a/app/src/main/java/io/legado/app/ui/audio/AudioPlayActivity.kt +++ b/app/src/main/java/io/legado/app/ui/audio/AudioPlayActivity.kt @@ -11,7 +11,7 @@ import android.widget.SeekBar import androidx.lifecycle.Observer import com.bumptech.glide.RequestBuilder import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions -import com.bumptech.glide.request.RequestOptions +import com.bumptech.glide.request.RequestOptions.bitmapTransform import io.legado.app.R import io.legado.app.base.VMBaseActivity import io.legado.app.constant.EventBus @@ -120,19 +120,17 @@ class AudioPlayActivity : ImageLoader.load(this, path) .placeholder(R.drawable.image_cover_default) .error(R.drawable.image_cover_default) - .centerCrop() .into(iv_cover) ImageLoader.load(this, path) .transition(DrawableTransitionOptions.withCrossFade(1500)) .thumbnail(defaultCover()) - .centerCrop() - .apply(RequestOptions.bitmapTransform(BlurTransformation(this, 25))) + .apply(bitmapTransform(BlurTransformation(this, 25))) .into(iv_bg) } private fun defaultCover(): RequestBuilder { return ImageLoader.load(this, R.drawable.image_cover_default) - .apply(RequestOptions.bitmapTransform(BlurTransformation(this, 25))) + .apply(bitmapTransform(BlurTransformation(this, 25))) } private fun playButton() { diff --git a/app/src/main/java/io/legado/app/ui/book/changecover/ChangeCoverDialog.kt b/app/src/main/java/io/legado/app/ui/book/changecover/ChangeCoverDialog.kt index fdc64fa5f..85df58172 100644 --- a/app/src/main/java/io/legado/app/ui/book/changecover/ChangeCoverDialog.kt +++ b/app/src/main/java/io/legado/app/ui/book/changecover/ChangeCoverDialog.kt @@ -3,19 +3,24 @@ package io.legado.app.ui.book.changecover import android.os.Bundle import android.util.DisplayMetrics import android.view.LayoutInflater +import android.view.MenuItem import android.view.View import android.view.ViewGroup -import androidx.fragment.app.DialogFragment +import androidx.appcompat.widget.Toolbar import androidx.fragment.app.FragmentManager import androidx.lifecycle.Observer +import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.GridLayoutManager import io.legado.app.R +import io.legado.app.base.BaseDialogFragment +import io.legado.app.constant.Theme +import io.legado.app.utils.applyTint import io.legado.app.utils.getViewModel import kotlinx.android.synthetic.main.dialog_change_source.* -class ChangeCoverDialog : DialogFragment(), - ChangeCoverViewModel.CallBack, +class ChangeCoverDialog : BaseDialogFragment(), + Toolbar.OnMenuItemClickListener, CoverAdapter.CallBack { companion object { @@ -35,7 +40,7 @@ class ChangeCoverDialog : DialogFragment(), private var callBack: CallBack? = null private lateinit var viewModel: ChangeCoverViewModel - override lateinit var adapter: CoverAdapter + lateinit var adapter: CoverAdapter override fun onStart() { super.onStart() @@ -51,25 +56,57 @@ class ChangeCoverDialog : DialogFragment(), ): View? { callBack = activity as? CallBack viewModel = getViewModel(ChangeCoverViewModel::class.java) - viewModel.callBack = this return inflater.inflate(R.layout.dialog_change_cover, container) } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - viewModel.searchStateData.observe(viewLifecycleOwner, Observer { - refresh_progress_bar.isAutoLoading = it - }) + override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) { tool_bar.setTitle(R.string.change_cover_source) - arguments?.let { bundle -> - viewModel.initData(bundle) - } + viewModel.initData(arguments) + initMenu() + initView() + } + + private fun initMenu() { + tool_bar.inflateMenu(R.menu.change_cover) + tool_bar.menu.applyTint(requireContext(), Theme.getTheme()) + tool_bar.setOnMenuItemClickListener(this) + } + + private fun initView() { recycler_view.layoutManager = GridLayoutManager(requireContext(), 3) adapter = CoverAdapter(requireContext(), this) recycler_view.adapter = adapter viewModel.loadDbSearchBook() } + override fun observeLiveBus() { + super.observeLiveBus() + viewModel.searchStateData.observe(viewLifecycleOwner, Observer { + refresh_progress_bar.isAutoLoading = it + if (it) { + stopMenuItem?.setIcon(R.drawable.ic_stop_black_24dp) + } else { + stopMenuItem?.setIcon(R.drawable.ic_refresh_black_24dp) + } + tool_bar.menu.applyTint(requireContext(), Theme.getTheme()) + }) + viewModel.searchBooksLiveData.observe(viewLifecycleOwner, Observer { + val diffResult = DiffUtil.calculateDiff(DiffCallBack(adapter.getItems(), it)) + adapter.setItems(it) + diffResult.dispatchUpdatesTo(adapter) + }) + } + + override fun onMenuItemClick(item: MenuItem?): Boolean { + when (item?.itemId) { + R.id.menu_stop -> viewModel.stopSearch() + } + return false + } + + private val stopMenuItem: MenuItem? + get() = tool_bar.menu.findItem(R.id.menu_stop) + override fun changeTo(coverUrl: String) { callBack?.coverChangeTo(coverUrl) dismiss() diff --git a/app/src/main/java/io/legado/app/ui/book/changecover/ChangeCoverViewModel.kt b/app/src/main/java/io/legado/app/ui/book/changecover/ChangeCoverViewModel.kt index 0316c4fc9..627bacc09 100644 --- a/app/src/main/java/io/legado/app/ui/book/changecover/ChangeCoverViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/changecover/ChangeCoverViewModel.kt @@ -6,41 +6,46 @@ import androidx.lifecycle.MutableLiveData import io.legado.app.App import io.legado.app.base.BaseViewModel import io.legado.app.constant.AppPattern +import io.legado.app.data.entities.SearchBook import io.legado.app.help.AppConfig import io.legado.app.help.coroutine.Coroutine import io.legado.app.model.WebBook import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.asCoroutineDispatcher -import kotlinx.coroutines.withContext import java.util.concurrent.Executors class ChangeCoverViewModel(application: Application) : BaseViewModel(application) { private var searchPool = Executors.newFixedThreadPool(AppConfig.threadCount).asCoroutineDispatcher() - var callBack: CallBack? = null var name: String = "" var author: String = "" private var task: Coroutine<*>? = null val searchStateData = MutableLiveData() + val searchBooksLiveData = MutableLiveData>() + private val searchBooks = ArrayList() - fun initData(bundle: Bundle) { - bundle.getString("name")?.let { - name = it - } - bundle.getString("author")?.let { - author = it.replace(AppPattern.authorRegex, "") + fun initData(arguments: Bundle?) { + arguments?.let { bundle -> + bundle.getString("name")?.let { + name = it + } + bundle.getString("author")?.let { + author = it.replace(AppPattern.authorRegex, "") + } } } fun loadDbSearchBook() { execute { - App.db.searchBookDao().getEnableHasCover(name, author) - }.onSuccess { - it?.let { - callBack?.adapter?.setItems(it) + App.db.searchBookDao().getEnableHasCover(name, author).let { + searchBooks.addAll(it) + if (it.size <= 1) { + searchBooksLiveData.postValue(searchBooks) + search() + } else { + searchBooksLiveData.postValue(searchBooks) + } } - }.onFinally { - search() } } @@ -59,12 +64,9 @@ class ChangeCoverViewModel(application: Application) : BaseViewModel(application && !searchBook.coverUrl.isNullOrEmpty() ) { App.db.searchBookDao().insert(searchBook) - callBack?.adapter?.let { adapter -> - if (!adapter.getItems().contains(searchBook)) { - withContext(Dispatchers.Main) { - adapter.addItem(searchBook) - } - } + if (!searchBooks.contains(searchBook)) { + searchBooks.add(searchBook) + searchBooksLiveData.postValue(searchBooks) } } } @@ -77,12 +79,17 @@ class ChangeCoverViewModel(application: Application) : BaseViewModel(application } } + fun stopSearch() { + if (task?.isActive == true) { + task?.cancel() + } else { + search() + } + } + override fun onCleared() { super.onCleared() searchPool.close() } - interface CallBack { - var adapter: CoverAdapter - } } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/book/changecover/DiffCallBack.kt b/app/src/main/java/io/legado/app/ui/book/changecover/DiffCallBack.kt new file mode 100644 index 000000000..ffdac0f79 --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/book/changecover/DiffCallBack.kt @@ -0,0 +1,52 @@ +package io.legado.app.ui.book.changecover + +import android.os.Bundle +import androidx.recyclerview.widget.DiffUtil +import io.legado.app.data.entities.SearchBook + +class DiffCallBack(private val oldItems: List, private val newItems: List) : + DiffUtil.Callback() { + + override fun getOldListSize(): Int { + return oldItems.size + } + + override fun getNewListSize(): Int { + return newItems.size + } + + override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { + val oldItem = oldItems[oldItemPosition] + val newItem = newItems[newItemPosition] + return oldItem.bookUrl == newItem.bookUrl + } + + override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { + val oldItem = oldItems[oldItemPosition] + val newItem = newItems[newItemPosition] + if (oldItem.originName != newItem.originName) { + return false + } + if (oldItem.coverUrl != newItem.coverUrl) { + return false + } + return true + } + + override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int): Any? { + val oldItem = oldItems[oldItemPosition] + val newItem = newItems[newItemPosition] + val payload = Bundle() + if (oldItem.originName != newItem.originName) { + payload.putString("name", newItem.originName) + } + if (oldItem.coverUrl != newItem.coverUrl) { + payload.putString("coverUrl", newItem.coverUrl) + } + if (payload.isEmpty) { + return null + } + return payload + } + +} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceDialog.kt b/app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceDialog.kt index 5188e38ce..bb60c19d4 100644 --- a/app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceDialog.kt +++ b/app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceDialog.kt @@ -8,13 +8,13 @@ import android.view.View import android.view.ViewGroup import androidx.appcompat.widget.SearchView import androidx.appcompat.widget.Toolbar -import androidx.fragment.app.DialogFragment import androidx.fragment.app.FragmentManager import androidx.lifecycle.Observer import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import io.legado.app.R +import io.legado.app.base.BaseDialogFragment import io.legado.app.constant.PreferKey import io.legado.app.constant.Theme import io.legado.app.data.entities.Book @@ -27,7 +27,7 @@ import io.legado.app.utils.putPrefBoolean import kotlinx.android.synthetic.main.dialog_change_source.* -class ChangeSourceDialog : DialogFragment(), +class ChangeSourceDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener, ChangeSourceAdapter.CallBack { @@ -67,8 +67,7 @@ class ChangeSourceDialog : DialogFragment(), return inflater.inflate(R.layout.dialog_change_source, container) } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) + override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) { viewModel.initData(arguments) showTitle() initMenu() @@ -76,7 +75,6 @@ class ChangeSourceDialog : DialogFragment(), initSearchView() initLiveData() viewModel.loadDbSearchBook() - viewModel.search() } private fun showTitle() { @@ -138,6 +136,12 @@ class ChangeSourceDialog : DialogFragment(), private fun initLiveData() { viewModel.searchStateData.observe(viewLifecycleOwner, Observer { refresh_progress_bar.isAutoLoading = it + if (it) { + stopMenuItem?.setIcon(R.drawable.ic_stop_black_24dp) + } else { + stopMenuItem?.setIcon(R.drawable.ic_refresh_black_24dp) + } + tool_bar.menu.applyTint(requireContext(), Theme.getTheme()) }) viewModel.searchBooksLiveData.observe(viewLifecycleOwner, Observer { val diffResult = DiffUtil.calculateDiff(DiffCallBack(adapter.getItems(), it)) @@ -146,6 +150,9 @@ class ChangeSourceDialog : DialogFragment(), }) } + private val stopMenuItem: MenuItem? + get() = tool_bar.menu.findItem(R.id.menu_stop) + override fun onMenuItemClick(item: MenuItem?): Boolean { when (item?.itemId) { R.id.menu_load_toc -> { diff --git a/app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceViewModel.kt b/app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceViewModel.kt index 151577c00..e0800c0a1 100644 --- a/app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceViewModel.kt @@ -45,7 +45,12 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio execute { App.db.searchBookDao().getByNameAuthorEnable(name, author).let { searchBooks.addAll(it) - upAdapter() + if (it.size <= 1) { + upAdapter() + search() + } else { + upAdapter() + } } } } @@ -147,7 +152,11 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio } fun stopSearch() { - task?.cancel() + if (task?.isActive == true) { + task?.cancel() + } else { + search() + } } override fun onCleared() { diff --git a/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt b/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt index 1deccd667..621a41fa9 100644 --- a/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt @@ -4,7 +4,6 @@ import android.app.Activity import android.content.Intent import android.graphics.drawable.Drawable import android.os.Bundle -import android.text.method.ScrollingMovementMethod import android.view.Menu import android.view.MenuItem import androidx.lifecycle.Observer @@ -55,7 +54,6 @@ class BookInfoActivity : override fun onActivityCreated(savedInstanceState: Bundle?) { setSupportActionBar(toolbar) supportActionBar?.setDisplayHomeAsUpEnabled(true) - tv_intro.movementMethod = ScrollingMovementMethod.getInstance() viewModel.bookData.observe(this, Observer { showBook(it) }) viewModel.chapterListData.observe(this, Observer { upLoading(false, it) }) viewModel.initData(intent) @@ -126,7 +124,6 @@ class BookInfoActivity : ImageLoader.load(this, book.getDisplayCover()) .transition(DrawableTransitionOptions.withCrossFade(1500)) .thumbnail(defaultCover()) - .centerCrop() .apply(bitmapTransform(BlurTransformation(this, 25))) .into(bg_book) //模糊、渐变、缩小效果 } diff --git a/app/src/main/java/io/legado/app/ui/book/read/config/PaddingConfigDialog.kt b/app/src/main/java/io/legado/app/ui/book/read/config/PaddingConfigDialog.kt index 1155e1d7d..6ce9dfc03 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/config/PaddingConfigDialog.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/config/PaddingConfigDialog.kt @@ -11,6 +11,7 @@ import io.legado.app.constant.EventBus import io.legado.app.help.ReadBookConfig import io.legado.app.ui.book.read.Help import io.legado.app.utils.dp +import io.legado.app.utils.gone import io.legado.app.utils.postEvent import io.legado.app.utils.visible import kotlinx.android.synthetic.main.dialog_read_padding.* @@ -51,14 +52,13 @@ class PaddingConfigDialog : DialogFragment() { ReadBookConfig.save() } - private fun initData() = with(ReadBookConfig) { + private fun initData() = ReadBookConfig.apply { if (hideStatusBar) { - tv_header_padding.visible() - dsb_header_padding_top.visible() - dsb_header_padding_bottom.visible() - dsb_header_padding_left.visible() - dsb_header_padding_right.visible() + ll_header_padding.visible() tv_body_padding.setPadding(0, 10.dp, 0, 10.dp) + } else { + ll_header_padding.gone() + tv_body_padding.setPadding(0, 0.dp, 0, 10.dp) } //正文 dsb_padding_top.progress = paddingTop @@ -75,6 +75,7 @@ class PaddingConfigDialog : DialogFragment() { dsb_footer_padding_bottom.progress = footerPaddingBottom dsb_footer_padding_left.progress = footerPaddingLeft dsb_footer_padding_right.progress = footerPaddingRight + cb_show_line.isChecked = showFooterLine } private fun initView() = with(ReadBookConfig) { @@ -129,6 +130,12 @@ class PaddingConfigDialog : DialogFragment() { footerPaddingRight = it postEvent(EventBus.UP_CONFIG, true) } + cb_show_line.onCheckedChangeListener = { cb, isChecked -> + if (cb.isPressed) { + showFooterLine = isChecked + postEvent(EventBus.UP_CONFIG, true) + } + } } } diff --git a/app/src/main/java/io/legado/app/ui/book/read/config/ReadAloudDialog.kt b/app/src/main/java/io/legado/app/ui/book/read/config/ReadAloudDialog.kt index b295a05dc..5540d0386 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/config/ReadAloudDialog.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/config/ReadAloudDialog.kt @@ -62,6 +62,7 @@ class ReadAloudDialog : BaseDialogFragment() { private fun initData() { upPlayState() + upTimerText(BaseReadAloudService.timeMinute) seek_timer.progress = BaseReadAloudService.timeMinute cb_tts_follow_sys.isChecked = requireContext().getPrefBoolean("ttsFollowSys", true) seek_tts_SpeechRate.isEnabled = !cb_tts_follow_sys.isChecked @@ -89,7 +90,7 @@ class ReadAloudDialog : BaseDialogFragment() { }) seek_timer.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener { override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) { - tv_timer.text = requireContext().getString(R.string.timer_m, progress) + upTimerText(progress) } override fun onStartTrackingTouch(seekBar: SeekBar?) = Unit @@ -126,6 +127,10 @@ class ReadAloudDialog : BaseDialogFragment() { } } + private fun upTimerText(timeMinute: Int) { + tv_timer.text = requireContext().getString(R.string.timer_m, timeMinute) + } + private fun upTtsSpeechRate() { ReadAloud.upTtsSpeechRate(requireContext()) if (!BaseReadAloudService.pause) { diff --git a/app/src/main/java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt b/app/src/main/java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt index b51d8c088..4a5d435cf 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt @@ -32,6 +32,7 @@ import org.jetbrains.anko.sdk27.listeners.onLongClick class ReadStyleDialog : DialogFragment(), FontSelectDialog.CallBack { val callBack get() = activity as? ReadBookActivity + lateinit var titleModes: Array override fun onStart() { super.onStart() @@ -61,6 +62,7 @@ class ReadStyleDialog : DialogFragment(), FontSelectDialog.CallBack { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + titleModes = requireContext().resources.getStringArray(R.array.title_mode) initView() initData() initViewEvent() @@ -79,6 +81,8 @@ class ReadStyleDialog : DialogFragment(), FontSelectDialog.CallBack { dsb_text_letter_spacing.valueFormat = { ((it - 50) / 100f).toString() } + dsb_line_size.valueFormat = { ((it - 10) / 10f).toString() } + dsb_paragraph_spacing.valueFormat = { (it / 10f).toString() } } private fun initData() { @@ -97,12 +101,12 @@ class ReadStyleDialog : DialogFragment(), FontSelectDialog.CallBack { chinese_converter.onChanged { postEvent(EventBus.UP_CONFIG, true) } - tv_title_center.onClick { - ReadBookConfig.apply { - titleCenter = !titleCenter - tv_title_center.isSelected = titleCenter + tv_title_mode.onClick { + requireContext().selector("标题模式", titleModes.toList()) { _, index -> + ReadBookConfig.titleMode = index + tv_title_mode.text = titleModes[index] + postEvent(EventBus.UP_CONFIG, true) } - postEvent(EventBus.UP_CONFIG, true) } tv_text_bold.onClick { ReadBookConfig.apply { @@ -128,7 +132,7 @@ class ReadStyleDialog : DialogFragment(), FontSelectDialog.CallBack { callBack?.showPaddingConfig() } dsb_text_size.onChanged = { - ReadBookConfig.textSize = it + 5 + ReadBookConfig.textSize = it + 10 postEvent(EventBus.UP_CONFIG, true) } dsb_text_letter_spacing.onChanged = { @@ -187,7 +191,7 @@ class ReadStyleDialog : DialogFragment(), FontSelectDialog.CallBack { private fun upStyle() { ReadBookConfig.let { - tv_title_center.isSelected = it.titleCenter + tv_title_mode.text = titleModes.getOrElse(it.titleMode) { titleModes[0] } tv_text_bold.isSelected = it.textBold dsb_text_size.progress = it.textSize - 5 dsb_text_letter_spacing.progress = (it.letterSpacing * 100).toInt() + 50 diff --git a/app/src/main/java/io/legado/app/ui/book/read/config/TocRegexDialog.kt b/app/src/main/java/io/legado/app/ui/book/read/config/TocRegexDialog.kt index af5d1ed65..aa816f93f 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/config/TocRegexDialog.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/config/TocRegexDialog.kt @@ -15,6 +15,7 @@ import androidx.lifecycle.Observer import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView +import com.google.android.material.snackbar.Snackbar import io.legado.app.App import io.legado.app.R import io.legado.app.base.BaseDialogFragment @@ -27,9 +28,10 @@ import io.legado.app.lib.dialogs.alert import io.legado.app.lib.dialogs.cancelButton import io.legado.app.lib.dialogs.customView import io.legado.app.lib.dialogs.okButton -import io.legado.app.model.localBook.AnalyzeTxtFile import io.legado.app.ui.widget.recycler.VerticalDivider -import io.legado.app.utils.applyTint +import io.legado.app.ui.widget.text.AutoCompleteTextView +import io.legado.app.utils.* +import kotlinx.android.synthetic.main.dialog_edit_text.view.* import kotlinx.android.synthetic.main.dialog_toc_regex.* import kotlinx.android.synthetic.main.dialog_toc_regex_edit.view.* import kotlinx.android.synthetic.main.item_toc_regex.view.* @@ -40,11 +42,12 @@ import java.util.* class TocRegexDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener { - + private val importTocRuleKey = "tocRuleUrl" private lateinit var adapter: TocRegexAdapter private var tocRegexLiveData: LiveData>? = null var selectedName: String? = null private var durRegex: String? = null + lateinit var viewModel: TocRegexViewModel override fun onStart() { super.onStart() @@ -58,6 +61,7 @@ class TocRegexDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener { container: ViewGroup?, savedInstanceState: Bundle? ): View? { + viewModel = getViewModel(TocRegexViewModel::class.java) return inflater.inflate(R.layout.dialog_toc_regex, container) } @@ -121,17 +125,51 @@ class TocRegexDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener { override fun onMenuItemClick(item: MenuItem?): Boolean { when (item?.itemId) { R.id.menu_add -> editRule() - R.id.menu_default -> importDefault() + R.id.menu_default -> viewModel.importDefault() + R.id.menu_import -> showImportDialog() } return false } - private fun importDefault() { - launch(IO) { - AnalyzeTxtFile.getDefaultRules().let { - App.db.txtTocRule().insert(*it.toTypedArray()) - } + @SuppressLint("InflateParams") + private fun showImportDialog() { + val aCache = ACache.get(requireContext(), cacheDir = false) + val defaultUrl = "https://gitee.com/fisher52/YueDuJson/raw/master/myTxtChapterRule.json" + val cacheUrls: MutableList = aCache + .getAsString(importTocRuleKey) + ?.splitNotBlank(",") + ?.toMutableList() + ?: mutableListOf() + if (!cacheUrls.contains(defaultUrl)) { + cacheUrls.add(0, defaultUrl) } + requireContext().alert(titleResource = R.string.import_book_source_on_line) { + var editText: AutoCompleteTextView? = null + customView { + layoutInflater.inflate(R.layout.dialog_edit_text, null).apply { + editText = this.edit_view + edit_view.setFilterValues(cacheUrls) + edit_view.delCallBack = { + cacheUrls.remove(it) + aCache.put(importTocRuleKey, cacheUrls.joinToString(",")) + } + } + } + okButton { + val text = editText?.text?.toString() + text?.let { + if (!cacheUrls.contains(it)) { + cacheUrls.add(0, it) + aCache.put(importTocRuleKey, cacheUrls.joinToString(",")) + } + Snackbar.make(tool_bar, R.string.importing, Snackbar.LENGTH_INDEFINITE).show() + viewModel.importOnLine(it) { msg -> + tool_bar.snackbar(msg) + } + } + } + cancelButton() + }.show().applyTint() } @SuppressLint("InflateParams") @@ -151,25 +189,13 @@ class TocRegexDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener { rootView?.apply { tocRule.name = tv_rule_name.text.toString() tocRule.rule = tv_rule_regex.text.toString() - saveRule(tocRule, rule) + viewModel.saveRule(tocRule, rule) } } cancelButton() }.show().applyTint() } - private fun saveRule(rule: TxtTocRule, oldRule: TxtTocRule? = null) { - launch(IO) { - if (rule.serialNumber < 0) { - rule.serialNumber = adapter.getItems().lastOrNull()?.serialNumber ?: 0 + 1 - } - oldRule?.let { - App.db.txtTocRule().delete(oldRule) - } - App.db.txtTocRule().insert(rule) - } - } - inner class TocRegexAdapter(context: Context) : SimpleRecyclerAdapter(context, R.layout.item_toc_regex), ItemTouchCallback.OnItemTouchCallbackListener { @@ -240,7 +266,6 @@ class TocRegexDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener { } } - companion object { fun show(fragmentManager: FragmentManager, tocRegex: String? = null) { val dialog = TocRegexDialog() diff --git a/app/src/main/java/io/legado/app/ui/book/read/config/TocRegexViewModel.kt b/app/src/main/java/io/legado/app/ui/book/read/config/TocRegexViewModel.kt new file mode 100644 index 000000000..4a63120c8 --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/book/read/config/TocRegexViewModel.kt @@ -0,0 +1,48 @@ +package io.legado.app.ui.book.read.config + +import android.app.Application +import io.legado.app.App +import io.legado.app.base.BaseViewModel +import io.legado.app.data.entities.TxtTocRule +import io.legado.app.help.http.HttpHelper +import io.legado.app.model.localBook.AnalyzeTxtFile +import io.legado.app.utils.GSON +import io.legado.app.utils.fromJsonArray + +class TocRegexViewModel(application: Application) : BaseViewModel(application) { + + fun saveRule(rule: TxtTocRule, oldRule: TxtTocRule? = null) { + execute { + if (rule.serialNumber < 0) { + rule.serialNumber = App.db.txtTocRule().lastOrderNum + 1 + } + oldRule?.let { + App.db.txtTocRule().delete(oldRule) + } + App.db.txtTocRule().insert(rule) + } + } + + fun importDefault() { + execute { + AnalyzeTxtFile.getDefaultRules().let { + App.db.txtTocRule().insert(*it.toTypedArray()) + } + } + } + + fun importOnLine(url: String, finally: (msg: String) -> Unit) { + execute { + HttpHelper.simpleGetAsync(url)?.let { json -> + GSON.fromJsonArray(json)?.let { + App.db.txtTocRule().insert(*it.toTypedArray()) + } + } + }.onSuccess { + finally("导入成功") + }.onError { + finally("导入失败") + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/ChapterProvider.kt b/app/src/main/java/io/legado/app/ui/book/read/page/ChapterProvider.kt index 20afe26cf..3366c019d 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/ChapterProvider.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/ChapterProvider.kt @@ -13,10 +13,7 @@ import io.legado.app.help.ReadBookConfig import io.legado.app.ui.book.read.page.entities.TextChapter import io.legado.app.ui.book.read.page.entities.TextLine import io.legado.app.ui.book.read.page.entities.TextPage -import io.legado.app.utils.dp -import io.legado.app.utils.getPrefString -import io.legado.app.utils.removePref -import io.legado.app.utils.toStringArray +import io.legado.app.utils.* @Suppress("DEPRECATION") @@ -51,34 +48,16 @@ object ChapterProvider { val pageLines = arrayListOf() val pageLengths = arrayListOf() val stringBuilder = StringBuilder() - var surplusText = content + val contents = content.split("\n") var durY = 0f textPages.add(TextPage()) - while (surplusText.isNotEmpty()) { - if (textPages.first().textLines.isEmpty()) { - //title - val end = surplusText.indexOf("\n") - if (end > 0) { - val title = surplusText.substring(0, end) - surplusText = surplusText.substring(end + 1) - durY = setTypeText( - title, durY, textPages, pageLines, pageLengths, stringBuilder, true - ) - } - } else { - //正文 - val end = surplusText.indexOf("\n") - val text: String - if (end >= 0) { - text = surplusText.substring(0, end) - surplusText = surplusText.substring(end + 1) - } else { - text = surplusText - surplusText = "" - } - durY = - setTypeText(text, durY, textPages, pageLines, pageLengths, stringBuilder, false) + for ((index, text) in contents.withIndex()) { + val isTitle = index == 0 + if (isTitle && ReadBookConfig.titleMode == 2) { + continue } + durY = + setTypeText(text, durY, textPages, pageLines, pageLengths, stringBuilder, isTitle) } textPages.last().height = durY + 20.dp textPages.last().text = stringBuilder.toString() @@ -125,44 +104,45 @@ object ChapterProvider { Layout.Alignment.ALIGN_NORMAL, 0f, 0f, true ) for (lineIndex in 0 until layout.lineCount) { - textPages.last().height = durY val textLine = TextLine(isTitle = isTitle) - if (durY + textPaint.textHeight < visibleHeight) { - textPages.last().textLines.add(textLine) - durY += textPaint.textHeight + lineSpacingExtra - } else { - textPages.last().text = stringBuilder.toString() - stringBuilder.clear() - pageLines.add(textPages.last().textLines.size) - pageLengths.add(textPages.last().text.length) - //新页面 - durY = textPaint.textHeight + lineSpacingExtra - textPages.add(TextPage()) - textPages.last().textLines.add(textLine) - } - textLine.lineBottom = paddingTop + durY - lineSpacingExtra - textLine.lineBase = textLine.lineBottom - textPaint.fontMetrics.descent - textLine.lineTop = textLine.lineBottom - textPaint.textHeight val words = text.substring(layout.getLineStart(lineIndex), layout.getLineEnd(lineIndex)) - stringBuilder.append(words) textLine.text = words val desiredWidth = layout.getLineWidth(lineIndex) + var isLastLine = false if (lineIndex == 0 && layout.lineCount > 1 && !isTitle) { //第一行 addCharsToLineFirst(textLine, words, textPaint, desiredWidth) } else if (lineIndex == layout.lineCount - 1) { //最后一行 - val x = if (isTitle && ReadBookConfig.titleCenter) + isLastLine = true + val x = if (isTitle && ReadBookConfig.titleMode == 1) (visibleWidth - layout.getLineWidth(lineIndex)) / 2 else 0f - addCharsToLineLast(textLine, words, stringBuilder, textPaint, x) + addCharsToLineLast(textLine, words, textPaint, x) } else { //中间行 addCharsToLineMiddle(textLine, words, textPaint, desiredWidth, 0f) } + if (durY + textPaint.textHeight > visibleHeight) { + //当前页面结束,设置各种值 + textPages.last().text = stringBuilder.toString() + pageLines.add(textPages.last().textLines.size) + pageLengths.add(textPages.last().text.length) + textPages.last().height = durY + //新建页面 + textPages.add(TextPage()) + stringBuilder.clear() + durY = 0f + } + stringBuilder.append(words) + if (isLastLine) stringBuilder.append("\n") + textPages.last().textLines.add(textLine) + textLine.upTopBottom(durY, textPaint) + durY += textPaint.textHeight * lineSpacingExtra / 10f + textPages.last().height = durY } - durY += paragraphSpacing + durY += textPaint.textHeight * paragraphSpacing / 10f return durY } @@ -223,11 +203,9 @@ object ChapterProvider { private fun addCharsToLineLast( textLine: TextLine, words: String, - stringBuilder: StringBuilder, textPaint: TextPaint, startX: Float ) { - stringBuilder.append("\n") textLine.text = "$words\n" var x = startX words.toStringArray().forEach { @@ -291,10 +269,10 @@ object ChapterProvider { val bold = if (ReadBookConfig.textBold) Typeface.BOLD else Typeface.NORMAL contentPaint.typeface = Typeface.create(typeface, bold) //间距 - lineSpacingExtra = ReadBookConfig.lineSpacingExtra.dp - paragraphSpacing = ReadBookConfig.paragraphSpacing.dp - titlePaint.textSize = (ReadBookConfig.textSize + 2).dp.toFloat() - contentPaint.textSize = ReadBookConfig.textSize.dp.toFloat() + lineSpacingExtra = ReadBookConfig.lineSpacingExtra + paragraphSpacing = ReadBookConfig.paragraphSpacing + titlePaint.textSize = (ReadBookConfig.textSize + 2).sp.toFloat() + contentPaint.textSize = ReadBookConfig.textSize.sp.toFloat() upSize() } @@ -311,8 +289,6 @@ object ChapterProvider { visibleBottom = paddingTop + visibleHeight } - private val TextPaint.textHeight: Float - get() { - return this.fontMetrics.descent - fontMetrics.ascent + fontMetrics.leading - } + val TextPaint.textHeight: Float + get() = this.fontMetrics.descent - fontMetrics.ascent + fontMetrics.leading } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/ContentView.kt b/app/src/main/java/io/legado/app/ui/book/read/page/ContentView.kt index 7f1dcec1c..8e0bf1d55 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/ContentView.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/ContentView.kt @@ -60,6 +60,7 @@ class ContentView(context: Context) : FrameLayout(context) { footerPaddingRight.dp, footerPaddingBottom.dp ) + vw_bottom_divider.visible(showFooterLine) content_text_view.upVisibleRect() durConfig.textColor().let { tv_top_left.setTextColor(it) diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/CoverPageDelegate.kt b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/CoverPageDelegate.kt index c973fc87c..05437cd70 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/CoverPageDelegate.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/CoverPageDelegate.kt @@ -18,14 +18,16 @@ class CoverPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageView) { } override fun onDraw(canvas: Canvas) { + if (!isRunning) return val offsetX = touchX - startX if ((mDirection == Direction.NEXT && offsetX > 0) || (mDirection == Direction.PREV && offsetX < 0) - ) return + ) { + return + } val distanceX = if (offsetX > 0) offsetX - viewWidth else offsetX + viewWidth - if (!isRunning) return if (mDirection == Direction.PREV) { bitmapMatrix.setTranslate(distanceX, 0.toFloat()) curBitmap?.let { canvas.drawBitmap(it, 0f, 0f, null) } 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 125d6bb5f..e85a073cf 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 @@ -65,13 +65,13 @@ abstract class PageDelegate(protected val pageView: PageView) : var mDirection = Direction.NONE var isCancel = false var isRunning = false - var isStarted = false + private var isStarted = false var isTextSelected = false - var selectedOnDown = false + private var selectedOnDown = false - var firstRelativePage = 0 - var firstLineIndex: Int = 0 - var firstCharIndex: Int = 0 + private var firstRelativePage = 0 + private var firstLineIndex: Int = 0 + private var firstCharIndex: Int = 0 init { curPage.resetPageOffset() @@ -127,10 +127,12 @@ abstract class PageDelegate(protected val pageView: PageView) : } private fun stopScroll() { - isMoved = false - isRunning = false isStarted = false - pageView.invalidate() + pageView.post { + isMoved = false + isRunning = false + pageView.invalidate() + } } open fun setViewSize(width: Int, height: Int) { diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextLine.kt b/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextLine.kt index a9c84fb68..c8a9c9ca0 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextLine.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextLine.kt @@ -1,5 +1,9 @@ package io.legado.app.ui.book.read.page.entities +import android.text.TextPaint +import io.legado.app.ui.book.read.page.ChapterProvider +import io.legado.app.ui.book.read.page.ChapterProvider.textHeight + data class TextLine( var text: String = "", val textChars: ArrayList = arrayListOf(), @@ -10,6 +14,12 @@ data class TextLine( var isReadAloud: Boolean = false ) { + fun upTopBottom(durY: Float, textPaint: TextPaint) { + lineTop = ChapterProvider.paddingTop + durY + lineBottom = lineTop + textPaint.textHeight + lineBase = lineBottom - textPaint.fontMetrics.descent + } + fun addTextChar(charData: String, start: Float, end: Float) { textChars.add(TextChar(charData, start = start, end = end)) } diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextPage.kt b/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextPage.kt index e8782b6c3..f42fcdb5d 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextPage.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextPage.kt @@ -34,14 +34,15 @@ data class TextPage( (layout.getLineBottom(lineIndex) - layout.getLineBaseline(lineIndex))) textLine.lineBottom = textLine.lineBase + ChapterProvider.contentPaint.fontMetrics.descent - var x = (ChapterProvider.visibleWidth - layout.getLineMax(lineIndex)) / 2 + var x = ChapterProvider.paddingLeft + + (ChapterProvider.visibleWidth - layout.getLineMax(lineIndex)) / 2 textLine.text = text.substring(layout.getLineStart(lineIndex), layout.getLineEnd(lineIndex)) for (i in textLine.text.indices) { val char = textLine.text[i].toString() val cw = StaticLayout.getDesiredWidth(char, ChapterProvider.contentPaint) val x1 = x + cw - textLine.textChars.add(TextChar(charData = char, start = x, end = x1)) + textLine.addTextChar(charData = char, start = x, end = x1) x = x1 } textLines.add(textLine) diff --git a/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt b/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt index c05294965..31b1c2028 100644 --- a/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt @@ -214,7 +214,8 @@ class BookSourceActivity : VMBaseActivity(R.layout.activity customView { layoutInflater.inflate(R.layout.dialog_edit_text, null).apply { editText = edit_view - edit_view.setFilterValues(cacheUrls) { + edit_view.setFilterValues(cacheUrls) + edit_view.delCallBack = { cacheUrls.remove(it) aCache.put(importRecordKey, cacheUrls.joinToString(",")) } diff --git a/app/src/main/java/io/legado/app/ui/config/OtherConfigFragment.kt b/app/src/main/java/io/legado/app/ui/config/OtherConfigFragment.kt index e8ea335c0..58caa351d 100644 --- a/app/src/main/java/io/legado/app/ui/config/OtherConfigFragment.kt +++ b/app/src/main/java/io/legado/app/ui/config/OtherConfigFragment.kt @@ -18,6 +18,7 @@ import io.legado.app.help.AppConfig import io.legado.app.help.BookHelp import io.legado.app.lib.theme.ATH import io.legado.app.receiver.SharedReceiverActivity +import io.legado.app.service.WebService import io.legado.app.ui.filechooser.FileChooserDialog import io.legado.app.ui.filechooser.FilePicker import io.legado.app.ui.widget.number.NumberPickerDialog @@ -34,12 +35,14 @@ class OtherConfigFragment : PreferenceFragmentCompat(), App.INSTANCE, SharedReceiverActivity::class.java.name ) + private val webPort get() = getPrefInt(PreferKey.webPort, 1122) override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { putPrefBoolean(PreferKey.processText, isProcessTextEnabled()) addPreferencesFromResource(R.xml.pref_config_other) upPreferenceSummary(PreferKey.downloadPath, BookHelp.downloadPath) upPreferenceSummary(PreferKey.threadCount, AppConfig.threadCount.toString()) + upPreferenceSummary(PreferKey.webPort, webPort.toString()) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -63,6 +66,14 @@ class OtherConfigFragment : PreferenceFragmentCompat(), .show { AppConfig.threadCount = it } + PreferKey.webPort -> NumberPickerDialog(requireContext()) + .setTitle(getString(R.string.web_port_title)) + .setMaxValue(60000) + .setMinValue(1024) + .setValue(webPort) + .show { + putPrefInt(PreferKey.webPort, it) + } PreferKey.downloadPath -> selectDownloadPath() PreferKey.cleanCache -> { BookHelp.clearCache() @@ -78,9 +89,15 @@ class OtherConfigFragment : PreferenceFragmentCompat(), upPreferenceSummary(key, BookHelp.downloadPath) } PreferKey.threadCount -> upPreferenceSummary( - PreferKey.threadCount, - AppConfig.threadCount.toString() + key, AppConfig.threadCount.toString() ) + PreferKey.webPort -> { + upPreferenceSummary(key, webPort.toString()) + if (WebService.isRun) { + WebService.stop(requireContext()) + WebService.start(requireContext()) + } + } PreferKey.recordLog -> LogUtils.upLevel() PreferKey.processText -> sharedPreferences?.let { setProcessTextEnable(it.getBoolean(key, true)) @@ -93,6 +110,7 @@ class OtherConfigFragment : PreferenceFragmentCompat(), val preference = findPreference(preferenceKey) ?: return when (preferenceKey) { PreferKey.threadCount -> preference.summary = getString(R.string.threads_num, value) + PreferKey.webPort -> preference.summary = getString(R.string.web_port_summary, value) else -> if (preference is ListPreference) { val index = preference.findIndexOfValue(value) // Set the summary to reflect the new value. diff --git a/app/src/main/java/io/legado/app/ui/login/SourceLogin.kt b/app/src/main/java/io/legado/app/ui/login/SourceLogin.kt index 35a62200c..767545d38 100644 --- a/app/src/main/java/io/legado/app/ui/login/SourceLogin.kt +++ b/app/src/main/java/io/legado/app/ui/login/SourceLogin.kt @@ -76,4 +76,8 @@ class SourceLogin : BaseActivity(R.layout.activity_source_login) { return super.onCompatOptionsItemSelected(item) } + override fun onDestroy() { + super.onDestroy() + web_view.destroy() + } } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleActivity.kt b/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleActivity.kt index 91f7e7b75..16ffee4a1 100644 --- a/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleActivity.kt +++ b/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleActivity.kt @@ -213,7 +213,8 @@ class ReplaceRuleActivity : VMBaseActivity(R.layout.activi customView { layoutInflater.inflate(R.layout.dialog_edit_text, null).apply { editText = edit_view - edit_view.setFilterValues(cacheUrls) { + edit_view.setFilterValues(cacheUrls) + edit_view.delCallBack = { cacheUrls.remove(it) aCache.put(importRecordKey, cacheUrls.joinToString(",")) } diff --git a/app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditDialog.kt b/app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditDialog.kt index d125674c5..73c64a0e1 100644 --- a/app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditDialog.kt +++ b/app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditDialog.kt @@ -16,6 +16,7 @@ import io.legado.app.constant.Theme import io.legado.app.data.entities.ReplaceRule import io.legado.app.utils.applyTint import io.legado.app.utils.getViewModel +import io.legado.app.utils.toast import kotlinx.android.synthetic.main.dialog_replace_edit.* class ReplaceEditDialog : DialogFragment(), @@ -73,9 +74,15 @@ class ReplaceEditDialog : DialogFragment(), override fun onMenuItemClick(item: MenuItem?): Boolean { when (item?.itemId) { R.id.menu_save -> { - viewModel.save(getReplaceRule()) { - callBack?.onReplaceRuleSave() - dismiss() + val rule = getReplaceRule(); + if (!rule.isValid()){ + toast(R.string.replace_rule_invalid) + } + else{ + viewModel.save(rule) { + callBack?.onReplaceRuleSave() + dismiss() + } } } } diff --git a/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt b/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt index 10504efd7..1764a1f32 100644 --- a/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt +++ b/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt @@ -203,4 +203,9 @@ class ReadRssActivity : VMBaseActivity(R.layout.activity_rss_r } } + override fun onDestroy() { + super.onDestroy() + web_view.destroy() + } + } diff --git a/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt b/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt index c3dd008c8..35934e4d6 100644 --- a/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt +++ b/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt @@ -227,7 +227,8 @@ class RssSourceActivity : VMBaseActivity(R.layout.activity_r customView { layoutInflater.inflate(R.layout.dialog_edit_text, null).apply { editText = edit_view - edit_view.setFilterValues(cacheUrls) { + edit_view.setFilterValues(cacheUrls) + edit_view.delCallBack = { cacheUrls.remove(it) aCache.put(importRecordKey, cacheUrls.joinToString(",")) } diff --git a/app/src/main/java/io/legado/app/ui/widget/ArcView.kt b/app/src/main/java/io/legado/app/ui/widget/ArcView.kt new file mode 100644 index 000000000..b9834132c --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/widget/ArcView.kt @@ -0,0 +1,79 @@ +package io.legado.app.ui.widget + +import android.content.Context +import android.graphics.* +import android.util.AttributeSet +import android.view.View +import io.legado.app.R + +class ArcView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : View(context, attrs, defStyleAttr) { + private var mWidth = 0 + private var mHeight = 0 + + //弧形高度 + private val mArcHeight: Int + + //背景颜色 + private val mBgColor: Int + private val mPaint: Paint = Paint() + private val mDirectionTop: Boolean + val rect = Rect() + val path = Path() + + init { + val typedArray = context.obtainStyledAttributes(attrs, R.styleable.ArcView) + mArcHeight = typedArray.getDimensionPixelSize(R.styleable.ArcView_arcHeight, 0) + mBgColor = typedArray.getColor( + R.styleable.ArcView_bgColor, + Color.parseColor("#303F9F") + ) + mDirectionTop = typedArray.getBoolean(R.styleable.ArcView_arcDirectionTop, false) + typedArray.recycle() + } + + override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + mPaint.style = Paint.Style.FILL + mPaint.color = mBgColor + if (mDirectionTop) { + rect.set(0, mArcHeight, mWidth, mHeight) + canvas.drawRect(rect, mPaint) + path.reset() + path.moveTo(0f, mArcHeight.toFloat()) + path.quadTo(mWidth / 2.toFloat(), 0f, mWidth.toFloat(), mArcHeight.toFloat()) + canvas.drawPath(path, mPaint) + } else { + rect.set(0, 0, mWidth, mHeight - mArcHeight) + canvas.drawRect(rect, mPaint) + path.reset() + path.moveTo(0f, mHeight - mArcHeight.toFloat()) + path.quadTo( + mWidth / 2.toFloat(), + mHeight.toFloat(), + mWidth.toFloat(), + mHeight - mArcHeight.toFloat() + ) + canvas.drawPath(path, mPaint) + } + } + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec) + val widthSize = MeasureSpec.getSize(widthMeasureSpec) + val widthMode = MeasureSpec.getMode(widthMeasureSpec) + val heightSize = MeasureSpec.getSize(heightMeasureSpec) + val heightMode = MeasureSpec.getMode(heightMeasureSpec) + if (widthMode == MeasureSpec.EXACTLY) { + mWidth = widthSize + } + if (heightMode == MeasureSpec.EXACTLY) { + mHeight = heightSize + } + setMeasuredDimension(mWidth, mHeight) + } + +} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/widget/font/FontSelectDialog.kt b/app/src/main/java/io/legado/app/ui/widget/font/FontSelectDialog.kt index ac5a1281c..8157ed870 100644 --- a/app/src/main/java/io/legado/app/ui/widget/font/FontSelectDialog.kt +++ b/app/src/main/java/io/legado/app/ui/widget/font/FontSelectDialog.kt @@ -61,7 +61,6 @@ class FontSelectDialog : BaseDialogFragment(), } override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) tool_bar.setTitle(R.string.select_font) tool_bar.inflateMenu(R.menu.font_select) tool_bar.setOnMenuItemClickListener(this) diff --git a/app/src/main/java/io/legado/app/ui/widget/text/AutoCompleteTextView.kt b/app/src/main/java/io/legado/app/ui/widget/text/AutoCompleteTextView.kt index ac0a5a8ac..1319aac5e 100644 --- a/app/src/main/java/io/legado/app/ui/widget/text/AutoCompleteTextView.kt +++ b/app/src/main/java/io/legado/app/ui/widget/text/AutoCompleteTextView.kt @@ -42,15 +42,13 @@ class AutoCompleteTextView : AppCompatAutoCompleteTextView { return super.onTouchEvent(event) } - fun setFilterValues(values: List?, delCallBack: ((value: String) -> Unit)? = null) { - this.delCallBack = delCallBack + fun setFilterValues(values: List?) { values?.let { setAdapter(MyAdapter(context, values)) } } - fun setFilterValues(vararg value: String, delCallBack: ((value: String) -> Unit)? = null) { - this.delCallBack = delCallBack + fun setFilterValues(vararg value: String) { setAdapter(MyAdapter(context, value.toMutableList())) } diff --git a/app/src/main/java/io/legado/app/ui/widget/text/ScrollTextView.kt b/app/src/main/java/io/legado/app/ui/widget/text/ScrollTextView.kt new file mode 100644 index 000000000..d0b579cd4 --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/widget/text/ScrollTextView.kt @@ -0,0 +1,81 @@ +package io.legado.app.ui.widget.text + +import android.annotation.SuppressLint +import android.content.Context +import android.util.AttributeSet +import android.view.MotionEvent +import androidx.appcompat.widget.AppCompatTextView + +class ScrollTextView(context: Context?, attrs: AttributeSet?) : AppCompatTextView(context, attrs) { + //滑动距离的最大边界 + private var mOffsetHeight = 0 + + //是否到顶或者到底的标志 + private var mBottomFlag = false + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec) + initOffsetHeight() + } + + override fun onTextChanged( + text: CharSequence, + start: Int, + lengthBefore: Int, + lengthAfter: Int + ) { + super.onTextChanged(text, start, lengthBefore, lengthAfter) + initOffsetHeight() + } + + private fun initOffsetHeight() { + val mLayoutHeight: Int + + //获得内容面板 + val mLayout = layout ?: return + //获得内容面板的高度 + mLayoutHeight = mLayout.height + //获取上内边距 + val paddingTop: Int = totalPaddingTop + //获取下内边距 + val paddingBottom: Int = totalPaddingBottom + + //获得控件的实际高度 + val mHeight: Int = measuredHeight + + //计算滑动距离的边界 + mOffsetHeight = mLayoutHeight + paddingTop + paddingBottom - mHeight + if (mOffsetHeight <= 0) { + scrollTo(0, 0) + } + } + + override fun dispatchTouchEvent(event: MotionEvent): Boolean { + if (event.action == MotionEvent.ACTION_DOWN) { + //如果是新的按下事件,则对mBottomFlag重新初始化 + mBottomFlag = mOffsetHeight <= 0 + } + //如果已经不要这次事件,则传出取消的信号,这里的作用不大 + if (mBottomFlag) { + event.action = MotionEvent.ACTION_CANCEL + } + return super.dispatchTouchEvent(event) + } + + @SuppressLint("ClickableViewAccessibility") + override fun onTouchEvent(event: MotionEvent): Boolean { + val result = super.onTouchEvent(event) + //如果是需要拦截,则再拦截,这个方法会在onScrollChanged方法之后再调用一次 + if (!mBottomFlag) parent.requestDisallowInterceptTouchEvent(true) + return result + } + + override fun onScrollChanged(horiz: Int, vert: Int, oldHoriz: Int, oldVert: Int) { + super.onScrollChanged(horiz, vert, oldHoriz, oldVert) + if (vert == mOffsetHeight || vert == 0) { + //这里触发父布局或祖父布局的滑动事件 + parent.requestDisallowInterceptTouchEvent(false) + mBottomFlag = true + } + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_book_info.xml b/app/src/main/res/layout/activity_book_info.xml index 693fb53a1..8e58e34cb 100644 --- a/app/src/main/res/layout/activity_book_info.xml +++ b/app/src/main/res/layout/activity_book_info.xml @@ -9,227 +9,335 @@ - + android:orientation="vertical"> - - - + + + + + + + + + + + + + + android:paddingLeft="10dp" + android:paddingTop="8dp" + android:paddingRight="10dp" + android:paddingBottom="3dp"> + android:text="@string/book_name" + android:textColor="@color/tv_text_default" + android:textSize="18sp" + tools:ignore="RtlHardcoded" /> - + android:layout_gravity="center" + android:gravity="center" + android:visibility="gone" /> - + + + android:scrollbarStyle="outsideInset" + android:scrollbars="vertical" + android:fillViewport="true" + android:fitsSystemWindows="false" + android:focusable="true" > - + - + - - + - + - + - + - + - + - + - + - + - + - + + + + + + + + + + + + + + + android:background="@color/background" + android:orientation="vertical" + android:paddingTop="12dp" + android:paddingBottom="12dp" + app:layout_constraintBottom_toTopOf="@+id/fl_action" + app:layout_constraintTop_toBottomOf="@+id/view_info"> - + + + + + + + android:layout_height="1px" + android:background="@color/bg_divider_line" /> - + - + - - - + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_read_book_style.xml b/app/src/main/res/layout/dialog_read_book_style.xml index 42b79b97d..f2d8a1810 100644 --- a/app/src/main/res/layout/dialog_read_book_style.xml +++ b/app/src/main/res/layout/dialog_read_book_style.xml @@ -10,7 +10,7 @@ android:padding="10dp"> @@ -123,7 +123,7 @@ android:layout_height="wrap_content" android:paddingLeft="10dp" android:paddingRight="10dp" - app:max="50" + app:max="20" app:title="@string/line_size" app:layout_constraintTop_toBottomOf="@+id/dsb_text_letter_spacing" /> @@ -133,7 +133,7 @@ android:layout_height="wrap_content" android:paddingLeft="10dp" android:paddingRight="10dp" - app:max="50" + app:max="20" app:title="@string/paragraph_size" app:layout_constraintTop_toBottomOf="@+id/dsb_line_size" /> diff --git a/app/src/main/res/layout/dialog_read_padding.xml b/app/src/main/res/layout/dialog_read_padding.xml index 1b600959e..83bb14c71 100644 --- a/app/src/main/res/layout/dialog_read_padding.xml +++ b/app/src/main/res/layout/dialog_read_padding.xml @@ -7,51 +7,55 @@ android:padding="10dp" android:orientation="vertical"> - - - - - - - - - + android:orientation="vertical"> + + + + + + + + + + + + @@ -84,13 +88,33 @@ app:title="@string/padding_right" app:max="100" /> - + android:gravity="center_vertical" + android:orientation="horizontal"> + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/txt_toc_regex.xml b/app/src/main/res/menu/txt_toc_regex.xml index 38a50e8cc..2eb4a2b4c 100644 --- a/app/src/main/res/menu/txt_toc_regex.xml +++ b/app/src/main/res/menu/txt_toc_regex.xml @@ -15,4 +15,9 @@ android:title="@string/import_default_rule" app:showAsAction="never" /> + + \ No newline at end of file diff --git a/app/src/main/res/values/array_values.xml b/app/src/main/res/values/array_values.xml new file mode 100644 index 000000000..2aa918837 --- /dev/null +++ b/app/src/main/res/values/array_values.xml @@ -0,0 +1,42 @@ + + + + + ic_launcher + launcher1 + launcher2 + launcher3 + + + + + 0 + 60 + 120 + 180 + -1 + + + + 0 + 1 + 2 + + + + 0 + 1 + 3 + 4 + 5 + 103 + 106 + 110 + 111 + 11 + 6 + 9 + + + + \ No newline at end of file diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 4cd90b40e..15deecdec 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -20,21 +20,6 @@ 百度主持 - - 0 - 1 - 3 - 4 - 5 - 103 - 106 - 110 - 111 - 11 - 6 - 9 - - @string/indent_0 @string/indent_1 @@ -61,12 +46,6 @@ 暗色主题 - - 0 - 1 - 2 - - 自动 黑色 @@ -82,14 +61,6 @@ 常亮 - - 0 - 60 - 120 - 180 - -1 - - @string/default_path @string/sys_folder_picker @@ -103,15 +74,6 @@ @string/screen_sensor - - - - - - - - - iconMain icon1 @@ -119,13 +81,6 @@ icon3 - - ic_launcher - launcher1 - launcher2 - launcher3 - - 关闭 繁体转简体 @@ -137,4 +92,11 @@ 系统衬线字体 系统等宽字体 + + + 标题靠左 + 标题居中 + 标题隐藏 + + \ No newline at end of file diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index e42a82a0d..cc7adbeb8 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -137,6 +137,15 @@ + + + + + + + + + @@ -154,4 +163,10 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 68f3af591..309f2bec2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -204,6 +204,7 @@ 清除缓存会删除所有已保存章节,是否确认删除? 书源共享 替换规则名称 + 替换规则为空或者不满足正则表达式要求 选择操作 全选 全选(%d/%d) @@ -226,7 +227,7 @@ 打开外部书籍 来源:%s 本地导入 - 网络导入 + 导入在线规则 检查更新间隔 按阅读时间 按更新时间 diff --git a/app/src/main/res/xml/pref_config_other.xml b/app/src/main/res/xml/pref_config_other.xml index 4ecfb8a8e..b049bcf22 100644 --- a/app/src/main/res/xml/pref_config_other.xml +++ b/app/src/main/res/xml/pref_config_other.xml @@ -37,6 +37,11 @@ android:title="@string/threads_num_title" app:iconSpaceReserved="false" /> + +