Merge branch 'master' into master

pull/43/head
kunfei 5 years ago committed by GitHub
commit 81140034e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      app/build.gradle
  2. 12
      app/src/main/java/io/legado/app/data/dao/BookSourceDao.kt
  3. 6
      app/src/main/java/io/legado/app/model/webbook/BookChapterList.kt
  4. 2
      app/src/main/java/io/legado/app/model/webbook/BookContent.kt
  5. 22
      app/src/main/java/io/legado/app/ui/book/search/SearchActivity.kt
  6. 23
      app/src/main/java/io/legado/app/ui/book/search/SearchViewModel.kt
  7. 13
      app/src/main/java/io/legado/app/ui/book/source/debug/BookSourceDebugAdapter.kt
  8. 5
      app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt
  9. 12
      app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceViewModel.kt
  10. 47
      app/src/main/java/io/legado/app/ui/widget/page/curl/CurlRenderer.kt
  11. 8
      app/src/main/java/io/legado/app/ui/widget/page/curl/CurlView.kt
  12. 1
      app/src/main/java/io/legado/app/ui/widget/page/delegate/SimulationPageDelegate.kt
  13. 32
      app/src/main/java/io/legado/app/utils/FileUtils.kt
  14. 1
      app/src/main/res/values/strings.xml

@ -87,7 +87,7 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
//fireBase //fireBase
implementation 'com.google.firebase:firebase-core:17.2.0' implementation 'com.google.firebase:firebase-core:17.2.1'
implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1' implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1'
//androidX //androidX
@ -97,8 +97,8 @@ dependencies {
implementation 'androidx.preference:preference:1.1.0' implementation 'androidx.preference:preference:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.0.0' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.0.0'
implementation 'androidx.viewpager2:viewpager2:1.0.0-beta05' implementation 'androidx.viewpager2:viewpager2:1.0.0-rc01'
implementation 'com.google.android.material:material:1.1.0-beta01' implementation 'com.google.android.material:material:1.2.0-alpha01'
implementation 'com.google.android:flexbox:1.1.0' implementation 'com.google.android:flexbox:1.1.0'
//lifecycle //lifecycle
@ -107,7 +107,7 @@ dependencies {
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version" implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
//room //room
def room_version = '2.2.0' def room_version = '2.2.1'
implementation "androidx.room:room-runtime:$room_version" implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version" kapt "androidx.room:room-compiler:$room_version"

@ -29,14 +29,14 @@ interface BookSourceDao {
@Query("select distinct enabled from book_sources where bookSourceName like :searchKey or bookSourceGroup like :searchKey or bookSourceUrl like :searchKey") @Query("select distinct enabled from book_sources where bookSourceName like :searchKey or bookSourceGroup like :searchKey or bookSourceUrl like :searchKey")
fun searchIsEnable(searchKey: String = ""): List<Boolean> fun searchIsEnable(searchKey: String = ""): List<Boolean>
@Query("update book_sources set enabled = 1 where bookSourceUrl in (:sourceUrls)") @Query("update book_sources set enabled = 1 where bookSourceUrl = :sourceUrl")
fun enableSection(vararg sourceUrls: String) fun enableSection(sourceUrl: String)
@Query("update book_sources set enabled = 0 where bookSourceUrl in (:sourceUrls)") @Query("update book_sources set enabled = 0 where bookSourceUrl = :sourceUrl")
fun disableSection(vararg sourceUrls: String) fun disableSection(sourceUrl: String)
@Query("delete from book_sources where bookSourceUrl in (:sourceUrls)") @Query("delete from book_sources where bookSourceUrl = :sourceUrl")
fun delSection(vararg sourceUrls: String) fun delSection(sourceUrl: String)
@Query("select * from book_sources where enabledExplore = 1 order by customOrder asc") @Query("select * from book_sources where enabledExplore = 1 order by customOrder asc")
fun observeFind(): DataSource.Factory<Int, BookSource> fun observeFind(): DataSource.Factory<Int, BookSource>

@ -58,7 +58,8 @@ object BookChapterList {
tocRule, tocRule,
listRule, listRule,
book, book,
bookSource bookSource,
printLog = false
) )
nextUrl = if (chapterData.nextUrl.isNotEmpty()) nextUrl = if (chapterData.nextUrl.isNotEmpty())
chapterData.nextUrl[0] chapterData.nextUrl[0]
@ -88,8 +89,7 @@ object BookChapterList {
tocRule, tocRule,
listRule, listRule,
book, book,
bookSource, bookSource
getNextUrl = false
) )
item.chapterList = nextChapterData.chapterList item.chapterList = nextChapterData.chapterList
} }

