diff --git a/app/build.gradle b/app/build.gradle
index a403631ad..2e4307723 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -126,7 +126,14 @@ dependencies {
//Glide
implementation 'com.github.bumptech.glide:glide:4.9.0'
+ //二维码
+ implementation 'cn.bingoogolapple:bga-qrcode-zxing:1.3.6'
+
//颜色选择
implementation 'com.jaredrummler:colorpicker:1.1.0'
+ //对话框
+ implementation 'com.afollestad.material-dialogs:core:3.0.0-rc3'
+ implementation 'com.afollestad.material-dialogs:input:3.0.0-rc3'
+ implementation 'com.afollestad.material-dialogs:files:3.0.0-rc3'
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 6cf4d7d67..91282f69e 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -43,6 +43,8 @@
android:theme="@style/Activity.Permission"/>
+
+
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/base/BaseActivity.kt b/app/src/main/java/io/legado/app/base/BaseActivity.kt
index 3f6873a44..8d86f5ccc 100644
--- a/app/src/main/java/io/legado/app/base/BaseActivity.kt
+++ b/app/src/main/java/io/legado/app/base/BaseActivity.kt
@@ -11,6 +11,7 @@ import androidx.lifecycle.ViewModel
import io.legado.app.R
import io.legado.app.lib.theme.ColorUtils
import io.legado.app.lib.theme.ThemeStore
+import io.legado.app.utils.disableAutoFill
import io.legado.app.utils.getCompatColor
import io.legado.app.utils.getPrefBoolean
import io.legado.app.utils.setIconColor
@@ -23,6 +24,7 @@ abstract class BaseActivity : AppCompatActivity() {
protected abstract val layoutID: Int
override fun onCreate(savedInstanceState: Bundle?) {
+ window.decorView.disableAutoFill()
initTheme()
setupSystemBar()
super.onCreate(savedInstanceState)
diff --git a/app/src/main/java/io/legado/app/data/dao/BookGroupDao.kt b/app/src/main/java/io/legado/app/data/dao/BookGroupDao.kt
index b348cfeaa..2a32912ee 100644
--- a/app/src/main/java/io/legado/app/data/dao/BookGroupDao.kt
+++ b/app/src/main/java/io/legado/app/data/dao/BookGroupDao.kt
@@ -2,6 +2,8 @@ package io.legado.app.data.dao
import androidx.paging.DataSource
import androidx.room.Dao
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy
import androidx.room.Query
import io.legado.app.data.entities.BookGroup
@@ -11,5 +13,9 @@ interface BookGroupDao {
@Query("SELECT * FROM book_groups ORDER BY `order`")
fun observeAll(): DataSource.Factory
+ @get:Query("SELECT MAX(groupId) FROM book_groups")
+ val maxId: Int
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ fun insert(bookGroup: BookGroup)
}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/data/entities/BaseBook.kt b/app/src/main/java/io/legado/app/data/entities/BaseBook.kt
new file mode 100644
index 000000000..e3605d6f6
--- /dev/null
+++ b/app/src/main/java/io/legado/app/data/entities/BaseBook.kt
@@ -0,0 +1,6 @@
+package io.legado.app.data.entities
+
+interface BaseBook {
+ var variableMap: HashMap?
+ fun putVariable(key: String, value: String)
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/data/entities/Book.kt b/app/src/main/java/io/legado/app/data/entities/Book.kt
index b1a18961f..6c7a2e4b3 100644
--- a/app/src/main/java/io/legado/app/data/entities/Book.kt
+++ b/app/src/main/java/io/legado/app/data/entities/Book.kt
@@ -1,10 +1,15 @@
package io.legado.app.data.entities
import android.os.Parcelable
+import android.text.TextUtils.isEmpty
import androidx.room.Entity
+import androidx.room.Ignore
import androidx.room.Index
import androidx.room.PrimaryKey
+import com.google.gson.Gson
import io.legado.app.constant.AppConst.NOT_AVAILABLE
+import io.legado.app.utils.fromJson
+import kotlinx.android.parcel.IgnoredOnParcel
import kotlinx.android.parcel.Parcelize
@Parcelize
@@ -40,7 +45,10 @@ data class Book(
var order: Int = 0, // 手动排序
var useReplaceRule: Boolean = true, // 正文使用净化替换规则
var variable: String? = null // 自定义书籍变量信息(用于书源规则检索书籍信息)
-) : Parcelable {
+) : Parcelable, BaseBook {
+ @IgnoredOnParcel
+ @Ignore
+ override var variableMap: HashMap? = null
fun getUnreadChapterNum() = Math.max(totalChapterNum - durChapterIndex - 1, 0)
@@ -52,4 +60,17 @@ data class Book(
fun getDisplayDescription() = customDescription ?: description
+ private fun initVariableMap() {
+ if (variableMap == null) {
+ variableMap = if (isEmpty(variable)) {
+ HashMap()
+ } else {
+ Gson().fromJson>(variable!!)
+ }
+ }
+ }
+
+ override fun putVariable(key: String, value: String) {
+ initVariableMap()
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/help/AdapterDataObserverProxy.kt b/app/src/main/java/io/legado/app/help/AdapterDataObserverProxy.kt
new file mode 100644
index 000000000..9ef120e15
--- /dev/null
+++ b/app/src/main/java/io/legado/app/help/AdapterDataObserverProxy.kt
@@ -0,0 +1,30 @@
+package io.legado.app.help
+
+import androidx.recyclerview.widget.RecyclerView
+
+internal class AdapterDataObserverProxy(var adapterDataObserver: RecyclerView.AdapterDataObserver, var headerCount: Int) : RecyclerView.AdapterDataObserver() {
+ override fun onChanged() {
+ adapterDataObserver.onChanged()
+ }
+
+ override fun onItemRangeChanged(positionStart: Int, itemCount: Int) {
+ adapterDataObserver.onItemRangeChanged(positionStart + headerCount, itemCount)
+ }
+
+ override fun onItemRangeChanged(positionStart: Int, itemCount: Int, payload: Any?) {
+ adapterDataObserver.onItemRangeChanged(positionStart + headerCount, itemCount, payload)
+ }
+
+ // 当第n个数据被获取,更新第n+1个position
+ override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
+ adapterDataObserver.onItemRangeInserted(positionStart + headerCount, itemCount)
+ }
+
+ override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
+ adapterDataObserver.onItemRangeRemoved(positionStart + headerCount, itemCount)
+ }
+
+ override fun onItemRangeMoved(fromPosition: Int, toPosition: Int, itemCount: Int) {
+ super.onItemRangeMoved(fromPosition + headerCount, toPosition + headerCount, itemCount)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/help/storage/Restore.kt b/app/src/main/java/io/legado/app/help/storage/Restore.kt
index aafb71f5c..021b8585c 100644
--- a/app/src/main/java/io/legado/app/help/storage/Restore.kt
+++ b/app/src/main/java/io/legado/app/help/storage/Restore.kt
@@ -26,7 +26,7 @@ object Restore {
}
fun importYueDuData(context: Context) {
- val yuedu = File(getSdPath(), "YueDu")
+ val yuedu = File(FileUtils.getSdPath(), "YueDu")
val jsonPath = JsonPath.using(
Configuration.builder()
.options(Option.SUPPRESS_EXCEPTIONS)
diff --git a/app/src/main/java/io/legado/app/ui/bookshelf/BookshelfActivity.kt b/app/src/main/java/io/legado/app/ui/bookshelf/BookshelfActivity.kt
new file mode 100644
index 000000000..445abca97
--- /dev/null
+++ b/app/src/main/java/io/legado/app/ui/bookshelf/BookshelfActivity.kt
@@ -0,0 +1,24 @@
+package io.legado.app.ui.bookshelf
+
+import android.os.Bundle
+import io.legado.app.R
+import io.legado.app.base.BaseActivity
+import io.legado.app.utils.getViewModel
+import kotlinx.android.synthetic.main.activity_bookshelf.*
+
+class BookshelfActivity : BaseActivity() {
+ override val viewModel: BookshelfViewModel
+ get() = getViewModel(BookshelfViewModel::class.java)
+ override val layoutID: Int
+ get() = R.layout.activity_bookshelf
+
+ override fun onViewModelCreated(viewModel: BookshelfViewModel, savedInstanceState: Bundle?) {
+ if (viewModel.bookGroup == null) {
+ viewModel.bookGroup = intent.getParcelableExtra("data")
+ }
+ viewModel.bookGroup?.let {
+ title_bar.title = it.groupName
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/bookshelf/BookshelfViewModel.kt b/app/src/main/java/io/legado/app/ui/bookshelf/BookshelfViewModel.kt
new file mode 100644
index 000000000..3948465ac
--- /dev/null
+++ b/app/src/main/java/io/legado/app/ui/bookshelf/BookshelfViewModel.kt
@@ -0,0 +1,11 @@
+package io.legado.app.ui.bookshelf
+
+import android.app.Application
+import io.legado.app.base.BaseViewModel
+import io.legado.app.data.entities.BookGroup
+
+class BookshelfViewModel(application: Application) : BaseViewModel(application) {
+
+ var bookGroup: BookGroup? = null
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/config/ThemeConfigFragment.kt b/app/src/main/java/io/legado/app/ui/config/ThemeConfigFragment.kt
index 82c198fc2..62d20203a 100644
--- a/app/src/main/java/io/legado/app/ui/config/ThemeConfigFragment.kt
+++ b/app/src/main/java/io/legado/app/ui/config/ThemeConfigFragment.kt
@@ -54,7 +54,7 @@ class ThemeConfigFragment : PreferenceFragmentCompat(), SharedPreferences.OnShar
upTheme(false)
}
.setNegativeButton(R.string.cancel) { _, _ -> upTheme(false) }
- .show().upTint
+ .show().upTint()
}
} else {
upTheme(false)
@@ -74,7 +74,7 @@ class ThemeConfigFragment : PreferenceFragmentCompat(), SharedPreferences.OnShar
upTheme(true)
}
.setNegativeButton(R.string.cancel) { _, _ -> upTheme(true) }
- .show().upTint
+ .show().upTint()
}
} else {
upTheme(true)
@@ -93,11 +93,11 @@ class ThemeConfigFragment : PreferenceFragmentCompat(), SharedPreferences.OnShar
.setMessage("是否确认恢复?")
.setPositiveButton(R.string.ok) { _, _ ->
preferenceManager.sharedPreferences.edit()
- .putInt("colorPrimary", App.INSTANCE.getCompatColor(R.color.md_grey_100))
- .putInt("colorAccent", App.INSTANCE.getCompatColor(R.color.md_pink_600))
+ .putInt("colorPrimary", App.INSTANCE.getCompatColor(R.color.colorPrimary))
+ .putInt("colorAccent", App.INSTANCE.getCompatColor(R.color.colorAccent))
.putInt("colorBackground", App.INSTANCE.getCompatColor(R.color.md_grey_100))
- .putInt("colorPrimaryNight", App.INSTANCE.getCompatColor(R.color.md_grey_800))
- .putInt("colorAccentNight", App.INSTANCE.getCompatColor(R.color.md_pink_800))
+ .putInt("colorPrimaryNight", App.INSTANCE.getCompatColor(R.color.colorPrimary))
+ .putInt("colorAccentNight", App.INSTANCE.getCompatColor(R.color.colorAccent))
.putInt("colorBackgroundNight", App.INSTANCE.getCompatColor(R.color.md_grey_800))
.apply()
App.INSTANCE.upThemeStore()
@@ -105,7 +105,7 @@ class ThemeConfigFragment : PreferenceFragmentCompat(), SharedPreferences.OnShar
Handler().postDelayed({ activity?.recreate() }, 100)
}
.setNegativeButton(R.string.cancel, null)
- .show().upTint
+ .show().upTint()
}
}
}
diff --git a/app/src/main/java/io/legado/app/ui/main/MainActivity.kt b/app/src/main/java/io/legado/app/ui/main/MainActivity.kt
index 973380645..c2a1b2f77 100644
--- a/app/src/main/java/io/legado/app/ui/main/MainActivity.kt
+++ b/app/src/main/java/io/legado/app/ui/main/MainActivity.kt
@@ -15,11 +15,13 @@ import io.legado.app.constant.Bus
import io.legado.app.help.permission.Permissions
import io.legado.app.help.permission.PermissionsCompat
import io.legado.app.help.storage.Restore
+import io.legado.app.lib.theme.Selector
import io.legado.app.lib.theme.ThemeStore
import io.legado.app.ui.main.bookshelf.BookshelfFragment
import io.legado.app.ui.main.booksource.BookSourceFragment
import io.legado.app.ui.main.findbook.FindBookFragment
import io.legado.app.ui.main.myconfig.MyConfigFragment
+import io.legado.app.utils.getCompatColor
import io.legado.app.utils.getViewModel
import kotlinx.android.synthetic.main.activity_main.*
@@ -34,6 +36,11 @@ class MainActivity : BaseActivity(), BottomNavigationView.OnNavig
override fun onViewModelCreated(viewModel: MainViewModel, savedInstanceState: Bundle?) {
bottom_navigation_view.setBackgroundColor(ThemeStore.backgroundColor(this))
+ val colorStateList = Selector.colorBuild()
+ .setDefaultColor(bottom_navigation_view.context.getCompatColor(R.color.btn_bg_press_tp))
+ .setSelectedColor(ThemeStore.primaryColor(bottom_navigation_view.context)).create()
+ bottom_navigation_view.itemIconTintList = colorStateList
+ bottom_navigation_view.itemTextColor = colorStateList
view_pager_main.offscreenPageLimit = 3
view_pager_main.adapter = TabFragmentPageAdapter(supportFragmentManager)
view_pager_main.addOnPageChangeListener(this)
diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookGroupAdapter.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookGroupAdapter.kt
index 077a610fc..bb3adbf8f 100644
--- a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookGroupAdapter.kt
+++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookGroupAdapter.kt
@@ -8,6 +8,8 @@ import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import io.legado.app.R
import io.legado.app.data.entities.BookGroup
+import io.legado.app.help.AdapterDataObserverProxy
+import kotlinx.android.synthetic.main.item_book_group.view.*
class BookGroupAdapter : PagedListAdapter(DIFF_CALLBACK) {
@@ -24,15 +26,44 @@ class BookGroupAdapter : PagedListAdapter holder.bind(defaultGroups[position], callBack)
+ position == itemCount - 1 -> holder.bind(addBookGroup, callBack)
+ else -> currentList?.get(position - defaultGroups.size)?.let {
+ holder.bind(it, callBack)
+ }
+ }
}
class MyViewHolder(view: View) : RecyclerView.ViewHolder(view) {
+ fun bind(bookGroup: BookGroup, callBack: CallBack?) = with(itemView) {
+ tv_group.text = bookGroup.groupName
+ tv_group.setOnClickListener { callBack?.open(bookGroup) }
+ }
+ }
+
+ interface CallBack {
+ fun open(bookGroup: BookGroup)
}
}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfAdapter.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfAdapter.kt
index e2cf5728f..eae2f0aec 100644
--- a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfAdapter.kt
+++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfAdapter.kt
@@ -16,7 +16,7 @@ import kotlinx.android.synthetic.main.item_bookshelf_list.view.*
import kotlinx.android.synthetic.main.item_relace_rule.view.tv_name
import java.io.File
-class BookshelfAdapter : PagedListAdapter(DIFF_CALLBACK) {
+class BookshelfAdapter : PagedListAdapter(DIFF_CALLBACK) {
companion object {
@JvmField
@@ -33,13 +33,40 @@ class BookshelfAdapter : PagedListAdapter(D
var callBack: CallBack? = null
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
+ override fun getItemViewType(position: Int): Int {
+ if (position == itemCount - 1) {
+ return 1
+ }
+ return super.getItemViewType(position)
+ }
+
+ override fun getItemCount(): Int {
+ return super.getItemCount() + 1
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
+ if (viewType == 1) {
+ return AddViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_bookshelf_list_add, parent, false))
+ }
return MyViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_bookshelf_list, parent, false))
}
- override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
- currentList?.get(position)?.let {
- holder.bind(it, callBack)
+ override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
+ when (holder) {
+ is MyViewHolder -> {
+ currentList?.get(position)?.let {
+ holder.bind(it, callBack)
+ }
+ }
+ is AddViewHolder -> holder.bind(callBack)
+ }
+
+ }
+
+ class AddViewHolder(view: View) : RecyclerView.ViewHolder(view) {
+
+ fun bind(callBack: CallBack?) = with(itemView) {
+ setOnClickListener { callBack?.search() }
}
}
@@ -76,5 +103,6 @@ class BookshelfAdapter : PagedListAdapter(D
interface CallBack {
fun open(book: Book)
+ fun search()
}
}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt
index b3aa3f2be..da5fe6bf0 100644
--- a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt
+++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt
@@ -7,16 +7,26 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import androidx.paging.LivePagedListBuilder
import androidx.paging.PagedList
+import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
+import com.afollestad.materialdialogs.MaterialDialog
+import com.afollestad.materialdialogs.input.input
import io.legado.app.App
import io.legado.app.R
import io.legado.app.base.BaseFragment
import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookGroup
+import io.legado.app.lib.theme.ThemeStore
+import io.legado.app.ui.bookshelf.BookshelfActivity
+import io.legado.app.utils.disableAutoFill
import kotlinx.android.synthetic.main.fragment_bookshelf.*
import kotlinx.android.synthetic.main.view_title_bar.*
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.launch
+import org.jetbrains.anko.startActivity
+import org.jetbrains.anko.textColor
-class BookshelfFragment : BaseFragment(R.layout.fragment_bookshelf) {
+class BookshelfFragment : BaseFragment(R.layout.fragment_bookshelf), BookGroupAdapter.CallBack {
private lateinit var bookshelfAdapter: BookshelfAdapter
private lateinit var bookGroupAdapter: BookGroupAdapter
@@ -25,6 +35,7 @@ class BookshelfFragment : BaseFragment(R.layout.fragment_bookshelf) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
setSupportToolbar(toolbar)
+ initSearchView()
initRecyclerView()
initBookGroupData()
initBookshelfData()
@@ -34,11 +45,25 @@ class BookshelfFragment : BaseFragment(R.layout.fragment_bookshelf) {
menuInflater.inflate(R.menu.bookshelf, menu)
}
+ private fun initSearchView() {
+ search_view.visibility = View.VISIBLE
+ search_view.onActionViewExpanded()
+ search_view.queryHint = getString(R.string.search_book_key)
+ search_view.clearFocus()
+ }
+
private fun initRecyclerView() {
+ refresh_layout.setColorSchemeColors(ThemeStore.accentColor(refresh_layout.context))
+ refresh_layout.setOnRefreshListener {
+ refresh_layout.isRefreshing = false
+ }
+ tv_recent_reading.textColor = ThemeStore.accentColor(tv_recent_reading.context)
rv_book_group.layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
bookGroupAdapter = BookGroupAdapter()
rv_book_group.adapter = bookGroupAdapter
+ bookGroupAdapter.callBack = this
rv_bookshelf.layoutManager = LinearLayoutManager(context)
+ rv_bookshelf.addItemDecoration(DividerItemDecoration(rv_bookshelf.context, LinearLayoutManager.VERTICAL))
bookshelfAdapter = BookshelfAdapter()
rv_bookshelf.adapter = bookshelfAdapter
}
@@ -55,4 +80,29 @@ class BookshelfFragment : BaseFragment(R.layout.fragment_bookshelf) {
bookshelfLiveData?.observe(viewLifecycleOwner, Observer { bookshelfAdapter.submitList(it) })
}
+ override fun open(bookGroup: BookGroup) {
+ when (bookGroup.groupId) {
+ -10 -> context?.let {
+ MaterialDialog(it).show {
+ window?.decorView?.disableAutoFill()
+ title(text = "新建分组")
+ input(hint = "分组名称") { _, charSequence ->
+ run {
+ GlobalScope.launch {
+ App.db.bookGroupDao().insert(
+ BookGroup(
+ App.db.bookGroupDao().maxId + 1,
+ charSequence.toString()
+ )
+ )
+ }
+ }
+ }
+ positiveButton(R.string.ok)
+ }
+ }
+ else -> context?.startActivity(Pair("data", bookGroup))
+ }
+ }
+
}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/main/booksource/BookSourceFragment.kt b/app/src/main/java/io/legado/app/ui/main/booksource/BookSourceFragment.kt
index 0816a2f4a..ea182f6dc 100644
--- a/app/src/main/java/io/legado/app/ui/main/booksource/BookSourceFragment.kt
+++ b/app/src/main/java/io/legado/app/ui/main/booksource/BookSourceFragment.kt
@@ -17,6 +17,7 @@ import io.legado.app.R
import io.legado.app.base.BaseFragment
import io.legado.app.data.entities.BookSource
import io.legado.app.help.ItemTouchCallback
+import io.legado.app.ui.qrcode.QrCodeActivity
import io.legado.app.ui.sourceedit.SourceEditActivity
import kotlinx.android.synthetic.main.fragment_book_source.*
import kotlinx.android.synthetic.main.view_title_bar.*
@@ -46,6 +47,9 @@ class BookSourceFragment : BaseFragment(R.layout.fragment_book_source), BookSour
R.id.action_add_book_source -> {
context?.startActivity()
}
+ R.id.action_import_book_source_qr -> {
+ context?.startActivity()
+ }
}
}
@@ -101,6 +105,6 @@ class BookSourceFragment : BaseFragment(R.layout.fragment_book_source), BookSour
}
override fun edit(bookSource: BookSource) {
- context?.let { it.startActivity(Pair("data", bookSource.origin)) }
+ context?.startActivity(Pair("data", bookSource.origin))
}
}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/main/myconfig/PreferenceFragment.kt b/app/src/main/java/io/legado/app/ui/main/myconfig/PreferenceFragment.kt
index a440d76f2..d3f862d20 100644
--- a/app/src/main/java/io/legado/app/ui/main/myconfig/PreferenceFragment.kt
+++ b/app/src/main/java/io/legado/app/ui/main/myconfig/PreferenceFragment.kt
@@ -6,6 +6,7 @@ import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import io.legado.app.App
import io.legado.app.R
+import io.legado.app.ui.about.AboutActivity
import io.legado.app.ui.config.ConfigActivity
import io.legado.app.ui.config.ConfigViewModel
import org.jetbrains.anko.startActivity
@@ -39,19 +40,20 @@ class PreferenceFragment : PreferenceFragmentCompat(), SharedPreferences.OnShare
when (preference.key) {
"setting" -> {
requireContext().startActivity(
- Pair("configType", ConfigViewModel.TYPE_CONFIG)
+ Pair("configType", ConfigViewModel.TYPE_CONFIG)
)
}
"web_dav_setting" -> {
requireContext().startActivity(
- Pair("configType", ConfigViewModel.TYPE_WEB_DAV_CONFIG)
+ Pair("configType", ConfigViewModel.TYPE_WEB_DAV_CONFIG)
)
}
"theme_setting" -> {
requireContext().startActivity(
- Pair("configType", ConfigViewModel.TYPE_THEME_CONFIG)
+ Pair("configType", ConfigViewModel.TYPE_THEME_CONFIG)
)
}
+ "about" -> requireContext().startActivity()
}
}
return super.onPreferenceTreeClick(preference)
diff --git a/app/src/main/java/io/legado/app/ui/qrcode/QrCodeActivity.kt b/app/src/main/java/io/legado/app/ui/qrcode/QrCodeActivity.kt
new file mode 100644
index 000000000..ad9019228
--- /dev/null
+++ b/app/src/main/java/io/legado/app/ui/qrcode/QrCodeActivity.kt
@@ -0,0 +1,112 @@
+package io.legado.app.ui.qrcode
+
+import android.app.Activity
+import android.content.Intent
+import android.os.Bundle
+import android.view.Menu
+import android.view.MenuItem
+import androidx.lifecycle.AndroidViewModel
+import cn.bingoogolapple.qrcode.core.QRCodeView
+import io.legado.app.R
+import io.legado.app.base.BaseActivity
+import io.legado.app.help.permission.Permissions
+import io.legado.app.help.permission.PermissionsCompat
+import io.legado.app.utils.FileUtils
+import io.legado.app.utils.getViewModel
+import kotlinx.android.synthetic.main.activity_qrcode_capture.*
+import kotlinx.android.synthetic.main.view_title_bar.*
+
+class QrCodeActivity : BaseActivity(), QRCodeView.Delegate {
+ override val viewModel: AndroidViewModel
+ get() = getViewModel(AndroidViewModel::class.java)
+ override val layoutID: Int
+ get() = R.layout.activity_qrcode_capture
+
+ private val requestQrImage = 202
+ private var flashlightIsOpen: Boolean = false
+
+ override fun onViewModelCreated(viewModel: AndroidViewModel, savedInstanceState: Bundle?) {
+ setSupportActionBar(toolbar)
+ zxingview.setDelegate(this)
+ fab_flashlight.setOnClickListener {
+ if (flashlightIsOpen) {
+ flashlightIsOpen = false
+ zxingview.closeFlashlight()
+ } else {
+ flashlightIsOpen = true
+ zxingview.openFlashlight()
+ }
+ }
+ }
+
+ override fun onCompatCreateOptionsMenu(menu: Menu): Boolean {
+ menuInflater.inflate(R.menu.qr_code_scan, menu)
+ return super.onCompatCreateOptionsMenu(menu)
+ }
+
+ override fun onCompatOptionsItemSelected(item: MenuItem): Boolean {
+ when (item.itemId) {
+ R.id.action_choose_from_gallery -> {
+ val intent = Intent(Intent.ACTION_GET_CONTENT)
+ intent.addCategory(Intent.CATEGORY_OPENABLE)
+ intent.type = "image/*"
+ startActivityForResult(intent, requestQrImage)
+ }
+ }
+ return super.onCompatOptionsItemSelected(item)
+ }
+
+ override fun onStart() {
+ super.onStart()
+ startCamera()
+ }
+
+ private fun startCamera() {
+ PermissionsCompat.Builder(this)
+ .addPermissions(*Permissions.Group.CAMERA)
+ .rationale(R.string.qr_per)
+ .onGranted {
+ zxingview.startCamera() // 打开后置摄像头开始预览,但是并未开始识别
+ zxingview.startSpotAndShowRect() // 显示扫描框,并开始识别
+ }.request()
+ }
+
+ override fun onStop() {
+ zxingview.stopCamera() // 关闭摄像头预览,并且隐藏扫描框
+ super.onStop()
+ }
+
+ override fun onDestroy() {
+ zxingview.onDestroy() // 销毁二维码扫描控件
+ super.onDestroy()
+ }
+
+ override fun onScanQRCodeSuccess(result: String) {
+ val intent = Intent()
+ intent.putExtra("result", result)
+ setResult(RESULT_OK, intent)
+ finish()
+ }
+
+ override fun onCameraAmbientBrightnessChanged(isDark: Boolean) {
+
+ }
+
+ override fun onScanQRCodeOpenCameraError() {
+
+ }
+
+ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ super.onActivityResult(requestCode, resultCode, data)
+ data?.data?.let {
+ zxingview.startSpotAndShowRect() // 显示扫描框,并开始识别
+
+ if (resultCode == Activity.RESULT_OK && requestCode == requestQrImage) {
+ val picturePath = FileUtils.getPath(this, it)
+ // 本来就用到 QRCodeView 时可直接调 QRCodeView 的方法,走通用的回调
+ zxingview.decodeQRCode(picturePath)
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/utils/AlertDialogExtensions.kt b/app/src/main/java/io/legado/app/utils/AlertDialogExtensions.kt
index 300309ca7..e956f1368 100644
--- a/app/src/main/java/io/legado/app/utils/AlertDialogExtensions.kt
+++ b/app/src/main/java/io/legado/app/utils/AlertDialogExtensions.kt
@@ -3,5 +3,6 @@ package io.legado.app.utils
import androidx.appcompat.app.AlertDialog
import io.legado.app.lib.theme.ATH
-val AlertDialog.upTint: AlertDialog
- get() = ATH.setAlertDialogTint(this)
\ No newline at end of file
+fun AlertDialog.upTint(): AlertDialog {
+ return ATH.setAlertDialogTint(this)
+}
diff --git a/app/src/main/java/io/legado/app/utils/FileUtils.kt b/app/src/main/java/io/legado/app/utils/FileUtils.kt
index 493347429..8c5ba3823 100644
--- a/app/src/main/java/io/legado/app/utils/FileUtils.kt
+++ b/app/src/main/java/io/legado/app/utils/FileUtils.kt
@@ -1,5 +1,235 @@
package io.legado.app.utils
+import android.annotation.TargetApi
+import android.content.ContentUris
+import android.content.Context
+import android.net.Uri
+import android.os.Build
import android.os.Environment
+import android.os.storage.StorageManager
+import android.provider.DocumentsContract
+import android.provider.MediaStore
+import android.util.Log
+import androidx.core.content.ContextCompat
+import java.io.File
+import java.io.IOException
+import java.lang.reflect.Array
+import java.util.*
-fun getSdPath() = Environment.getExternalStorageDirectory().absolutePath
+
+object FileUtils {
+ fun getSdPath() = Environment.getExternalStorageDirectory().absolutePath
+
+ fun getFileByPath(filePath: String): File? {
+ return if (isSpace(filePath)) null else File(filePath)
+ }
+
+ fun isSpace(s: String?): Boolean {
+ if (s == null) return true
+ var i = 0
+ val len = s.length
+ while (i < len) {
+ if (!Character.isWhitespace(s[i])) {
+ return false
+ }
+ ++i
+ }
+ return true
+ }
+
+ fun getSdCardPath(): String {
+ var sdCardDirectory = Environment.getExternalStorageDirectory().absolutePath
+
+ try {
+ sdCardDirectory = File(sdCardDirectory).canonicalPath
+ } catch (ioe: IOException) {
+ }
+
+ return sdCardDirectory
+ }
+
+ fun getStorageData(pContext: Context): ArrayList? {
+
+ val storageManager = pContext.getSystemService(Context.STORAGE_SERVICE) as StorageManager
+
+ try {
+ val getVolumeList = storageManager.javaClass.getMethod("getVolumeList")
+
+ val storageValumeClazz = Class.forName("android.os.storage.StorageVolume")
+ val getPath = storageValumeClazz.getMethod("getPath")
+
+ val invokeVolumeList = getVolumeList.invoke(storageManager)
+ val length = Array.getLength(invokeVolumeList)
+
+ val list = ArrayList()
+ for (i in 0 until length) {
+ val storageValume = Array.get(invokeVolumeList, i)//得到StorageVolume对象
+ val path = getPath.invoke(storageValume) as String
+
+ list.add(path)
+ }
+ return list
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+
+ return null
+ }
+
+
+ fun getExtSdCardPaths(con: Context): ArrayList {
+ val paths = ArrayList()
+ val files = ContextCompat.getExternalFilesDirs(con, "external")
+ val firstFile = files[0]
+ for (file in files) {
+ if (file != null && file != firstFile) {
+ val index = file.absolutePath.lastIndexOf("/Android/data")
+ if (index < 0) {
+ Log.w("", "Unexpected external file dir: " + file.absolutePath)
+ } else {
+ var path = file.absolutePath.substring(0, index)
+ try {
+ path = File(path).canonicalPath
+ } catch (e: IOException) {
+ // Keep non-canonical path.
+ }
+
+ paths.add(path)
+ }
+ }
+ }
+ return paths
+ }
+
+ @TargetApi(Build.VERSION_CODES.KITKAT)
+ fun getPath(context: Context, uri: Uri): String? {
+ val isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
+
+ // DocumentProvider
+ if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
+ // ExternalStorageProvider
+ if (isExternalStorageDocument(uri)) {
+ val docId = DocumentsContract.getDocumentId(uri)
+ val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
+ val type = split[0]
+
+ if ("primary".equals(type, ignoreCase = true)) {
+ return Environment.getExternalStorageDirectory().toString() + "/" + split[1]
+ }
+
+ } else if (isDownloadsDocument(uri)) {
+ val id = DocumentsContract.getDocumentId(uri)
+ val split = id.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
+ val type = split[0]
+ if ("raw".equals(
+ type,
+ ignoreCase = true
+ )
+ ) { //处理某些机型(比如Goole Pixel )ID是raw:/storage/emulated/0/Download/c20f8664da05ab6b4644913048ea8c83.mp4
+ return split[1]
+ }
+
+ val contentUri = ContentUris.withAppendedId(
+ Uri.parse("content://downloads/public_downloads"), java.lang.Long.valueOf(id)
+ )
+
+ return getDataColumn(context, contentUri, null, null)
+ } else if (isMediaDocument(uri)) {
+ val docId = DocumentsContract.getDocumentId(uri)
+ val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
+ val type = split[0]
+
+ var contentUri: Uri? = null
+ if ("image" == type) {
+ contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
+ } else if ("video" == type) {
+ contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
+ } else if ("audio" == type) {
+ contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
+ }
+
+ val selection = "_id=?"
+ val selectionArgs = arrayOf(split[1])
+
+ return getDataColumn(context, contentUri, selection, selectionArgs)
+ }// MediaProvider
+ // DownloadsProvider
+ } else if ("content".equals(uri.scheme!!, ignoreCase = true)) {
+
+ // Return the remote address
+ return if (isGooglePhotosUri(uri)) uri.lastPathSegment else getDataColumn(
+ context,
+ uri,
+ null,
+ null
+ )
+
+ } else if ("file".equals(uri.scheme!!, ignoreCase = true)) {
+ return uri.path
+ }// File
+ // MediaStore (and general)
+
+ return null
+ }
+
+ fun getDataColumn(
+ context: Context, uri: Uri?, selection: String?,
+ selectionArgs: kotlin.Array?
+ ): String? {
+
+ val column = "_data"
+ val projection = arrayOf(column)
+
+ try {
+ context.contentResolver.query(
+ uri!!,
+ projection,
+ selection,
+ selectionArgs,
+ null
+ )!!.use { cursor ->
+ if (cursor != null && cursor.moveToFirst()) {
+ val index = cursor.getColumnIndexOrThrow(column)
+ return cursor.getString(index)
+ }
+ }
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+
+ return null
+ }
+
+ /**
+ * @param uri The Uri to check.
+ * @return Whether the Uri authority is ExternalStorageProvider.
+ */
+ fun isExternalStorageDocument(uri: Uri): Boolean {
+ return "com.android.externalstorage.documents" == uri.authority
+ }
+
+ /**
+ * @param uri The Uri to check.
+ * @return Whether the Uri authority is DownloadsProvider.
+ */
+ fun isDownloadsDocument(uri: Uri): Boolean {
+ return "com.android.providers.downloads.documents" == uri.authority
+ }
+
+ /**
+ * @param uri The Uri to check.
+ * @return Whether the Uri authority is MediaProvider.
+ */
+ fun isMediaDocument(uri: Uri): Boolean {
+ return "com.android.providers.media.documents" == uri.authority
+ }
+
+ /**
+ * @param uri The Uri to check.
+ * @return Whether the Uri authority is Google Photos.
+ */
+ fun isGooglePhotosUri(uri: Uri): Boolean {
+ return "com.google.android.apps.photos.content" == uri.authority
+ }
+
+}
diff --git a/app/src/main/java/io/legado/app/utils/GsonExtensions.kt b/app/src/main/java/io/legado/app/utils/GsonExtensions.kt
new file mode 100644
index 000000000..7a5b315c9
--- /dev/null
+++ b/app/src/main/java/io/legado/app/utils/GsonExtensions.kt
@@ -0,0 +1,5 @@
+package io.legado.app.utils
+
+import com.google.gson.Gson
+
+inline fun Gson.fromJson(json: String) = fromJson(json, T::class.java)
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/utils/ViewExtensions.kt b/app/src/main/java/io/legado/app/utils/ViewExtensions.kt
new file mode 100644
index 000000000..18c802010
--- /dev/null
+++ b/app/src/main/java/io/legado/app/utils/ViewExtensions.kt
@@ -0,0 +1,20 @@
+package io.legado.app.utils
+
+import android.content.Context
+import android.os.Build
+import android.view.View
+import android.view.inputmethod.InputMethodManager
+import io.legado.app.App
+
+fun View.hidehideSoftInput() = run {
+ val imm = App.INSTANCE.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager
+ imm?.let {
+ imm.hideSoftInputFromWindow(this.windowToken, 0)
+ }
+}
+
+fun View.disableAutoFill() = run {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ this.importantForAutofill = View.IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_explore_black_24dp.xml b/app/src/main/res/drawable/ic_explore_black_24dp.xml
new file mode 100644
index 000000000..d841826b0
--- /dev/null
+++ b/app/src/main/res/drawable/ic_explore_black_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_library_books_black_24dp.xml b/app/src/main/res/drawable/ic_library_books_black_24dp.xml
new file mode 100644
index 000000000..06deda209
--- /dev/null
+++ b/app/src/main/res/drawable/ic_library_books_black_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_person_black_24dp.xml b/app/src/main/res/drawable/ic_person_black_24dp.xml
new file mode 100644
index 000000000..9f1acc792
--- /dev/null
+++ b/app/src/main/res/drawable/ic_person_black_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_storage_black_24dp.xml b/app/src/main/res/drawable/ic_storage_black_24dp.xml
new file mode 100644
index 000000000..b11623929
--- /dev/null
+++ b/app/src/main/res/drawable/ic_storage_black_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/layout/activity_about.xml b/app/src/main/res/layout/activity_about.xml
index afb70931e..30f83b7ce 100644
--- a/app/src/main/res/layout/activity_about.xml
+++ b/app/src/main/res/layout/activity_about.xml
@@ -1,8 +1,14 @@
-
+
-
+
+
+
diff --git a/app/src/main/res/layout/activity_bookshelf.xml b/app/src/main/res/layout/activity_bookshelf.xml
new file mode 100644
index 000000000..49c8c9d5d
--- /dev/null
+++ b/app/src/main/res/layout/activity_bookshelf.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_qrcode_capture.xml b/app/src/main/res/layout/activity_qrcode_capture.xml
new file mode 100644
index 000000000..0bdc89c35
--- /dev/null
+++ b/app/src/main/res/layout/activity_qrcode_capture.xml
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_input.xml b/app/src/main/res/layout/dialog_input.xml
new file mode 100644
index 000000000..e867c1792
--- /dev/null
+++ b/app/src/main/res/layout/dialog_input.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_bookshelf.xml b/app/src/main/res/layout/fragment_bookshelf.xml
index e018605d4..722707e63 100644
--- a/app/src/main/res/layout/fragment_bookshelf.xml
+++ b/app/src/main/res/layout/fragment_bookshelf.xml
@@ -11,14 +11,31 @@
android:layout_height="wrap_content"
app:attachToActivity="false"
app:title="@string/bookshelf"/>
-
+
-
+ android:layout_height="wrap_content"
+ android:background="@color/background"
+ android:elevation="3dp"
+ android:padding="5dp"
+ android:text="@string/recent_reading"/>
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_book_group.xml b/app/src/main/res/layout/item_book_group.xml
index dea00be8b..1fd1e0d0e 100644
--- a/app/src/main/res/layout/item_book_group.xml
+++ b/app/src/main/res/layout/item_book_group.xml
@@ -1,5 +1,8 @@
\ No newline at end of file
+ android:padding="5dp" />
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_bookshelf_list_add.xml b/app/src/main/res/layout/item_bookshelf_list_add.xml
new file mode 100644
index 000000000..50282f4a8
--- /dev/null
+++ b/app/src/main/res/layout/item_bookshelf_list_add.xml
@@ -0,0 +1,7 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/activity_main_bnv.xml b/app/src/main/res/menu/activity_main_bnv.xml
index 8cdb43eea..8794b7993 100644
--- a/app/src/main/res/menu/activity_main_bnv.xml
+++ b/app/src/main/res/menu/activity_main_bnv.xml
@@ -4,20 +4,20 @@
tools:showIn="bottom_navigation_view">
+ android:id="@+id/menu_bookshelf"
+ android:icon="@drawable/ic_library_books_black_24dp"
+ android:title="@string/bookshelf"/>
+ android:id="@+id/menu_find_book"
+ android:icon="@drawable/ic_explore_black_24dp"
+ android:title="@string/find"/>
+ android:id="@+id/menu_book_source"
+ android:icon="@drawable/ic_storage_black_24dp"
+ android:title="@string/book_source"/>
+ android:id="@+id/menu_my_config"
+ android:icon="@drawable/ic_person_black_24dp"
+ android:title="@string/my"/>
diff --git a/app/src/main/res/menu/book_source.xml b/app/src/main/res/menu/book_source.xml
index f5fe9609c..bbae899ff 100644
--- a/app/src/main/res/menu/book_source.xml
+++ b/app/src/main/res/menu/book_source.xml
@@ -72,7 +72,7 @@
app:showAsAction="never" />
diff --git a/app/src/main/res/menu/qr_code_scan.xml b/app/src/main/res/menu/qr_code_scan.xml
new file mode 100644
index 000000000..00bb7ec15
--- /dev/null
+++ b/app/src/main/res/menu/qr_code_scan.xml
@@ -0,0 +1,10 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml
index 465af3a30..2e4227f8b 100644
--- a/app/src/main/res/values-night/colors.xml
+++ b/app/src/main/res/values-night/colors.xml
@@ -11,7 +11,7 @@
#363636
#804D4D4D
#80686868
- #88111111
+ #80C7C7C7
#66666666
#737373
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 27e97eb27..45265daac 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -1,7 +1,7 @@
- @color/md_teal_800
- @color/md_teal_900
+ @color/md_light_blue_500
+ @color/md_light_blue_600
@color/md_pink_A400
#222222
#66666666
@@ -23,7 +23,7 @@
#80ACACAC
#80858585
- #88000000
+ #802C2C2C
#737373
#adadad
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 9f1a444a3..d6ba1a7ff 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -33,6 +33,7 @@
启用
书架
+ 最近阅读
最后阅读
让阅读成为一种习惯。
更新日志
@@ -470,4 +471,5 @@
编辑发现
切换软件显示在桌面的图标
帮助
+ 我的
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 3b8558198..1d6fd88d1 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -48,13 +48,7 @@
- @color/md_grey_900
-
+
//**************************************************************System
Style******************************************************************************//
diff --git a/app/src/main/res/xml/pref_config_theme.xml b/app/src/main/res/xml/pref_config_theme.xml
index 510ce3603..f5013bd7b 100644
--- a/app/src/main/res/xml/pref_config_theme.xml
+++ b/app/src/main/res/xml/pref_config_theme.xml
@@ -31,7 +31,7 @@
android:title="@string/e_ink_mode"
app:iconSpaceReserved="false" />
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file