Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
LM-Firefly committed Dec 22, 2024
2 parents d1c1b07 + 3b9f8cc commit a460716
Show file tree
Hide file tree
Showing 19 changed files with 166 additions and 35 deletions.
5 changes: 5 additions & 0 deletions app/src/main/java/io/legado/app/data/dao/RssArticleDao.kt
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,10 @@ interface RssArticleDao {
@Insert(onConflict = OnConflictStrategy.IGNORE)
fun insertRecord(vararg rssReadRecord: RssReadRecord)

@get:Query("select count(1) from rssReadRecords")
val countRead: Int

@Query("delete from rssReadRecords")
fun deleteRecord()

}
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import io.legado.app.ui.widget.recycler.DragSelectTouchHelper
import io.legado.app.ui.widget.recycler.ItemTouchCallback
import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.ACache
import io.legado.app.utils.NetworkUtils
import io.legado.app.utils.applyTint
import io.legado.app.utils.cnCompare
import io.legado.app.utils.dpToPx
Expand Down Expand Up @@ -103,6 +104,8 @@ class BookSourceActivity : VMBaseActivity<ActivityBookSourceBinding, BookSourceV
override var sortAscending = true
private set
private var snackBar: Snackbar? = null
private var showDuplicationSource = false
private val hostMap = hashMapOf<String, String>()
private val qrResult = registerForActivityResult(QrCodeResult()) {
it ?: return@registerForActivityResult
showDialogFragment(ImportBookSourceDialog(it))
Expand Down Expand Up @@ -265,6 +268,13 @@ class BookSourceActivity : VMBaseActivity<ActivityBookSourceBinding, BookSourceV
searchView.setQuery(getString(R.string.disabled_explore), true)
}

R.id.menu_show_same_source -> {
item.isChecked = !item.isChecked
showDuplicationSource = item.isChecked
adapter.showSourceHost = item.isChecked
upBookSource(searchView.query?.toString())
}

