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 <activity
android:name=".ui.audio.AudioPlayActivity" android:name=".ui.audio.AudioPlayActivity"
android:launchMode="singleTask" /> android:launchMode="singleTask" />
<activity android:name=".ui.importbook.ImportBookActivity" />
<activity android:name=".ui.explore.ExploreShowActivity" /> <activity android:name=".ui.explore.ExploreShowActivity" />
<activity android:name=".ui.rss.source.manage.RssSourceActivity" /> <activity android:name=".ui.rss.source.manage.RssSourceActivity" />
<activity android:name=".ui.rss.source.debug.RssSourceDebugActivity" /> <activity android:name=".ui.rss.source.debug.RssSourceDebugActivity" />

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

@ -86,7 +86,7 @@ object BookHelp {
} }
private fun getBookFolder(book: Book): String { 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" return "${getBookCachePath()}${File.separator}$bookFolder"
} }
@ -108,6 +108,9 @@ object BookHelp {
?: "" ?: ""
} }
/**
* 找到相似度最高的章节
*/
fun getDurChapterIndexByChapterTitle( fun getDurChapterIndexByChapterTitle(
title: String?, title: String?,
index: Int, index: Int,

@ -15,7 +15,7 @@ import io.legado.app.R
* @author Aidan Follestad (afollestad), Karim Abou Zeid (kabouzeid) * @author Aidan Follestad (afollestad), Karim Abou Zeid (kabouzeid)
*/ */
class ThemeStore @SuppressLint("CommitPrefEdits") class ThemeStore @SuppressLint("CommitPrefEdits")
private constructor(private val mContext: Context) : ThemeStorePrefKeys, ThemeStoreInterface { private constructor(private val mContext: Context) : ThemeStoreInterface {
private val mEditor: SharedPreferences.Editor private val mEditor: SharedPreferences.Editor
init { init {

@ -3,8 +3,7 @@ package io.legado.app.lib.theme
/** /**
* @author Aidan Follestad (afollestad), Karim Abou Zeid (kabouzeid) * @author Aidan Follestad (afollestad), Karim Abou Zeid (kabouzeid)
*/ */
internal interface ThemeStorePrefKeys { object ThemeStorePrefKeys {
companion object {
const val CONFIG_PREFS_KEY_DEFAULT = "app_themes" const val CONFIG_PREFS_KEY_DEFAULT = "app_themes"
const val IS_CONFIGURED_KEY = "is_configured" 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_PRIMARYDARK_STATUSBAR = "apply_primarydark_statusbar"
const val KEY_APPLY_PRIMARY_NAVBAR = "apply_primary_navbar" const val KEY_APPLY_PRIMARY_NAVBAR = "apply_primary_navbar"
const val KEY_AUTO_GENERATE_PRIMARYDARK = "auto_generate_primarydark" 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.lib.theme.ATH
import io.legado.app.ui.book.source.debug.BookSourceDebugActivity import io.legado.app.ui.book.source.debug.BookSourceDebugActivity
import io.legado.app.ui.widget.KeyboardToolPop import io.legado.app.ui.widget.KeyboardToolPop
import io.legado.app.utils.GSON import io.legado.app.utils.*
import io.legado.app.utils.applyTint
import io.legado.app.utils.getViewModel
import kotlinx.android.synthetic.main.activity_book_source_edit.* import kotlinx.android.synthetic.main.activity_book_source_edit.*
import org.jetbrains.anko.displayMetrics import org.jetbrains.anko.displayMetrics
import org.jetbrains.anko.startActivity import org.jetbrains.anko.startActivity
@ -87,17 +85,11 @@ class BookSourceEditActivity :
} }
} }
R.id.menu_paste_source -> viewModel.pasteSource { upRecyclerView(it) } R.id.menu_paste_source -> viewModel.pasteSource { upRecyclerView(it) }
R.id.menu_share_str -> { R.id.menu_share_str -> GSON.toJson(getSource())?.let { sourceStr ->
GSON.toJson(getSource())?.let { sourceStr -> shareText(getString(R.string.share_book_source), 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_qr -> GSON.toJson(getSource())?.let { sourceStr ->
shareWithQr(getString(R.string.share_book_source), sourceStr)
} }
R.id.menu_rule_summary -> { R.id.menu_rule_summary -> {
try { 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 package io.legado.app.ui.rss.read
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.KeyEvent import android.view.KeyEvent
import android.view.Menu 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.lib.theme.primaryTextColor
import io.legado.app.utils.NetworkUtils import io.legado.app.utils.NetworkUtils
import io.legado.app.utils.getViewModel import io.legado.app.utils.getViewModel
import io.legado.app.utils.shareText
import kotlinx.android.synthetic.main.activity_rss_read.* import kotlinx.android.synthetic.main.activity_rss_read.*
import org.jetbrains.anko.toast
class ReadRssActivity : VMBaseActivity<ReadRssViewModel>(R.layout.activity_rss_read), class ReadRssActivity : VMBaseActivity<ReadRssViewModel>(R.layout.activity_rss_read),
ReadRssViewModel.CallBack { ReadRssViewModel.CallBack {
@ -25,6 +24,7 @@ class ReadRssActivity : VMBaseActivity<ReadRssViewModel>(R.layout.activity_rss_r
get() = getViewModel(ReadRssViewModel::class.java) get() = getViewModel(ReadRssViewModel::class.java)
private var starMenuItem: MenuItem? = null private var starMenuItem: MenuItem? = null
private var ttsMenuItem: MenuItem? = null
override fun onActivityCreated(savedInstanceState: Bundle?) { override fun onActivityCreated(savedInstanceState: Bundle?) {
viewModel.callBack = this viewModel.callBack = this
@ -37,6 +37,7 @@ class ReadRssActivity : VMBaseActivity<ReadRssViewModel>(R.layout.activity_rss_r
override fun onCompatCreateOptionsMenu(menu: Menu): Boolean { override fun onCompatCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.rss_read, menu) menuInflater.inflate(R.menu.rss_read, menu)
starMenuItem = menu.findItem(R.id.menu_rss_star) starMenuItem = menu.findItem(R.id.menu_rss_star)
ttsMenuItem = menu.findItem(R.id.menu_aloud)
upStarMenu() upStarMenu()
return super.onCompatCreateOptionsMenu(menu) 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 { R.id.menu_share_it -> viewModel.rssArticle?.let {
shareText("链接分享", it.link) shareText("链接分享", it.link)
} }
R.id.menu_aloud -> readAloud()
} }
return super.onCompatOptionsItemSelected(item) return super.onCompatOptionsItemSelected(item)
} }
@ -97,6 +99,17 @@ class ReadRssActivity : VMBaseActivity<ReadRssViewModel>(R.layout.activity_rss_r
DrawableUtils.setTint(starMenuItem?.icon, primaryTextColor) 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 { override fun onKeyLongPress(keyCode: Int, event: KeyEvent?): Boolean {
when (keyCode) { when (keyCode) {
KeyEvent.KEYCODE_BACK -> { KeyEvent.KEYCODE_BACK -> {
@ -121,14 +134,10 @@ class ReadRssActivity : VMBaseActivity<ReadRssViewModel>(R.layout.activity_rss_r
return super.onKeyUp(keyCode, event) return super.onKeyUp(keyCode, event)
} }
private fun shareText(title: String, text: String) { private fun readAloud() {
try { webView.evaluateJavascript("document.documentElement.outerHTML") {
val textIntent = Intent(Intent.ACTION_SEND) viewModel.readAloud(it)
textIntent.type = "text/plain"
textIntent.putExtra(Intent.EXTRA_TEXT, text)
startActivity(Intent.createChooser(textIntent, title))
} catch (e: Exception) {
toast(R.string.can_not_share)
} }
} }
} }

@ -2,21 +2,28 @@ package io.legado.app.ui.rss.read
import android.app.Application import android.app.Application
import android.content.Intent import android.content.Intent
import android.speech.tts.TextToSpeech
import android.speech.tts.UtteranceProgressListener
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import io.legado.app.App import io.legado.app.App
import io.legado.app.R
import io.legado.app.base.BaseViewModel import io.legado.app.base.BaseViewModel
import io.legado.app.data.entities.RssArticle import io.legado.app.data.entities.RssArticle
import io.legado.app.data.entities.RssSource import io.legado.app.data.entities.RssSource
import io.legado.app.model.Rss import io.legado.app.model.Rss
import io.legado.app.model.analyzeRule.AnalyzeUrl 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 callBack: CallBack? = null
var rssSource: RssSource? = null var rssSource: RssSource? = null
var rssArticle: RssArticle? = null var rssArticle: RssArticle? = null
val contentLiveData = MutableLiveData<String>() val contentLiveData = MutableLiveData<String>()
val urlLiveData = MutableLiveData<AnalyzeUrl>() val urlLiveData = MutableLiveData<AnalyzeUrl>()
var star = false var star = false
var textToSpeech: TextToSpeech = TextToSpeech(context, this)
fun initData(intent: Intent) { fun initData(intent: Intent) {
execute { 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 { interface CallBack {
fun upStarMenu() 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.lib.theme.ATH
import io.legado.app.ui.rss.source.debug.RssSourceDebugActivity import io.legado.app.ui.rss.source.debug.RssSourceDebugActivity
import io.legado.app.ui.widget.KeyboardToolPop import io.legado.app.ui.widget.KeyboardToolPop
import io.legado.app.utils.GSON import io.legado.app.utils.*
import io.legado.app.utils.applyTint
import io.legado.app.utils.getViewModel
import kotlinx.android.synthetic.main.activity_rss_source_edit.* import kotlinx.android.synthetic.main.activity_rss_source_edit.*
import org.jetbrains.anko.displayMetrics import org.jetbrains.anko.displayMetrics
import org.jetbrains.anko.startActivity import org.jetbrains.anko.startActivity
@ -103,6 +101,12 @@ class RssSourceEditActivity :
} }
} }
R.id.menu_paste_source -> viewModel.pasteSource { upRecyclerView(it) } 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) return super.onCompatOptionsItemSelected(item)
} }

@ -88,16 +88,14 @@ class FontSelectDialog : DialogFragment(),
override fun onMenuItemClick(item: MenuItem?): Boolean { override fun onMenuItemClick(item: MenuItem?): Boolean {
when (item?.itemId) { when (item?.itemId) {
R.id.menu_default -> { R.id.menu_default -> {
val pf = parentFragment (parentFragment as? CallBack)?.let {
if (pf is CallBack) { if (it.curFontPath != "") {
if ("" != pf.curFontPath) { it.selectFile("")
pf.selectFile("")
} }
} }
val activity = activity (activity as? CallBack)?.let {
if (activity is CallBack) { if (it.curFontPath != "") {
if ("" != activity.curFontPath) { it.selectFile("")
activity.selectFile("")
} }
} }
dismiss() dismiss()
@ -131,7 +129,7 @@ class FontSelectDialog : DialogFragment(),
@SuppressLint("DefaultLocale") @SuppressLint("DefaultLocale")
private fun getFontFiles(uri: Uri) { private fun getFontFiles(uri: Uri) {
launch(IO) { 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) { if (file.name?.toLowerCase()?.matches(".*\\.[ot]tf".toRegex()) == true) {
DocumentUtils.readBytes(App.INSTANCE, file.uri)?.let { DocumentUtils.readBytes(App.INSTANCE, file.uri)?.let {
FileHelp.getFile(fontFolder + file.name).writeBytes(it) FileHelp.getFile(fontFolder + file.name).writeBytes(it)
@ -169,17 +167,15 @@ class FontSelectDialog : DialogFragment(),
} }
override fun onClick(file: File) { override fun onClick(file: File) {
file.absolutePath.let { file.absolutePath.let { path ->
val pf = parentFragment (parentFragment as? CallBack)?.let {
if (pf is CallBack) { if (it.curFontPath != path) {
if (it != pf.curFontPath) { it.selectFile(path)
pf.selectFile(it)
} }
} }
val activity = activity (activity as? CallBack)?.let {
if (activity is CallBack) { if (it.curFontPath != path) {
if (it != activity.curFontPath) { it.selectFile(path)
activity.selectFile(it)
} }
} }
} }
@ -187,15 +183,9 @@ class FontSelectDialog : DialogFragment(),
} }
override fun curFilePath(): String { override fun curFilePath(): String {
val pf = parentFragment return (parentFragment as? CallBack)?.curFontPath
if (pf is CallBack) { ?: (activity as? CallBack)?.curFontPath
return pf.curFontPath ?: ""
}
val activity = activity
if (activity is CallBack) {
return activity.curFontPath
}
return ""
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {

@ -1,13 +1,25 @@
package io.legado.app.utils package io.legado.app.utils
import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.content.Intent
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.graphics.Bitmap
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import androidx.annotation.ColorRes import androidx.annotation.ColorRes
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import androidx.core.content.edit 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.defaultSharedPreferences
import org.jetbrains.anko.toast
import java.io.File
import java.io.FileOutputStream
fun Context.getPrefBoolean(key: String, defValue: Boolean = false) = fun Context.getPrefBoolean(key: String, defValue: Boolean = false) =
defaultSharedPreferences.getBoolean(key, defValue) 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.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 { fun Context.getStatusBarHeight(): Int {
val resourceId = resources.getIdentifier("status_bar_height", "dimen", "android") val resourceId = resources.getIdentifier("status_bar_height", "dimen", "android")
@ -60,6 +73,48 @@ fun Context.getNavigationBarHeight(): Int {
return resources.getDimensionPixelSize(resourceId) 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 val Context.isNightTheme: Boolean
get() = getPrefBoolean("isNightTheme") 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" android:icon="@drawable/ic_menu_share"
app:showAsAction="ifRoom" /> app:showAsAction="ifRoom" />
<item
android:id="@+id/menu_aloud"
android:title="@string/read_aloud"
android:icon="@drawable/ic_volume_up"
app:showAsAction="ifRoom" />
</menu> </menu>

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

@ -579,5 +579,8 @@
<string name="prev_sentence">上一句</string> <string name="prev_sentence">上一句</string>
<string name="next_sentence">下一句</string> <string name="next_sentence">下一句</string>
<string name="other_folder">其它目录</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> </resources>

Loading…
Cancel
Save