Merge pull request #11 from gedoor/master

up
pull/71/head
口口吕 5 years ago committed by GitHub
commit 800fe34938
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      app/src/main/AndroidManifest.xml
  2. 3
      app/src/main/assets/updateLog.md
  3. 5
      app/src/main/java/io/legado/app/help/BookHelp.kt
  4. 2
      app/src/main/java/io/legado/app/lib/theme/ThemeStore.kt
  5. 4
      app/src/main/java/io/legado/app/lib/theme/ThemeStorePrefKeys.kt
  6. 18
      app/src/main/java/io/legado/app/ui/book/source/edit/BookSourceEditActivity.kt
  7. 19
      app/src/main/java/io/legado/app/ui/importbook/ImportBookActivity.kt
  8. 10
      app/src/main/java/io/legado/app/ui/importbook/ImportBookViewModel.kt
  9. 29
      app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt
  10. 50
      app/src/main/java/io/legado/app/ui/rss/read/ReadRssViewModel.kt
  11. 10
      app/src/main/java/io/legado/app/ui/rss/source/edit/RssSourceEditActivity.kt
  12. 44
      app/src/main/java/io/legado/app/ui/widget/font/FontSelectDialog.kt
  13. 57
      app/src/main/java/io/legado/app/utils/ContextExtensions.kt
  14. 7
      app/src/main/res/layout/activity_import_book.xml
  15. 6
      app/src/main/res/menu/rss_read.xml
  16. 2
      app/src/main/res/menu/source_edit.xml
  17. 3
      app/src/main/res/values/strings.xml

@ -98,6 +98,7 @@
<activity
android:name=".ui.audio.AudioPlayActivity"
android:launchMode="singleTask" />
<activity android:name=".ui.importbook.ImportBookActivity" />
<activity android:name=".ui.explore.ExploreShowActivity" />
<activity android:name=".ui.rss.source.manage.RssSourceActivity" />
<activity android:name=".ui.rss.source.debug.RssSourceDebugActivity" />

@ -3,6 +3,9 @@
* 旧版数据导入教程:
* 先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。
**2020/01/10
* 合并KKL369提交的代码
**2020/01/08**
* 导入本地源不再需要存储权限

@ -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,

@ -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 {

@ -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"
@ -28,4 +27,3 @@ internal interface ThemeStorePrefKeys {
const val KEY_APPLY_PRIMARY_NAVBAR = "apply_primary_navbar"
const val KEY_AUTO_GENERATE_PRIMARYDARK = "auto_generate_primarydark"
}
}

@ -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 {

@ -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<ImportBookViewModel>(R.layout.activity_import_book) {
override val viewModel: ImportBookViewModel
get() = getViewModel(ImportBookViewModel::class.java)
override fun onActivityCreated(savedInstanceState: Bundle?) {
}
}

@ -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) {
}

@ -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<ReadRssViewModel>(R.layout.activity_rss_read),
ReadRssViewModel.CallBack {
@ -25,6 +24,7 @@ class ReadRssActivity : VMBaseActivity<ReadRssViewModel>(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<ReadRssViewModel>(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<ReadRssViewModel>(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<ReadRssViewModel>(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<ReadRssViewModel>(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)
}
}
}

@ -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<String>()
val urlLiveData = MutableLiveData<AnalyzeUrl>()
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)
}
}

@ -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)
}

@ -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?) {

@ -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")

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
</LinearLayout>

@ -14,4 +14,10 @@
android:icon="@drawable/ic_menu_share"
app:showAsAction="ifRoom" />
<item
android:id="@+id/menu_aloud"
android:title="@string/read_aloud"
android:icon="@drawable/ic_volume_up"
app:showAsAction="ifRoom" />
</menu>

@ -35,7 +35,7 @@
app:showAsAction="never" />
<item
android:id="@+id/menu_share_it"
android:id="@+id/menu_share_qr"
android:title="@string/qr_share"
app:showAsAction="never" />

@ -579,5 +579,8 @@
<string name="prev_sentence">上一句</string>
<string name="next_sentence">下一句</string>
<string name="other_folder">其它目录</string>
<string name="text_too_long_qr_error">文字太多,生成二维码失败</string>
<string name="share_rss_source">分享RSS源</string>
<string name="share_book_source">分享书源</string>
</resources>

Loading…
Cancel
Save