diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index bc92ac136..23f5edd36 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -98,6 +98,7 @@
+
diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md
index 48dc30c5b..a067a90e9 100644
--- a/app/src/main/assets/updateLog.md
+++ b/app/src/main/assets/updateLog.md
@@ -3,6 +3,9 @@
* 旧版数据导入教程:
* 先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。
+**2020/01/10
+* 合并KKL369提交的代码
+
**2020/01/08**
* 导入本地源不再需要存储权限
diff --git a/app/src/main/java/io/legado/app/help/BookHelp.kt b/app/src/main/java/io/legado/app/help/BookHelp.kt
index 1ea320505..ff5ee0b7d 100644
--- a/app/src/main/java/io/legado/app/help/BookHelp.kt
+++ b/app/src/main/java/io/legado/app/help/BookHelp.kt
@@ -86,7 +86,7 @@ object BookHelp {
}
private fun getBookFolder(book: Book): String {
- val bookFolder = formatFolderName(book.name + book.bookUrl)
+ val bookFolder = formatFolderName(book.name + MD5Utils.md5Encode16(book.bookUrl))
return "${getBookCachePath()}${File.separator}$bookFolder"
}
@@ -108,6 +108,9 @@ object BookHelp {
?: ""
}
+ /**
+ * 找到相似度最高的章节
+ */
fun getDurChapterIndexByChapterTitle(
title: String?,
index: Int,
diff --git a/app/src/main/java/io/legado/app/lib/theme/ThemeStore.kt b/app/src/main/java/io/legado/app/lib/theme/ThemeStore.kt
index b3605e3e9..5335a6830 100644
--- a/app/src/main/java/io/legado/app/lib/theme/ThemeStore.kt
+++ b/app/src/main/java/io/legado/app/lib/theme/ThemeStore.kt
@@ -15,7 +15,7 @@ import io.legado.app.R
* @author Aidan Follestad (afollestad), Karim Abou Zeid (kabouzeid)
*/
class ThemeStore @SuppressLint("CommitPrefEdits")
-private constructor(private val mContext: Context) : ThemeStorePrefKeys, ThemeStoreInterface {
+private constructor(private val mContext: Context) : ThemeStoreInterface {
private val mEditor: SharedPreferences.Editor
init {
diff --git a/app/src/main/java/io/legado/app/lib/theme/ThemeStorePrefKeys.kt b/app/src/main/java/io/legado/app/lib/theme/ThemeStorePrefKeys.kt
index 4fd4540fc..c934ef176 100644
--- a/app/src/main/java/io/legado/app/lib/theme/ThemeStorePrefKeys.kt
+++ b/app/src/main/java/io/legado/app/lib/theme/ThemeStorePrefKeys.kt
@@ -3,8 +3,7 @@ package io.legado.app.lib.theme
/**
* @author Aidan Follestad (afollestad), Karim Abou Zeid (kabouzeid)
*/
-internal interface ThemeStorePrefKeys {
- companion object {
+object ThemeStorePrefKeys {
const val CONFIG_PREFS_KEY_DEFAULT = "app_themes"
const val IS_CONFIGURED_KEY = "is_configured"
@@ -27,5 +26,4 @@ internal interface ThemeStorePrefKeys {
const val KEY_APPLY_PRIMARYDARK_STATUSBAR = "apply_primarydark_statusbar"
const val KEY_APPLY_PRIMARY_NAVBAR = "apply_primary_navbar"
const val KEY_AUTO_GENERATE_PRIMARYDARK = "auto_generate_primarydark"
- }
}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/book/source/edit/BookSourceEditActivity.kt b/app/src/main/java/io/legado/app/ui/book/source/edit/BookSourceEditActivity.kt
index aeb83a093..cc63c93d4 100644
--- a/app/src/main/java/io/legado/app/ui/book/source/edit/BookSourceEditActivity.kt
+++ b/app/src/main/java/io/legado/app/ui/book/source/edit/BookSourceEditActivity.kt
@@ -26,9 +26,7 @@ import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.theme.ATH
import io.legado.app.ui.book.source.debug.BookSourceDebugActivity
import io.legado.app.ui.widget.KeyboardToolPop
-import io.legado.app.utils.GSON
-import io.legado.app.utils.applyTint
-import io.legado.app.utils.getViewModel
+import io.legado.app.utils.*
import kotlinx.android.synthetic.main.activity_book_source_edit.*
import org.jetbrains.anko.displayMetrics
import org.jetbrains.anko.startActivity
@@ -87,17 +85,11 @@ class BookSourceEditActivity :
}
}
R.id.menu_paste_source -> viewModel.pasteSource { upRecyclerView(it) }
- R.id.menu_share_str -> {
- GSON.toJson(getSource())?.let { sourceStr ->
- try {
- val textIntent = Intent(Intent.ACTION_SEND)
- textIntent.type = "text/plain"
- textIntent.putExtra(Intent.EXTRA_TEXT, sourceStr)
- startActivity(Intent.createChooser(textIntent, "Source Share"))
- } catch (e: Exception) {
- toast(R.string.can_not_share)
- }
- }
+ R.id.menu_share_str -> GSON.toJson(getSource())?.let { sourceStr ->
+ shareText(getString(R.string.share_book_source), sourceStr)
+ }
+ R.id.menu_share_qr -> GSON.toJson(getSource())?.let { sourceStr ->
+ shareWithQr(getString(R.string.share_book_source), sourceStr)
}
R.id.menu_rule_summary -> {
try {
diff --git a/app/src/main/java/io/legado/app/ui/importbook/ImportBookActivity.kt b/app/src/main/java/io/legado/app/ui/importbook/ImportBookActivity.kt
new file mode 100644
index 000000000..8cbce52f8
--- /dev/null
+++ b/app/src/main/java/io/legado/app/ui/importbook/ImportBookActivity.kt
@@ -0,0 +1,19 @@
+package io.legado.app.ui.importbook
+
+import android.os.Bundle
+import io.legado.app.R
+import io.legado.app.base.VMBaseActivity
+import io.legado.app.utils.getViewModel
+
+
+class ImportBookActivity : VMBaseActivity(R.layout.activity_import_book) {
+
+ override val viewModel: ImportBookViewModel
+ get() = getViewModel(ImportBookViewModel::class.java)
+
+
+ override fun onActivityCreated(savedInstanceState: Bundle?) {
+
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/importbook/ImportBookViewModel.kt b/app/src/main/java/io/legado/app/ui/importbook/ImportBookViewModel.kt
new file mode 100644
index 000000000..1865f1be8
--- /dev/null
+++ b/app/src/main/java/io/legado/app/ui/importbook/ImportBookViewModel.kt
@@ -0,0 +1,10 @@
+package io.legado.app.ui.importbook
+
+import android.app.Application
+import io.legado.app.base.BaseViewModel
+
+
+class ImportBookViewModel(application: Application) : BaseViewModel(application) {
+
+
+}
\ No newline at end of file
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 037364296..24b49a5c6 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
@@ -1,7 +1,6 @@
package io.legado.app.ui.rss.read
import android.annotation.SuppressLint
-import android.content.Intent
import android.os.Bundle
import android.view.KeyEvent
import android.view.Menu
@@ -15,8 +14,8 @@ import io.legado.app.lib.theme.DrawableUtils
import io.legado.app.lib.theme.primaryTextColor
import io.legado.app.utils.NetworkUtils
import io.legado.app.utils.getViewModel
+import io.legado.app.utils.shareText
import kotlinx.android.synthetic.main.activity_rss_read.*
-import org.jetbrains.anko.toast
class ReadRssActivity : VMBaseActivity(R.layout.activity_rss_read),
ReadRssViewModel.CallBack {
@@ -25,6 +24,7 @@ class ReadRssActivity : VMBaseActivity(R.layout.activity_rss_r
get() = getViewModel(ReadRssViewModel::class.java)
private var starMenuItem: MenuItem? = null
+ private var ttsMenuItem: MenuItem? = null
override fun onActivityCreated(savedInstanceState: Bundle?) {
viewModel.callBack = this
@@ -37,6 +37,7 @@ class ReadRssActivity : VMBaseActivity(R.layout.activity_rss_r
override fun onCompatCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.rss_read, menu)
starMenuItem = menu.findItem(R.id.menu_rss_star)
+ ttsMenuItem = menu.findItem(R.id.menu_aloud)
upStarMenu()
return super.onCompatCreateOptionsMenu(menu)
}
@@ -47,6 +48,7 @@ class ReadRssActivity : VMBaseActivity(R.layout.activity_rss_r
R.id.menu_share_it -> viewModel.rssArticle?.let {
shareText("链接分享", it.link)
}
+ R.id.menu_aloud -> readAloud()
}
return super.onCompatOptionsItemSelected(item)
}
@@ -97,6 +99,17 @@ class ReadRssActivity : VMBaseActivity(R.layout.activity_rss_r
DrawableUtils.setTint(starMenuItem?.icon, primaryTextColor)
}
+ override fun upTtsMenu(isPlaying: Boolean) {
+ if (isPlaying) {
+ ttsMenuItem?.setIcon(R.drawable.ic_stop_black_24dp)
+ ttsMenuItem?.setTitle(R.string.aloud_stop)
+ } else {
+ ttsMenuItem?.setIcon(R.drawable.ic_volume_up)
+ ttsMenuItem?.setTitle(R.string.read_aloud)
+ }
+ DrawableUtils.setTint(ttsMenuItem?.icon, primaryTextColor)
+ }
+
override fun onKeyLongPress(keyCode: Int, event: KeyEvent?): Boolean {
when (keyCode) {
KeyEvent.KEYCODE_BACK -> {
@@ -121,14 +134,10 @@ class ReadRssActivity : VMBaseActivity(R.layout.activity_rss_r
return super.onKeyUp(keyCode, event)
}
- private fun shareText(title: String, text: String) {
- try {
- val textIntent = Intent(Intent.ACTION_SEND)
- textIntent.type = "text/plain"
- textIntent.putExtra(Intent.EXTRA_TEXT, text)
- startActivity(Intent.createChooser(textIntent, title))
- } catch (e: Exception) {
- toast(R.string.can_not_share)
+ private fun readAloud() {
+ webView.evaluateJavascript("document.documentElement.outerHTML") {
+ viewModel.readAloud(it)
}
}
+
}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/rss/read/ReadRssViewModel.kt b/app/src/main/java/io/legado/app/ui/rss/read/ReadRssViewModel.kt
index 98355d7d8..83a88091e 100644
--- a/app/src/main/java/io/legado/app/ui/rss/read/ReadRssViewModel.kt
+++ b/app/src/main/java/io/legado/app/ui/rss/read/ReadRssViewModel.kt
@@ -2,21 +2,28 @@ package io.legado.app.ui.rss.read
import android.app.Application
import android.content.Intent
+import android.speech.tts.TextToSpeech
+import android.speech.tts.UtteranceProgressListener
import androidx.lifecycle.MutableLiveData
import io.legado.app.App
+import io.legado.app.R
import io.legado.app.base.BaseViewModel
import io.legado.app.data.entities.RssArticle
import io.legado.app.data.entities.RssSource
import io.legado.app.model.Rss
import io.legado.app.model.analyzeRule.AnalyzeUrl
+import kotlinx.coroutines.launch
+import java.util.*
-class ReadRssViewModel(application: Application) : BaseViewModel(application) {
+class ReadRssViewModel(application: Application) : BaseViewModel(application),
+ TextToSpeech.OnInitListener {
var callBack: CallBack? = null
var rssSource: RssSource? = null
var rssArticle: RssArticle? = null
val contentLiveData = MutableLiveData()
val urlLiveData = MutableLiveData()
var star = false
+ var textToSpeech: TextToSpeech = TextToSpeech(context, this)
fun initData(intent: Intent) {
execute {
@@ -91,7 +98,48 @@ class ReadRssViewModel(application: Application) : BaseViewModel(application) {
}
}
+ override fun onInit(status: Int) {
+ launch {
+ if (status == TextToSpeech.SUCCESS) {
+ textToSpeech.language = Locale.CHINA
+ textToSpeech.setOnUtteranceProgressListener(TTSUtteranceListener())
+ } else {
+ toast(R.string.tts_init_failed)
+ }
+ }
+ }
+
+ fun readAloud(text: String) {
+ textToSpeech.speak(text, TextToSpeech.QUEUE_FLUSH, null, "rss")
+ }
+
+ override fun onCleared() {
+ super.onCleared()
+ textToSpeech.stop()
+ textToSpeech.shutdown()
+ }
+
+ /**
+ * 朗读监听
+ */
+ private inner class TTSUtteranceListener : UtteranceProgressListener() {
+
+ override fun onStart(s: String) {
+ callBack?.upTtsMenu(true)
+ }
+
+ override fun onDone(s: String) {
+ callBack?.upTtsMenu(false)
+ }
+
+ override fun onError(s: String) {
+
+ }
+
+ }
+
interface CallBack {
fun upStarMenu()
+ fun upTtsMenu(isPlaying: Boolean)
}
}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/rss/source/edit/RssSourceEditActivity.kt b/app/src/main/java/io/legado/app/ui/rss/source/edit/RssSourceEditActivity.kt
index d1704848e..1ae70df5d 100644
--- a/app/src/main/java/io/legado/app/ui/rss/source/edit/RssSourceEditActivity.kt
+++ b/app/src/main/java/io/legado/app/ui/rss/source/edit/RssSourceEditActivity.kt
@@ -22,9 +22,7 @@ import io.legado.app.lib.dialogs.alert
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.applyTint
-import io.legado.app.utils.getViewModel
+import io.legado.app.utils.*
import kotlinx.android.synthetic.main.activity_rss_source_edit.*
import org.jetbrains.anko.displayMetrics
import org.jetbrains.anko.startActivity
@@ -103,6 +101,12 @@ class RssSourceEditActivity :
}
}
R.id.menu_paste_source -> viewModel.pasteSource { upRecyclerView(it) }
+ R.id.menu_share_str -> GSON.toJson(getRssSource())?.let { sourceStr ->
+ shareText(getString(R.string.share_rss_source), sourceStr)
+ }
+ R.id.menu_share_qr -> GSON.toJson(getRssSource())?.let { sourceStr ->
+ shareWithQr(getString(R.string.share_rss_source), sourceStr)
+ }
}
return super.onCompatOptionsItemSelected(item)
}
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 a0a4170d8..6ff51d379 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
@@ -88,16 +88,14 @@ class FontSelectDialog : DialogFragment(),
override fun onMenuItemClick(item: MenuItem?): Boolean {
when (item?.itemId) {
R.id.menu_default -> {
- val pf = parentFragment
- if (pf is CallBack) {
- if ("" != pf.curFontPath) {
- pf.selectFile("")
+ (parentFragment as? CallBack)?.let {
+ if (it.curFontPath != "") {
+ it.selectFile("")
}
}
- val activity = activity
- if (activity is CallBack) {
- if ("" != activity.curFontPath) {
- activity.selectFile("")
+ (activity as? CallBack)?.let {
+ if (it.curFontPath != "") {
+ it.selectFile("")
}
}
dismiss()
@@ -131,7 +129,7 @@ class FontSelectDialog : DialogFragment(),
@SuppressLint("DefaultLocale")
private fun getFontFiles(uri: Uri) {
launch(IO) {
- DocumentFile.fromTreeUri(requireContext(), uri)?.listFiles()?.forEach { file ->
+ DocumentFile.fromTreeUri(App.INSTANCE, uri)?.listFiles()?.forEach { file ->
if (file.name?.toLowerCase()?.matches(".*\\.[ot]tf".toRegex()) == true) {
DocumentUtils.readBytes(App.INSTANCE, file.uri)?.let {
FileHelp.getFile(fontFolder + file.name).writeBytes(it)
@@ -169,17 +167,15 @@ class FontSelectDialog : DialogFragment(),
}
override fun onClick(file: File) {
- file.absolutePath.let {
- val pf = parentFragment
- if (pf is CallBack) {
- if (it != pf.curFontPath) {
- pf.selectFile(it)
+ file.absolutePath.let { path ->
+ (parentFragment as? CallBack)?.let {
+ if (it.curFontPath != path) {
+ it.selectFile(path)
}
}
- val activity = activity
- if (activity is CallBack) {
- if (it != activity.curFontPath) {
- activity.selectFile(it)
+ (activity as? CallBack)?.let {
+ if (it.curFontPath != path) {
+ it.selectFile(path)
}
}
}
@@ -187,15 +183,9 @@ class FontSelectDialog : DialogFragment(),
}
override fun curFilePath(): String {
- val pf = parentFragment
- if (pf is CallBack) {
- return pf.curFontPath
- }
- val activity = activity
- if (activity is CallBack) {
- return activity.curFontPath
- }
- return ""
+ return (parentFragment as? CallBack)?.curFontPath
+ ?: (activity as? CallBack)?.curFontPath
+ ?: ""
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
diff --git a/app/src/main/java/io/legado/app/utils/ContextExtensions.kt b/app/src/main/java/io/legado/app/utils/ContextExtensions.kt
index f47a0a2a8..2ab88b481 100644
--- a/app/src/main/java/io/legado/app/utils/ContextExtensions.kt
+++ b/app/src/main/java/io/legado/app/utils/ContextExtensions.kt
@@ -1,13 +1,25 @@
package io.legado.app.utils
+import android.annotation.SuppressLint
import android.content.Context
+import android.content.Intent
import android.content.res.ColorStateList
+import android.graphics.Bitmap
import android.graphics.drawable.Drawable
import androidx.annotation.ColorRes
import androidx.annotation.DrawableRes
import androidx.core.content.ContextCompat
+import androidx.core.content.FileProvider
import androidx.core.content.edit
+import cn.bingoogolapple.qrcode.zxing.QRCodeEncoder
+import com.google.zxing.EncodeHintType
+import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel
+import io.legado.app.BuildConfig
+import io.legado.app.R
import org.jetbrains.anko.defaultSharedPreferences
+import org.jetbrains.anko.toast
+import java.io.File
+import java.io.FileOutputStream
fun Context.getPrefBoolean(key: String, defValue: Boolean = false) =
defaultSharedPreferences.getBoolean(key, defValue)
@@ -48,7 +60,8 @@ fun Context.getCompatColor(@ColorRes id: Int): Int = ContextCompat.getColor(this
fun Context.getCompatDrawable(@DrawableRes id: Int): Drawable? = ContextCompat.getDrawable(this, id)
-fun Context.getCompatColorStateList(@ColorRes id: Int): ColorStateList? = ContextCompat.getColorStateList(this, id)
+fun Context.getCompatColorStateList(@ColorRes id: Int): ColorStateList? =
+ ContextCompat.getColorStateList(this, id)
fun Context.getStatusBarHeight(): Int {
val resourceId = resources.getIdentifier("status_bar_height", "dimen", "android")
@@ -60,6 +73,48 @@ fun Context.getNavigationBarHeight(): Int {
return resources.getDimensionPixelSize(resourceId)
}
+fun Context.shareText(title: String, text: String) {
+ try {
+ val textIntent = Intent(Intent.ACTION_SEND)
+ textIntent.type = "text/plain"
+ textIntent.putExtra(Intent.EXTRA_TEXT, text)
+ startActivity(Intent.createChooser(textIntent, title))
+ } catch (e: Exception) {
+ toast(R.string.can_not_share)
+ }
+}
+
+@SuppressLint("SetWorldReadable")
+fun Context.shareWithQr(title: String, text: String) {
+ QRCodeEncoder.HINTS[EncodeHintType.ERROR_CORRECTION] = ErrorCorrectionLevel.L
+ val bitmap = QRCodeEncoder.syncEncodeQRCode(text, 600)
+ QRCodeEncoder.HINTS[EncodeHintType.ERROR_CORRECTION] = ErrorCorrectionLevel.H
+ if (bitmap == null) {
+ toast(R.string.text_too_long_qr_error)
+ } else {
+ try {
+ val file = File(externalCacheDir, "qr.png")
+ val fOut = FileOutputStream(file)
+ bitmap.compress(Bitmap.CompressFormat.PNG, 100, fOut)
+ fOut.flush()
+ fOut.close()
+ file.setReadable(true, false)
+ val contentUri = FileProvider.getUriForFile(
+ this,
+ "${BuildConfig.APPLICATION_ID}.fileProvider",
+ file
+ )
+ val intent = Intent(Intent.ACTION_SEND)
+ intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
+ intent.putExtra(Intent.EXTRA_STREAM, contentUri)
+ intent.type = "image/png"
+ startActivity(Intent.createChooser(intent, title))
+ } catch (e: Exception) {
+ toast(e.localizedMessage ?: "ERROR")
+ }
+ }
+}
+
val Context.isNightTheme: Boolean
get() = getPrefBoolean("isNightTheme")
@@ -67,4 +122,4 @@ val Context.isTransparentStatusBar: Boolean
get() = getPrefBoolean("transparentStatusBar", true)
val Context.isShowRSS: Boolean
- get() = getPrefBoolean("showRss", true)
\ No newline at end of file
+ get() = getPrefBoolean("showRss", true)
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_import_book.xml b/app/src/main/res/layout/activity_import_book.xml
new file mode 100644
index 000000000..d829e291c
--- /dev/null
+++ b/app/src/main/res/layout/activity_import_book.xml
@@ -0,0 +1,7 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/rss_read.xml b/app/src/main/res/menu/rss_read.xml
index 15463f64d..f32d01d2e 100644
--- a/app/src/main/res/menu/rss_read.xml
+++ b/app/src/main/res/menu/rss_read.xml
@@ -14,4 +14,10 @@
android:icon="@drawable/ic_menu_share"
app:showAsAction="ifRoom" />
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/source_edit.xml b/app/src/main/res/menu/source_edit.xml
index ec8739c11..2fd9678c4 100644
--- a/app/src/main/res/menu/source_edit.xml
+++ b/app/src/main/res/menu/source_edit.xml
@@ -35,7 +35,7 @@
app:showAsAction="never" />
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 5036723df..3ceda2af3 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -579,5 +579,8 @@
上一句
下一句
其它目录
+ 文字太多,生成二维码失败
+ 分享RSS源
+ 分享书源