R.id.menu_help -> showHelp("SourceMBookHelp")
}
if (item.groupId == R.id.source_group) {
Expand Down Expand Up @@ -335,7 +345,13 @@ class BookSourceActivity : VMBaseActivity<ActivityBookSourceBinding, BookSourceV
appDb.bookSourceDao.flowSearch(searchKey)
}
}.map { data ->
if (sortAscending) {
hostMap.clear()
if (showDuplicationSource) {
data.sortedWith(
compareBy<BookSourcePart> { getSourceHost(it.bookSourceUrl) == "#" }
.thenBy { getSourceHost(it.bookSourceUrl) }
.thenByDescending { it.lastUpdateTime })
} else if (sortAscending) {
when (sort) {
BookSourceSort.Weight -> data.sortedBy { it.weight }
BookSourceSort.Name -> data.sortedWith { o1, o2 ->
Expand Down Expand Up @@ -383,7 +399,8 @@ class BookSourceActivity : VMBaseActivity<ActivityBookSourceBinding, BookSourceV
AppLog.put("书源界面更新书源出错", it)
}.flowOn(IO).conflate().collect { data ->
adapter.setItems(data, adapter.diffItemCallback, !Debug.isChecking)
itemTouchCallback.isCanDrag = sort == BookSourceSort.Default
itemTouchCallback.isCanDrag =
sort == BookSourceSort.Default && !showDuplicationSource
delay(500)
}
}
Expand Down Expand Up @@ -686,6 +703,12 @@ class BookSourceActivity : VMBaseActivity<ActivityBookSourceBinding, BookSourceV
.upCountView(adapter.selection.size, adapter.itemCount)
}

override fun getSourceHost(origin: String): String {
return hostMap.getOrPut(origin) {
NetworkUtils.getSubDomainOrNull(origin) ?: "#"
}
}

override fun onQueryTextChange(newText: String?): Boolean {
newText?.let {
upBookSource(it)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import io.legado.app.ui.login.SourceLoginActivity
import io.legado.app.ui.widget.recycler.DragSelectTouchHelper
import io.legado.app.ui.widget.recycler.ItemTouchCallback
import io.legado.app.utils.ColorUtils
import io.legado.app.utils.gone
import io.legado.app.utils.invisible
import io.legado.app.utils.startActivity
import io.legado.app.utils.visible
Expand All @@ -33,6 +34,7 @@ class BookSourceAdapter(context: Context, val callBack: CallBack) :

private val selected = linkedSetOf<BookSourcePart>()
private val finalMessageRegex = Regex("成功|失败")
var showSourceHost = false

val selection: List<BookSourcePart>
get() {
Expand Down Expand Up @@ -96,6 +98,7 @@ class BookSourceAdapter(context: Context, val callBack: CallBack) :
cbBookSource.isChecked = selected.contains(item)
upCheckSourceMessage(binding, item)
upShowExplore(ivExplore, item)
upSourceHost(binding, holder.layoutPosition)
} else {
for (i in payloads.indices) {
val bundle = payloads[i] as Bundle
Expand All @@ -106,6 +109,7 @@ class BookSourceAdapter(context: Context, val callBack: CallBack) :
"upExplore" -> upShowExplore(ivExplore, item)
"selected" -> cbBookSource.isChecked = selected.contains(item)
"checkSourceMessage" -> upCheckSourceMessage(binding, item)
"upSourceHost" -> upSourceHost(binding, holder.layoutPosition)
}
}
}
Expand Down Expand Up @@ -148,6 +152,9 @@ class BookSourceAdapter(context: Context, val callBack: CallBack) :

override fun onCurrentListChanged() {
callBack.upCountView()
if (showSourceHost) {
notifyItemRangeChanged(0, itemCount, bundleOf("upSourceHost" to null))
}
}

private fun showMenu(view: View, position: Int) {
Expand Down Expand Up @@ -233,6 +240,15 @@ class BookSourceAdapter(context: Context, val callBack: CallBack) :
if (isFinalMessage || isEmpty || !Debug.isChecking) View.GONE else View.VISIBLE
}

private fun upSourceHost(binding: ItemBookSourceBinding, position: Int) = binding.run {
if (showSourceHost && isItemHeader(position)) {
tvHostText.text = getHeaderText(position)
tvHostText.visible()
} else {
tvHostText.gone()
}
}

fun selectAll() {
getItems().forEach {
selected.add(it)
Expand Down Expand Up @@ -272,6 +288,18 @@ class BookSourceAdapter(context: Context, val callBack: CallBack) :
callBack.upCountView()
}

fun getHeaderText(position: Int): String {
val source = getItem(position)!!
return callBack.getSourceHost(source.bookSourceUrl)
}

fun isItemHeader(position: Int): Boolean {
if (position == 0) return true
val lastHost = getHeaderText(position - 1)
val curHost = getHeaderText(position)
return lastHost != curHost
}

override fun swap(srcPosition: Int, targetPosition: Int): Boolean {
val srcItem = getItem(srcPosition)
val targetItem = getItem(targetPosition)
Expand Down Expand Up @@ -344,5 +372,6 @@ class BookSourceAdapter(context: Context, val callBack: CallBack) :
fun enable(enable: Boolean, bookSource: BookSourcePart)
fun enableExplore(enable: Boolean, bookSource: BookSourcePart)
fun upCountView()
fun getSourceHost(origin: String): String
}
}
16 changes: 16 additions & 0 deletions app/src/main/java/io/legado/app/ui/rss/article/RssSortActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import io.legado.app.R
import io.legado.app.base.VMBaseActivity
import io.legado.app.databinding.ActivityRssArtivlesBinding
import io.legado.app.help.source.sortUrls
import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.theme.accentColor
import io.legado.app.ui.login.SourceLoginActivity
import io.legado.app.ui.rss.source.edit.RssSourceEditActivity
Expand Down Expand Up @@ -90,6 +91,10 @@ class RssSortActivity : VMBaseActivity<ActivityRssArtivlesBinding, RssSortViewMo
viewModel.switchLayout()
upFragments()
}

R.id.menu_del_read_record -> {
delReadRecord()
}
}
return super.onCompatOptionsItemSelected(item)
}
Expand All @@ -109,6 +114,17 @@ class RssSortActivity : VMBaseActivity<ActivityRssArtivlesBinding, RssSortViewMo
}
}

