RSS快速添加对话框

pull/36/head
mabdc 5 years ago
commit 066460ac15
  1. 4
      app/src/main/java/io/legado/app/ui/book/source/debug/SourceDebugActivity.kt
  2. 146
      app/src/main/java/io/legado/app/ui/book/source/edit/SourceEditActivity.kt
  3. 26
      app/src/main/java/io/legado/app/ui/main/rss/RssFragment.kt
  4. 64
      app/src/main/java/io/legado/app/ui/rss/article/RssArticlesActivity.kt
  5. 4
      app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt
  6. 14
      app/src/main/java/io/legado/app/ui/rss/source/debug/RssSourceDebugActivity.kt
  7. 62
      app/src/main/java/io/legado/app/ui/rss/source/edit/RssSourceEditActivity.kt
  8. 24
      app/src/main/java/io/legado/app/ui/rss/source/edit/RssSourceEditViewModel.kt
  9. 2
      app/src/main/res/layout/activity_rss_source_edit.xml
  10. 0
      app/src/main/res/layout/activity_source_debug.xml
  11. 10
      app/src/main/res/layout/item_rss.xml

@ -12,13 +12,13 @@ import io.legado.app.lib.theme.ATH
import io.legado.app.lib.theme.accentColor
import io.legado.app.ui.qrcode.QrCodeActivity
import io.legado.app.utils.getViewModel
import kotlinx.android.synthetic.main.activity_book_source_debug.*
import kotlinx.android.synthetic.main.activity_source_debug.*
import kotlinx.android.synthetic.main.view_search.*
import kotlinx.coroutines.launch
import org.jetbrains.anko.startActivityForResult
import org.jetbrains.anko.toast
class SourceDebugActivity : VMBaseActivity<SourceDebugModel>(R.layout.activity_book_source_debug) {
class SourceDebugActivity : VMBaseActivity<SourceDebugModel>(R.layout.activity_source_debug) {
override val viewModel: SourceDebugModel
get() = getViewModel(SourceDebugModel::class.java)

@ -79,20 +79,14 @@ class SourceEditActivity :
override fun onCompatOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.menu_save -> {
val bookSource = getSource()
if (bookSource == null) {
toast("书源名称和URL不能为空")
} else {
viewModel.save(bookSource) { setResult(Activity.RESULT_OK); finish() }
getSource()?.let {
viewModel.save(it) { setResult(Activity.RESULT_OK); finish() }
}
}
R.id.menu_debug_source -> {
val bookSource = getSource()
if (bookSource == null) {
toast("书源名称和URL不能为空")
} else {
viewModel.save(bookSource) {
startActivity<SourceDebugActivity>(Pair("key", bookSource.bookSourceUrl))
getSource()?.let {
viewModel.save(it) {
startActivity<SourceDebugActivity>(Pair("key", it.bookSourceUrl))
}
}
}
@ -147,7 +141,7 @@ class SourceEditActivity :
}
//基本信息
sourceEntities.clear()
searchEntities.apply {
sourceEntities.apply {
add(EditEntity("bookSourceUrl", bookSource?.bookSourceUrl, R.string.book_source_url))
add(EditEntity("bookSourceName", bookSource?.bookSourceName, R.string.book_source_name))
add(
@ -249,84 +243,76 @@ class SourceEditActivity :
val bookInfoRule = BookInfoRule()
val tocRule = TocRule()
val contentRule = ContentRule()
for (entity in sourceEntities) {
with(entity) {
when (key) {
"bookSourceUrl" -> value?.let { source.bookSourceUrl = it } ?: return null
"bookSourceName" -> value?.let { source.bookSourceName = it } ?: return null
"bookSourceGroup" -> source.bookSourceGroup = value
"loginUrl" -> source.loginUrl = value
"bookUrlPattern" -> source.bookUrlPattern = value
"header" -> source.header = value
}
sourceEntities.forEach {
when (it.key) {
"bookSourceUrl" -> source.bookSourceUrl = it.value ?: ""
"bookSourceName" -> source.bookSourceName = it.value ?: ""
"bookSourceGroup" -> source.bookSourceGroup = it.value
"loginUrl" -> source.loginUrl = it.value
"bookUrlPattern" -> source.bookUrlPattern = it.value
"header" -> source.header = it.value
}
}
for (entity in searchEntities) {
with(entity) {
when (key) {
"searchUrl" -> source.searchUrl = value
"bookList" -> searchRule.bookList = value
"name" -> searchRule.name = value
"author" -> searchRule.author = value
"kind" -> searchRule.kind = value
"intro" -> searchRule.intro = value
"updateTime" -> searchRule.updateTime = value
"wordCount" -> searchRule.wordCount = value
"lastChapter" -> searchRule.lastChapter = value
"coverUrl" -> searchRule.coverUrl = value
"bookUrl" -> searchRule.bookUrl = value
}
if (source.bookSourceUrl.isBlank() || source.bookSourceName.isBlank()) {
toast("书源名称和URL不能为空")
return null
}
searchEntities.forEach {
when (it.key) {
"searchUrl" -> source.searchUrl = it.value
"bookList" -> searchRule.bookList = it.value
"name" -> searchRule.name = it.value
"author" -> searchRule.author = it.value
"kind" -> searchRule.kind = it.value
"intro" -> searchRule.intro = it.value
"updateTime" -> searchRule.updateTime = it.value
"wordCount" -> searchRule.wordCount = it.value
"lastChapter" -> searchRule.lastChapter = it.value
"coverUrl" -> searchRule.coverUrl = it.value
"bookUrl" -> searchRule.bookUrl = it.value
}
}
for (entity in findEntities) {
with(entity) {
when (key) {
"exploreUrl" -> source.exploreUrl = value
"bookList" -> exploreRule.bookList = value
"name" -> exploreRule.name = value
"author" -> exploreRule.author = value
"kind" -> exploreRule.kind = value
"intro" -> exploreRule.intro = value
"updateTime" -> exploreRule.updateTime = value
"wordCount" -> exploreRule.wordCount = value
"lastChapter" -> exploreRule.lastChapter = value
"coverUrl" -> exploreRule.coverUrl = value
"bookUrl" -> exploreRule.bookUrl = value
}
findEntities.forEach {
when (it.key) {
"exploreUrl" -> source.exploreUrl = it.value
"bookList" -> exploreRule.bookList = it.value
"name" -> exploreRule.name = it.value
"author" -> exploreRule.author = it.value
"kind" -> exploreRule.kind = it.value
"intro" -> exploreRule.intro = it.value
"updateTime" -> exploreRule.updateTime = it.value
"wordCount" -> exploreRule.wordCount = it.value
"lastChapter" -> exploreRule.lastChapter = it.value
"coverUrl" -> exploreRule.coverUrl = it.value
"bookUrl" -> exploreRule.bookUrl = it.value
}
}
for (entity in infoEntities) {
with(entity) {
when (key) {
"init" -> bookInfoRule.init = value
"name" -> bookInfoRule.name = value
"author" -> bookInfoRule.author = value
"kind" -> bookInfoRule.kind = value
"intro" -> bookInfoRule.intro = value
"updateTime" -> bookInfoRule.updateTime = value
"wordCount" -> bookInfoRule.wordCount = value
"lastChapter" -> bookInfoRule.lastChapter = value
"coverUrl" -> bookInfoRule.coverUrl = value
"tocUrl" -> bookInfoRule.tocUrl = value
}
infoEntities.forEach {
when (it.key) {
"init" -> bookInfoRule.init = it.value
"name" -> bookInfoRule.name = it.value
"author" -> bookInfoRule.author = it.value
"kind" -> bookInfoRule.kind = it.value
"intro" -> bookInfoRule.intro = it.value
"updateTime" -> bookInfoRule.updateTime = it.value
"wordCount" -> bookInfoRule.wordCount = it.value
"lastChapter" -> bookInfoRule.lastChapter = it.value
"coverUrl" -> bookInfoRule.coverUrl = it.value
"tocUrl" -> bookInfoRule.tocUrl = it.value
}
}
for (entity in tocEntities) {
with(entity) {
when (key) {
"chapterList" -> tocRule.chapterList = value
"chapterName" -> tocRule.chapterName = value
"chapterUrl" -> tocRule.chapterUrl = value
"nextTocUrl" -> tocRule.nextTocUrl = value
}
tocEntities.forEach {
when (it.key) {
"chapterList" -> tocRule.chapterList = it.value
"chapterName" -> tocRule.chapterName = it.value
"chapterUrl" -> tocRule.chapterUrl = it.value
"nextTocUrl" -> tocRule.nextTocUrl = it.value
}
}
for (entity in contentEntities) {
with(entity) {
when (key) {
"content" -> contentRule.content = value
"nextContentUrl" -> contentRule.nextContentUrl = value
}
contentEntities.forEach {
when (it.key) {
"content" -> contentRule.content = it.value
"nextContentUrl" -> contentRule.nextContentUrl = it.value
}
}
source.ruleSearch = GSON.toJson(searchRule)

@ -1,20 +1,28 @@
package io.legado.app.ui.main.rss
import android.os.Bundle
import android.util.Log
import android.view.Gravity
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.EditText
import android.widget.LinearLayout
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.GridLayoutManager
import io.legado.app.App
import io.legado.app.R
import io.legado.app.base.BaseFragment
import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.dialogs.cancelButton
import io.legado.app.lib.dialogs.yesButton
import io.legado.app.lib.theme.ATH
import io.legado.app.ui.rss.article.RssArticlesActivity
import io.legado.app.ui.rss.source.manage.RssSourceActivity
import io.legado.app.utils.startActivity
import kotlinx.android.synthetic.main.fragment_rss.*
import kotlinx.android.synthetic.main.view_title_bar.*
import org.jetbrains.anko.padding
class RssFragment : BaseFragment(R.layout.fragment_rss),
RssAdapter.CallBack {
@ -36,7 +44,23 @@ class RssFragment : BaseFragment(R.layout.fragment_rss),
when (item.itemId) {
R.id.menu_rss_config -> startActivity<RssSourceActivity>()
R.id.menu_rss_add -> {
startActivity<RssArticlesActivity>()
alert {
title = "快速添加并预览"
val layout = LinearLayout(activity)
val urlEdit = EditText(activity)
urlEdit.hint = "输入RSS地址"
urlEdit.width = 800
layout.gravity = Gravity.CENTER
layout.addView(urlEdit)
customView = layout
cancelButton{
Log.i("RSS","Quick Add URL cancel")
}
yesButton{
Log.i("RSS","Quick Add URL: ${urlEdit.text}")
startActivity<RssArticlesActivity>("QuickAddURL" to urlEdit.text.toString().trim())
}
}.show()
}
}
}

@ -1,5 +1,6 @@
package io.legado.app.ui.rss.article
import android.app.ProgressDialog
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
@ -13,68 +14,47 @@ import io.legado.app.model.rss.RssParser
import io.legado.app.ui.rss.read.ReadRssActivity
import kotlinx.android.synthetic.main.item_rss.view.*
import org.jetbrains.anko.*
import org.jetbrains.anko.sdk27.listeners.onClick
import org.jetbrains.anko.sdk27.listeners.onItemClick
import java.net.URL
class RssArticlesActivity : AppCompatActivity() {
val zhihu_dayli = "https://rsshub.app/zhihu/daily"
val nytimes = "https://rsshub.app/nytimes/dual"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
var articles = mutableListOf<RssArticle>()
val adapter = ArticleAdapter(articles, this)
verticalLayout {
val url = editText {
hint = "请输入RSS地址"
}
fun loadRss(urlString:String){
Thread {
val xml = URL(urlString).readText()
articles = RssParser.parseXML(xml)
runOnUiThread {
adapter.articles = articles
adapter.notifyDataSetChanged()
}
}.start()
}
button("解析") {
onClick {
val urlString = url.text.toString().trim()
if(urlString != ""){
loadRss(urlString)
}
}
}
button("知乎日报") {
onClick {
loadRss(zhihu_dayli)
}
}
button("纽约时报") {
onClick {
loadRss(nytimes)
}
}
if (intent.hasExtra("QuickAddURL")) {
// 处理从 快速添加并预览 跳转到这个页面
val urlString = intent.getStringExtra("QuickAddURL")
val adapter = ArticleAdapter(mutableListOf<RssArticle>(), this)
listView {
this.adapter = adapter
onItemClick { p0, p1, p2, p3 ->
startActivity<ReadRssActivity>("description" to articles[p2].description)
startActivity<ReadRssActivity>("description" to adapter.articles[p2].description)
}
}
val loading = ProgressDialog(this@RssArticlesActivity)
loading.setMessage("加载中...")
loading.show()
Thread {
val xml = URL(urlString).readText()
val articles = RssParser.parseXML(xml)
runOnUiThread {
adapter.articles = articles
adapter.notifyDataSetChanged()
loading.dismiss()
}
}.start()
}
}
}
class ArticleAdapter(var articles: List<RssArticle>, var context: Context) : BaseAdapter() {
class ArticleAdapter(var articles: MutableList<RssArticle>, var context: Context) : BaseAdapter() {
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
val item_rss= LayoutInflater.from(context).inflate(R.layout.item_rss, null)
val item_rss = LayoutInflater.from(context).inflate(R.layout.item_rss, null)
val article = articles[position]
item_rss.title.text = article.title
item_rss.pub_date.text = article.pubDate
if(article.author != null && article.author != ""){
if (article.author != null && article.author != "") {
item_rss.author.text = article.author
} else{
} else {
item_rss.author.text = article.link
}
return item_rss

@ -9,7 +9,7 @@ class ReadRssActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_read_rss)
val s = intent.getStringExtra("description")
webView.loadData("<style>img{max-width:100%}</style>"+s,"text/html","utf-8")
val description = intent.getStringExtra("description")
webView.loadData("<style>img{max-width:100%}</style>$description","text/html", "utf-8")
}
}

@ -1,2 +1,16 @@
package io.legado.app.ui.rss.source.debug
import android.os.Bundle
import io.legado.app.R
import io.legado.app.base.BaseActivity
class RssSourceDebugActivity : BaseActivity(R.layout.activity_source_debug, false) {
override fun onActivityCreated(savedInstanceState: Bundle?) {
}
}

@ -1,9 +1,14 @@
package io.legado.app.ui.rss.source.edit
import android.app.Activity
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.graphics.Rect
import android.os.Bundle
import android.view.Gravity
import android.view.Menu
import android.view.MenuItem
import android.view.ViewTreeObserver
import android.widget.EditText
import android.widget.PopupWindow
@ -15,10 +20,13 @@ import io.legado.app.constant.AppConst
import io.legado.app.data.entities.EditEntity
import io.legado.app.data.entities.RssSource
import io.legado.app.lib.theme.ATH
import io.legado.app.ui.rss.source.debug.RssSourceDebugActivity
import io.legado.app.ui.widget.KeyboardToolPop
import io.legado.app.utils.GSON
import io.legado.app.utils.getViewModel
import kotlinx.android.synthetic.main.activity_book_source_edit.*
import org.jetbrains.anko.displayMetrics
import org.jetbrains.anko.startActivity
import kotlin.math.abs
class RssSourceEditActivity :
@ -28,7 +36,7 @@ class RssSourceEditActivity :
private var mSoftKeyboardTool: PopupWindow? = null
private var mIsSoftKeyBoardShowing = false
private lateinit var adapter: RssSourceEditAdapter
private val adapter = RssSourceEditAdapter()
private val sourceEntities: ArrayList<EditEntity> = ArrayList()
override val viewModel: RssSourceEditViewModel
@ -62,6 +70,34 @@ class RssSourceEditActivity :
return super.onCompatCreateOptionsMenu(menu)
}
override fun onCompatOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.menu_save -> {
getRssSource()?.let {
viewModel.save(it) {
setResult(Activity.RESULT_OK)
finish()
}
}
}
R.id.menu_debug_source -> {
getRssSource()?.let {
viewModel.save(it) {
startActivity<RssSourceDebugActivity>(Pair("key", it.sourceUrl))
}
}
}
R.id.menu_copy_source -> {
GSON.toJson(getRssSource())?.let { sourceStr ->
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager?
clipboard?.primaryClip = ClipData.newPlainText(null, sourceStr)
}
}
R.id.menu_paste_source -> viewModel.pasteSource()
}
return super.onCompatOptionsItemSelected(item)
}
private fun initView() {
ATH.applyEdgeEffectColor(recycler_view)
mSoftKeyboardTool = KeyboardToolPop(this, AppConst.keyboardToolChars, this)
@ -98,7 +134,31 @@ class RssSourceEditActivity :
add(EditEntity("ruleContent", rssSource?.ruleContent, R.string.rss_rule_content))
add(EditEntity("ruleLink", rssSource?.ruleLink, R.string.rss_rule_link))
}
adapter.editEntities = sourceEntities
}
private fun getRssSource(): RssSource? {
val source = viewModel.sourceLiveData.value ?: RssSource()
sourceEntities.forEach {
when (it.key) {
"sourceName" -> source.sourceName = it.value ?: ""
"sourceUrl" -> source.sourceName = it.value ?: ""
"iconUrl" -> source.sourceName = it.value ?: ""
"ruleTitle" -> source.sourceName = it.value ?: ""
"ruleAuthor" -> source.sourceName = it.value ?: ""
"ruleGuid" -> source.sourceName = it.value ?: ""
"rulePubDate" -> source.sourceName = it.value ?: ""
"ruleCategories" -> source.sourceName = it.value ?: ""
"ruleDescription" -> source.sourceName = it.value ?: ""
"ruleImage" -> source.sourceName = it.value ?: ""
"ruleContent" -> source.sourceName = it.value ?: ""
"ruleLink" -> source.sourceName = it.value ?: ""
}
}
if (source.sourceName.isBlank() || source.sourceName.isBlank()) {
return null
}
return source
}
override fun sendText(text: String) {

@ -1,10 +1,14 @@
package io.legado.app.ui.rss.source.edit
import android.app.Application
import android.content.ClipboardManager
import android.content.Context
import androidx.lifecycle.MutableLiveData
import io.legado.app.App
import io.legado.app.base.BaseViewModel
import io.legado.app.data.entities.RssSource
import io.legado.app.utils.GSON
import io.legado.app.utils.fromJsonObject
class RssSourceEditViewModel(application: Application) : BaseViewModel(application) {
@ -18,5 +22,25 @@ class RssSourceEditViewModel(application: Application) : BaseViewModel(applicati
}
}
fun save(rssSource: RssSource, success: (() -> Unit)) {
execute {
App.db.rssSourceDao().insert(rssSource)
}.onSuccess {
success()
}
}
fun pasteSource() {
execute {
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager?
clipboard?.primaryClip?.let {
if (it.itemCount > 0) {
val json = it.getItemAt(0).text.toString()
GSON.fromJsonObject<RssSource>(json)?.let { source ->
sourceLiveData.postValue(source)
} ?: toast("格式不对")
}
}
}
}
}

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/ll_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
@ -11,6 +12,7 @@
android:layout_height="wrap_content"
app:contentInsetStartWithNavigation="0dp"
app:displayHomeAsUp="true"
app:fitStatusBar="false"
app:title="@string/rss_source_edit" />
<androidx.recyclerview.widget.RecyclerView

@ -1,8 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:padding="12dp"
android:layout_margin="20dp"
android:padding="16dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="2dp"
@ -17,6 +16,7 @@
android:id="@+id/title"
android:textSize="18sp"
android:text="Title"
android:layout_gravity="center"
android:textStyle="bold"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
@ -24,7 +24,10 @@
<TextView
android:id="@+id/pub_date"
android:textSize="12sp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:text="2019-10-01 13:48:00"
android:layout_gravity="center"
android:textStyle="italic"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
@ -32,7 +35,8 @@
<TextView
android:id="@+id/author"
android:textSize="16sp"
android:text="Content"
android:text="author"
android:layout_gravity="center"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>

Loading…
Cancel
Save