@ -123,7 +123,7 @@ object BookContent {
analyzeRule.getStringList(nextUrlRule, true)?.let { analyzeRule.getStringList(nextUrlRule, true)?.let {
nextUrlList.addAll(it) nextUrlList.addAll(it)
} }
SourceDebug.printLog(bookSource.bookSourceUrl, "" + nextUrlList.joinToString(",")) SourceDebug.printLog(bookSource.bookSourceUrl, "" + nextUrlList.joinToString(","), printLog)
} }
val content = analyzeRule.getString(contentRule.content ?: "")?.htmlFormat() ?: "" val content = analyzeRule.getString(contentRule.content ?: "")?.htmlFormat() ?: ""
return ContentData(content, nextUrlList) return ContentData(content, nextUrlList)

@ -99,14 +99,7 @@ class SearchActivity : VMBaseActivity<SearchViewModel>(R.layout.activity_book_se
search_view.clearFocus() search_view.clearFocus()
query?.let { query?.let {
viewModel.saveSearchKey(query) viewModel.saveSearchKey(query)
viewModel.search(it, { viewModel.search(it)
refresh_progress_bar.isAutoLoading = true
initData()
fb_stop.visible()
}, {
refresh_progress_bar.isAutoLoading = false
fb_stop.invisible()
})
} }
return true return true
} }
@ -178,7 +171,7 @@ class SearchActivity : VMBaseActivity<SearchViewModel>(R.layout.activity_book_se
} }
private fun scrollToBottom() { private fun scrollToBottom() {
if (!viewModel.isLoading) { if (!viewModel.isLoading && viewModel.searchKey.isNotEmpty()) {
viewModel.search("") viewModel.search("")
} }
} }
@ -227,6 +220,17 @@ class SearchActivity : VMBaseActivity<SearchViewModel>(R.layout.activity_book_se
historyData?.observe(this, Observer { historyKeyAdapter.setItems(it) }) historyData?.observe(this, Observer { historyKeyAdapter.setItems(it) })
} }
override fun startSearch() {
refresh_progress_bar.isAutoLoading = true
initData()
fb_stop.visible()
}
override fun searchFinally() {
refresh_progress_bar.isAutoLoading = false
fb_stop.invisible()
}
override fun showBookInfo(name: String, author: String) { override fun showBookInfo(name: String, author: String) {
viewModel.getSearchBook(name, author) { searchBook -> viewModel.getSearchBook(name, author) { searchBook ->
searchBook?.let { searchBook?.let {

@ -25,23 +25,22 @@ class SearchViewModel(application: Application) : BaseViewModel(application) {
var isLoading = false var isLoading = false
private var searchBooks = arrayListOf<SearchBook>() private var searchBooks = arrayListOf<SearchBook>()
fun search( fun search(key: String) {
key: String,
start: (() -> Unit)? = null,
finally: (() -> Unit)? = null
) {
task?.cancel() task?.cancel()
if (key.isEmpty()) { if (key.isEmpty() && searchKey.isEmpty()) {
return
} else if (key.isEmpty()) {
isLoading = true
searchPage++ searchPage++
} else { } else if (key.isNotEmpty()) {
isLoading = true
searchPage = 0
searchKey = key searchKey = key
searchBooks.clear() searchBooks.clear()
} }
if (searchKey.isEmpty()) return
startTime = System.currentTimeMillis() startTime = System.currentTimeMillis()
start?.invoke() callBack?.startSearch()
task = execute { task = execute {
isLoading = true
val searchGroup = context.getPrefString("searchGroup") ?: "" val searchGroup = context.getPrefString("searchGroup") ?: ""
val bookSourceList = if (searchGroup.isBlank()) { val bookSourceList = if (searchGroup.isBlank()) {
App.db.bookSourceDao().allEnabled App.db.bookSourceDao().allEnabled
@ -66,7 +65,7 @@ class SearchViewModel(application: Application) : BaseViewModel(application) {
} }
task?.invokeOnCompletion { task?.invokeOnCompletion {
finally?.invoke() callBack?.searchFinally()
isLoading = false isLoading = false
} }
} }
@ -176,5 +175,7 @@ class SearchViewModel(application: Application) : BaseViewModel(application) {
interface CallBack { interface CallBack {
var adapter: SearchAdapter var adapter: SearchAdapter
fun startSearch()
fun searchFinally()
} }
} }

@ -1,6 +1,7 @@
package io.legado.app.ui.book.source.debug package io.legado.app.ui.book.source.debug
import android.content.Context import android.content.Context
import android.view.View
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.adapter.ItemViewHolder import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.SimpleRecyclerAdapter import io.legado.app.base.adapter.SimpleRecyclerAdapter
@ -10,6 +11,18 @@ class BookSourceDebugAdapter(context: Context) :
SimpleRecyclerAdapter<String>(context, R.layout.item_log) { SimpleRecyclerAdapter<String>(context, R.layout.item_log) {
override fun convert(holder: ItemViewHolder, item: String, payloads: MutableList<Any>) { override fun convert(holder: ItemViewHolder, item: String, payloads: MutableList<Any>) {
holder.itemView.apply { holder.itemView.apply {
if (text_view.getTag(R.id.tag1) == null) {
val listener = object : View.OnAttachStateChangeListener {
override fun onViewAttachedToWindow(v: View) {
text_view.isCursorVisible = false
text_view.isCursorVisible = true
}
override fun onViewDetachedFromWindow(v: View) {}
}
text_view.addOnAttachStateChangeListener(listener)
text_view.setTag(R.id.tag1, listener)
}
text_view.text = item text_view.text = item
} }
} }

@ -249,8 +249,11 @@ class BookSourceActivity : VMBaseActivity<BookSourceViewModel>(R.layout.activity
} }
importSource -> if (resultCode == Activity.RESULT_OK) { importSource -> if (resultCode == Activity.RESULT_OK) {
data?.data?.let { data?.data?.let {
FileUtils.getPath(this, it)?.let { path -> val path = FileUtils.getPath(this, it)
if (path != null) {
viewModel.importSourceFromFilePath(path) viewModel.importSourceFromFilePath(path)
} else {
toast(R.string.uri_to_path_fail)
} }
} }
} }

@ -42,19 +42,25 @@ class BookSourceViewModel(application: Application) : BaseViewModel(application)
fun enableSelection(ids: LinkedHashSet<String>) { fun enableSelection(ids: LinkedHashSet<String>) {
execute { execute {
App.db.bookSourceDao().enableSection(*ids.toTypedArray()) ids.forEach {
App.db.bookSourceDao().enableSection(it)
}
} }
} }
fun disableSelection(ids: LinkedHashSet<String>) { fun disableSelection(ids: LinkedHashSet<String>) {
execute { execute {
App.db.bookSourceDao().disableSection(*ids.toTypedArray()) ids.forEach {
App.db.bookSourceDao().disableSection(it)
}
} }
} }
fun delSelection(ids: LinkedHashSet<String>) { fun delSelection(ids: LinkedHashSet<String>) {
execute { execute {
App.db.bookSourceDao().delSection(*ids.toTypedArray()) ids.forEach {
App.db.bookSourceDao().delSection(it)
}
} }
} }

@ -14,11 +14,7 @@ import javax.microedition.khronos.opengles.GL10
* *
* @author harism * @author harism
*/ */
class CurlRenderer class CurlRenderer(private val mObserver: Observer) : GLSurfaceView.Renderer {
/**
* Basic constructor.
*/
(private val mObserver: Observer) : GLSurfaceView.Renderer {
// Background fill color. // Background fill color.
private var mBackgroundColor: Int = 0 private var mBackgroundColor: Int = 0
// Curl meshes used for static and dynamic rendering. // Curl meshes used for static and dynamic rendering.
@ -71,10 +67,6 @@ class CurlRenderer
gl.glClear(GL10.GL_COLOR_BUFFER_BIT) gl.glClear(GL10.GL_COLOR_BUFFER_BIT)
gl.glLoadIdentity() gl.glLoadIdentity()
if (USE_PERSPECTIVE_PROJECTION) {
gl.glTranslatef(0f, 0f, -6f)
}
for (i in mCurlMeshes.indices) { for (i in mCurlMeshes.indices) {
mCurlMeshes[i].onDrawFrame(gl) mCurlMeshes[i].onDrawFrame(gl)
} }
@ -94,14 +86,10 @@ class CurlRenderer
gl.glMatrixMode(GL10.GL_PROJECTION) gl.glMatrixMode(GL10.GL_PROJECTION)
gl.glLoadIdentity() gl.glLoadIdentity()
if (USE_PERSPECTIVE_PROJECTION) { GLU.gluOrtho2D(
GLU.gluPerspective(gl, 20f, width.toFloat() / height, .1f, 100f) gl, mViewRect.left, mViewRect.right,
} else { mViewRect.bottom, mViewRect.top
GLU.gluOrtho2D( )
gl, mViewRect.left, mViewRect.right,
mViewRect.bottom, mViewRect.top
)
}
gl.glMatrixMode(GL10.GL_MODELVIEW) gl.glMatrixMode(GL10.GL_MODELVIEW)
gl.glLoadIdentity() gl.glLoadIdentity()
@ -128,29 +116,6 @@ class CurlRenderer
mCurlMeshes.remove(mesh) mCurlMeshes.remove(mesh)
} }
/**
* Change background/clear color.
*/
fun setBackgroundColor(color: Int) {
mBackgroundColor = color
}
/**
* Set margins or padding. Note: margins are proportional. Meaning a value
* of .1f will produce a 10% margin.
*/
@Synchronized
fun setMargins(
left: Float, top: Float, right: Float,
bottom: Float
) {
mMargins.left = left
mMargins.top = top
mMargins.right = right
mMargins.bottom = bottom
updatePageRects()
}
/** /**
* Sets visible page count to one or two. Should be either SHOW_ONE_PAGE or * Sets visible page count to one or two. Should be either SHOW_ONE_PAGE or
* SHOW_TWO_PAGES. * SHOW_TWO_PAGES.
@ -246,7 +211,5 @@ class CurlRenderer
// Constants for changing view mode. // Constants for changing view mode.
const val SHOW_ONE_PAGE = 1 const val SHOW_ONE_PAGE = 1
const val SHOW_TWO_PAGES = 2 const val SHOW_TWO_PAGES = 2
// Set to true for checking quickly how perspective projection looks.
private const val USE_PERSPECTIVE_PROJECTION = false
} }
} }

@ -410,14 +410,6 @@ class CurlView : GLSurfaceView, View.OnTouchListener, CurlRenderer.Observer {
mEnableTouchPressure = enableTouchPressure mEnableTouchPressure = enableTouchPressure
} }
/**
* Set margins (or padding). Note: margins are proportional. Meaning a value
* of .1f will produce a 10% margin.
*/
fun setMargins(left: Float, top: Float, right: Float, bottom: Float) {
mRenderer.setMargins(left, top, right, bottom)
}
/** /**
* Setter for whether left side page is rendered. This is useful mostly for * Setter for whether left side page is rendered. This is useful mostly for
* situations where right (main) page is aligned to left side of screen and * situations where right (main) page is aligned to left side of screen and

@ -56,7 +56,6 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi
private inner class SizeChangedObserver : CurlView.SizeChangedObserver { private inner class SizeChangedObserver : CurlView.SizeChangedObserver {
override fun onSizeChanged(width: Int, height: Int) { override fun onSizeChanged(width: Int, height: Int) {
pageView.curlView?.setViewMode(CurlView.SHOW_ONE_PAGE) pageView.curlView?.setViewMode(CurlView.SHOW_ONE_PAGE)
pageView.curlView?.setMargins(0f, 0f, 0f, 0f)
} }
} }
} }

@ -92,7 +92,7 @@ object FileUtils {
// ExternalStorageProvider // ExternalStorageProvider
if (isExternalStorageDocument(uri)) { if (isExternalStorageDocument(uri)) {
val docId = DocumentsContract.getDocumentId(uri) val docId = DocumentsContract.getDocumentId(uri)
val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() val split = docId.split(":")
val type = split[0] val type = split[0]
if ("primary".equals(type, ignoreCase = true)) { if ("primary".equals(type, ignoreCase = true)) {
@ -101,7 +101,7 @@ object FileUtils {
} else if (isDownloadsDocument(uri)) { } else if (isDownloadsDocument(uri)) {
val id = DocumentsContract.getDocumentId(uri) val id = DocumentsContract.getDocumentId(uri)
val split = id.split(":").dropLastWhile { it.isEmpty() }.toTypedArray() val split = id.split(":")
val type = split[0] val type = split[0]
if ("raw".equals(type, ignoreCase = true)) { if ("raw".equals(type, ignoreCase = true)) {
//处理某些机型(比如Google Pixel )ID是raw:/storage/emulated/0/Download/c20f8664da05ab6b4644913048ea8c83.mp4 //处理某些机型(比如Google Pixel )ID是raw:/storage/emulated/0/Download/c20f8664da05ab6b4644913048ea8c83.mp4
@ -115,14 +115,13 @@ object FileUtils {
return getDataColumn(context, contentUri, null, null) return getDataColumn(context, contentUri, null, null)
} else if (isMediaDocument(uri)) { } else if (isMediaDocument(uri)) {
val docId = DocumentsContract.getDocumentId(uri) val docId = DocumentsContract.getDocumentId(uri)
val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() val split = docId.split(":".toRegex())
val type = split[0]
var contentUri: Uri? = null val contentUri: Uri = when (split[0]) {
when (type) { "image" -> MediaStore.Images.Media.EXTERNAL_CONTENT_URI
"image" -> contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI "video" -> MediaStore.Video.Media.EXTERNAL_CONTENT_URI
"video" -> contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI "audio" -> MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
"audio" -> contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI else -> uri
} }
val selection = "_id=?" val selection = "_id=?"
@ -137,7 +136,6 @@ object FileUtils {
uri.lastPathSegment uri.lastPathSegment
else else
getDataColumn(context, uri, null, null) getDataColumn(context, uri, null, null)
} else if ("file".equals(uri.scheme, ignoreCase = true)) { } else if ("file".equals(uri.scheme, ignoreCase = true)) {
return uri.path return uri.path
}// File }// File
@ -145,8 +143,8 @@ object FileUtils {
return null return null
} }
fun getDataColumn( private fun getDataColumn(
context: Context, uri: Uri?, selection: String?, context: Context, uri: Uri, selection: String?,
selectionArgs: kotlin.Array<String>? selectionArgs: kotlin.Array<String>?
): String? { ): String? {
@ -155,7 +153,7 @@ object FileUtils {
try { try {
context.contentResolver.query( context.contentResolver.query(
uri!!, uri,
projection, projection,
selection, selection,
selectionArgs, selectionArgs,
@ -177,7 +175,7 @@ object FileUtils {
* @param uri The Uri to check. * @param uri The Uri to check.
* @return Whether the Uri authority is ExternalStorageProvider. * @return Whether the Uri authority is ExternalStorageProvider.
*/ */
fun isExternalStorageDocument(uri: Uri): Boolean { private fun isExternalStorageDocument(uri: Uri): Boolean {
return "com.android.externalstorage.documents" == uri.authority return "com.android.externalstorage.documents" == uri.authority
} }
@ -185,7 +183,7 @@ object FileUtils {
* @param uri The Uri to check. * @param uri The Uri to check.
* @return Whether the Uri authority is DownloadsProvider. * @return Whether the Uri authority is DownloadsProvider.
*/ */
fun isDownloadsDocument(uri: Uri): Boolean { private fun isDownloadsDocument(uri: Uri): Boolean {
return "com.android.providers.downloads.documents" == uri.authority return "com.android.providers.downloads.documents" == uri.authority
} }
@ -193,7 +191,7 @@ object FileUtils {
* @param uri The Uri to check. * @param uri The Uri to check.
* @return Whether the Uri authority is MediaProvider. * @return Whether the Uri authority is MediaProvider.
*/ */
fun isMediaDocument(uri: Uri): Boolean { private fun isMediaDocument(uri: Uri): Boolean {
return "com.android.providers.media.documents" == uri.authority return "com.android.providers.media.documents" == uri.authority
} }
@ -201,7 +199,7 @@ object FileUtils {
* @param uri The Uri to check. * @param uri The Uri to check.
* @return Whether the Uri authority is Google Photos. * @return Whether the Uri authority is Google Photos.
*/ */
fun isGooglePhotosUri(uri: Uri): Boolean { private fun isGooglePhotosUri(uri: Uri): Boolean {
return "com.google.android.apps.photos.content" == uri.authority return "com.google.android.apps.photos.content" == uri.authority
} }

@ -540,5 +540,6 @@
<string name="file_chooser">文件选择</string> <string name="file_chooser">文件选择</string>
<string name="folder_chooser">文件夹选择</string> <string name="folder_chooser">文件夹选择</string>
<string name="bottom_line">我是有底线的</string> <string name="bottom_line">我是有底线的</string>
<string name="uri_to_path_fail">Uri转Path失败</string>
</resources> </resources>

Loading…
Cancel
Save