private fun delReadRecord() {
alert(R.string.draw) {
val countRead = viewModel.countRead()
setMessage(getString(R.string.sure_del) + "\n" + countRead + " " + getString(R.string.read_record))
noButton()
yesButton(){
viewModel.delReadRecord()
}
}
}

private fun setSourceVariable() {
lifecycleScope.launch {
val source = viewModel.rssSource
Expand Down
10 changes: 10 additions & 0 deletions app/src/main/java/io/legado/app/ui/rss/article/RssSortViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,14 @@ class RssSortViewModel(application: Application) : BaseViewModel(application) {
}
}

fun countRead() : Int{
return appDb.rssArticleDao.countRead
}

fun delReadRecord() {
execute {
appDb.rssArticleDao.deleteRecord()
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import android.view.SubMenu
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentStatePagerAdapter
import androidx.lifecycle.lifecycleScope
import androidx.viewpager.widget.ViewPager
import io.legado.app.R
import io.legado.app.base.BaseActivity
import io.legado.app.constant.AppLog
Expand All @@ -35,7 +34,6 @@ class RssFavoritesActivity : BaseActivity<ActivityRssFavoritesBinding>() {
private val adapter by lazy { TabFragmentPageAdapter() }
private var groupList = mutableListOf<String>()
private var groupsMenu: SubMenu? = null
private var currentGroup = ""

override fun onActivityCreated(savedInstanceState: Bundle?) {
initView()
Expand All @@ -44,21 +42,6 @@ class RssFavoritesActivity : BaseActivity<ActivityRssFavoritesBinding>() {

private fun initView() {
binding.viewPager.adapter = adapter
binding.viewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
override fun onPageScrolled(
position: Int,
positionOffset: Float,
positionOffsetPixels: Int
) {
}

override fun onPageSelected(position: Int) {
currentGroup = groupList[position]
}

override fun onPageScrollStateChanged(state: Int) {}

})
binding.tabLayout.setupWithViewPager(binding.viewPager)
binding.tabLayout.setSelectedTabIndicatorColor(accentColor)
}
Expand Down Expand Up @@ -105,12 +88,6 @@ class RssFavoritesActivity : BaseActivity<ActivityRssFavoritesBinding>() {
upGroupsMenu()
}
adapter.notifyDataSetChanged()
val item = groupList.indexOf(currentGroup)
if (item > -1) {
binding.viewPager.setCurrentItem(item)
} else if (groupList.isNotEmpty()) {
currentGroup = groupList[binding.viewPager.currentItem]
}
}
}
}
Expand All @@ -119,7 +96,7 @@ class RssFavoritesActivity : BaseActivity<ActivityRssFavoritesBinding>() {
alert(R.string.draw) {
val item = binding.viewPager.currentItem
val group = groupList[item]
setMessage(getString(R.string.sure_del) + "\n" + group + " 分组的收藏")
setMessage(getString(R.string.sure_del) + "\n<" + group + ">" + getString(R.string.group))
noButton()
yesButton {
appDb.rssStarDao.deleteByGroup(group)
Expand All @@ -129,7 +106,7 @@ class RssFavoritesActivity : BaseActivity<ActivityRssFavoritesBinding>() {

private fun deleteAll() {
alert(R.string.draw) {
setMessage(getString(R.string.sure_del) + "\n" + "所有收藏")
setMessage(getString(R.string.sure_del) + "\n<" + getString(R.string.all) + ">" + getString(R.string.favorite))
noButton()
yesButton {
appDb.rssStarDao.deleteAll()
Expand Down
20 changes: 18 additions & 2 deletions app/src/main/java/io/legado/app/utils/NetworkUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,19 @@ object NetworkUtils {
}.getOrDefault(baseUrl)
}

fun getSubDomainOrNull(url: String): String? {
val baseUrl = getBaseUrl(url) ?: return null
return kotlin.runCatching {
val mURL = URL(baseUrl)
val host: String = mURL.host
//mURL.scheme https/http
//判断是否为ip
if (isIPAddress(host)) return host
//PublicSuffixDatabase处理域名
PublicSuffixDatabase.get().getEffectiveTldPlusOne(host) ?: host
}.getOrDefault(null)
}

fun getDomain(url: String): String {
val baseUrl = getBaseUrl(url) ?: return url
return kotlin.runCatching {
Expand Down Expand Up @@ -219,14 +232,17 @@ object NetworkUtils {
* @return True if the input parameter is a valid IPv4 address.
*/
fun isIPv4Address(input: String?): Boolean {
return input != null && Validator.isIpv4(input)
return input != null && input.isNotEmpty()
&& input[0] in '1'..'9'
&& input.count { it == '.' } == 3
&& Validator.isIpv4(input)
}

/**
* Check if valid IPV6 address.
*/
fun isIPv6Address(input: String?): Boolean {
return input != null && Validator.isIpv6(input)
return input != null && input.contains(":") && Validator.isIpv6(input)
}

/**
Expand Down
32 changes: 28 additions & 4 deletions app/src/main/res/layout/item_book_source.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,33 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:clipToPadding="false"
android:padding="16dp"
android:scrollbars="none">

<io.legado.app.ui.widget.text.AccentTextView
android:id="@+id/tv_host_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="-12dp"
android:layout_marginBottom="16dp"
android:scrollbars="none"
android:singleLine="true"
android:textSize="16sp"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@id/barrier"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="host"
tools:visibility="visible" />

<androidx.constraintlayout.widget.Barrier
android:id="@+id/barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="top"
app:constraint_referenced_ids="cb_book_source,swt_enabled,iv_edit,iv_menu_more" />

<io.legado.app.lib.theme.view.ThemeCheckBox
android:id="@+id/cb_book_source"
android:layout_width="0dp"
Expand All @@ -19,7 +43,7 @@
app:layout_constraintBottom_toTopOf="@id/iv_debug_text"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/swt_enabled"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintTop_toBottomOf="@id/barrier"
tools:ignore="TouchTargetSizeCheck"
tools:text="@string/book_source" />

Expand All @@ -32,7 +56,7 @@
android:scrollbars="none"
app:layout_constraintBottom_toTopOf="@id/iv_debug_text"
app:layout_constraintRight_toLeftOf="@id/iv_edit"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintTop_toBottomOf="@id/barrier"
tools:ignore="RtlHardcoded,RtlSymmetry,TouchTargetSizeCheck" />

<androidx.appcompat.widget.AppCompatImageView
Expand All @@ -48,7 +72,7 @@
android:tint="@color/primaryText"
app:layout_constraintBottom_toTopOf="@id/iv_debug_text"
app:layout_constraintRight_toLeftOf="@id/iv_menu_more"
app:layout_constraintTop_toTopOf="parent" />
app:layout_constraintTop_toBottomOf="@id/barrier" />

<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_menu_more"
Expand All @@ -62,7 +86,7 @@
android:tint="@color/primaryText"
app:layout_constraintBottom_toTopOf="@id/iv_debug_text"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintTop_toBottomOf="@id/barrier"
tools:ignore="RtlHardcoded" />

<io.legado.app.ui.widget.image.CircleImageView
Expand Down
Loading

0 comments on commit a460716

Please sign in to comment.