# Conflicts: # app/src/main/res/layout/fragment_bookshelf.xmlpull/32/head
commit
fe05789ebc
@ -0,0 +1,6 @@ |
|||||||
|
package io.legado.app.data.entities |
||||||
|
|
||||||
|
interface BaseBook { |
||||||
|
var variableMap: HashMap<String, String>? |
||||||
|
fun putVariable(key: String, value: String) |
||||||
|
} |
@ -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) |
||||||
|
} |
||||||
|
} |
@ -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<BookshelfViewModel>() { |
||||||
|
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 |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -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 |
||||||
|
|
||||||
|
} |
@ -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<AndroidViewModel>(), 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) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -1,5 +1,235 @@ |
|||||||
package io.legado.app.utils |
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.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.* |
||||||
|
|
||||||
|
|
||||||
|
object FileUtils { |
||||||
fun getSdPath() = Environment.getExternalStorageDirectory().absolutePath |
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<String>? { |
||||||
|
|
||||||
|
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<String>() |
||||||
|
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<String> { |
||||||
|
val paths = ArrayList<String>() |
||||||
|
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>? |
||||||
|
): 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 |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
@ -0,0 +1,5 @@ |
|||||||
|
package io.legado.app.utils |
||||||
|
|
||||||
|
import com.google.gson.Gson |
||||||
|
|
||||||
|
inline fun <reified T> Gson.fromJson(json: String) = fromJson(json, T::class.java) |
@ -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 |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,9 @@ |
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
android:width="24dp" |
||||||
|
android:height="24dp" |
||||||
|
android:viewportWidth="24.0" |
||||||
|
android:viewportHeight="24.0"> |
||||||
|
<path |
||||||
|
android:fillColor="#FF000000" |
||||||
|
android:pathData="M12,10.9c-0.61,0 -1.1,0.49 -1.1,1.1s0.49,1.1 1.1,1.1c0.61,0 1.1,-0.49 1.1,-1.1s-0.49,-1.1 -1.1,-1.1zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM14.19,14.19L6,18l3.81,-8.19L18,6l-3.81,8.19z"/> |
||||||
|
</vector> |
@ -0,0 +1,9 @@ |
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
android:width="24dp" |
||||||
|
android:height="24dp" |
||||||
|
android:viewportWidth="24.0" |
||||||
|
android:viewportHeight="24.0"> |
||||||
|
<path |
||||||
|
android:fillColor="#FF000000" |
||||||
|
android:pathData="M4,6L2,6v14c0,1.1 0.9,2 2,2h14v-2L4,20L4,6zM20,2L8,2c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM19,11L9,11L9,9h10v2zM15,15L9,15v-2h6v2zM19,7L9,7L9,5h10v2z"/> |
||||||
|
</vector> |
@ -0,0 +1,9 @@ |
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
android:width="24dp" |
||||||
|
android:height="24dp" |
||||||
|
android:viewportWidth="24.0" |
||||||
|
android:viewportHeight="24.0"> |
||||||
|
<path |
||||||
|
android:fillColor="#FF000000" |
||||||
|
android:pathData="M12,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z"/> |
||||||
|
</vector> |
@ -0,0 +1,9 @@ |
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
android:width="24dp" |
||||||
|
android:height="24dp" |
||||||
|
android:viewportWidth="24.0" |
||||||
|
android:viewportHeight="24.0"> |
||||||
|
<path |
||||||
|
android:fillColor="#FF000000" |
||||||
|
android:pathData="M2,20h20v-4L2,16v4zM4,17h2v2L4,19v-2zM2,4v4h20L22,4L2,4zM6,7L4,7L4,5h2v2zM2,14h20v-4L2,10v4zM4,11h2v2L4,13v-2z"/> |
||||||
|
</vector> |
@ -1,8 +1,14 @@ |
|||||||
<?xml version="1.0" encoding="utf-8"?> |
<?xml version="1.0" encoding="utf-8"?> |
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" |
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
xmlns:tools="http://schemas.android.com/tools" |
xmlns:tools="http://schemas.android.com/tools" |
||||||
android:layout_width="match_parent" |
xmlns:app="http://schemas.android.com/apk/res-auto" |
||||||
android:layout_height="match_parent" |
android:layout_width="match_parent" android:layout_height="match_parent" |
||||||
tools:context="io.legado.app.ui.about.AboutActivity"> |
tools:context="io.legado.app.ui.about.AboutActivity"> |
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout> |
<io.legado.app.ui.widget.TitleBar |
||||||
|
android:id="@+id/title_bar" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
app:title="@string/about"/> |
||||||
|
|
||||||
|
</LinearLayout> |
||||||
|
@ -0,0 +1,12 @@ |
|||||||
|
<?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"> |
||||||
|
|
||||||
|
<io.legado.app.ui.widget.TitleBar |
||||||
|
android:id="@+id/title_bar" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content" /> |
||||||
|
|
||||||
|
</LinearLayout> |
@ -0,0 +1,80 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="match_parent"> |
||||||
|
|
||||||
|
<cn.bingoogolapple.qrcode.zxing.ZXingView |
||||||
|
android:id="@+id/zxingview" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="match_parent" |
||||||
|
app:qrcv_animTime="1000" |
||||||
|
app:qrcv_barCodeTipText="将条码放入框内,即可自动扫描" |
||||||
|
app:qrcv_barcodeRectHeight="120dp" |
||||||
|
app:qrcv_borderColor="@android:color/white" |
||||||
|
app:qrcv_borderSize="1dp" |
||||||
|
app:qrcv_cornerColor="@color/colorPrimaryDark" |
||||||
|
app:qrcv_cornerDisplayType="center" |
||||||
|
app:qrcv_cornerLength="20dp" |
||||||
|
app:qrcv_cornerSize="3dp" |
||||||
|
app:qrcv_isAutoZoom="true" |
||||||
|
app:qrcv_isBarcode="false" |
||||||
|
app:qrcv_isOnlyDecodeScanBoxArea="false" |
||||||
|
app:qrcv_isScanLineReverse="true" |
||||||
|
app:qrcv_isShowDefaultGridScanLineDrawable="false" |
||||||
|
app:qrcv_isShowDefaultScanLineDrawable="true" |
||||||
|
app:qrcv_isShowLocationPoint="true" |
||||||
|
app:qrcv_isShowTipBackground="true" |
||||||
|
app:qrcv_isShowTipTextAsSingleLine="false" |
||||||
|
app:qrcv_isTipTextBelowRect="false" |
||||||
|
app:qrcv_maskColor="#33FFFFFF" |
||||||
|
app:qrcv_qrCodeTipText="将二维码放入框内,即可自动扫描" |
||||||
|
app:qrcv_rectWidth="300dp" |
||||||
|
app:qrcv_scanLineColor="@color/colorPrimaryDark" |
||||||
|
app:qrcv_scanLineMargin="0dp" |
||||||
|
app:qrcv_scanLineSize="0.5dp" |
||||||
|
app:qrcv_tipTextColor="@android:color/white" |
||||||
|
app:qrcv_tipTextSize="12sp" |
||||||
|
app:qrcv_toolbarHeight="?attr/actionBarSize" |
||||||
|
app:qrcv_topOffset="100dp" |
||||||
|
app:qrcv_verticalBias="-1" /> |
||||||
|
|
||||||
|
|
||||||
|
<io.legado.app.ui.widget.TitleBar |
||||||
|
android:id="@+id/title_bar" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
app:title="@string/scan_qr_code" /> |
||||||
|
|
||||||
|
<LinearLayout |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_alignParentStart="true" |
||||||
|
android:layout_alignParentBottom="true" |
||||||
|
android:padding="16dp"> |
||||||
|
|
||||||
|
<Space |
||||||
|
android:layout_width="0dp" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_weight="1" /> |
||||||
|
|
||||||
|
<com.google.android.material.floatingactionbutton.FloatingActionButton |
||||||
|
android:id="@+id/fab_flashlight" |
||||||
|
android:layout_width="wrap_content" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_margin="16dp" |
||||||
|
android:contentDescription="@string/read_aloud" |
||||||
|
android:src="@drawable/ic_daytime" |
||||||
|
android:tint="@color/tv_text_default" |
||||||
|
app:backgroundTint="@color/background_menu" |
||||||
|
app:elevation="2dp" |
||||||
|
app:fabSize="mini" |
||||||
|
app:pressedTranslationZ="2dp" /> |
||||||
|
|
||||||
|
<Space |
||||||
|
android:layout_width="0dp" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_weight="1" /> |
||||||
|
|
||||||
|
</LinearLayout> |
||||||
|
</RelativeLayout> |
@ -0,0 +1,39 @@ |
|||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
xmlns:tools="http://schemas.android.com/tools" |
||||||
|
android:id="@+id/ll_content" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:background="@color/background" |
||||||
|
android:orientation="vertical" |
||||||
|
android:padding="10dp"> |
||||||
|
|
||||||
|
<TextView |
||||||
|
android:id="@+id/tv_title" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_margin="5dp"/> |
||||||
|
|
||||||
|
<io.legado.app.lib.theme.view.ATEAutoCompleteTextView |
||||||
|
android:id="@+id/et_input" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_margin="5dp" |
||||||
|
android:completionThreshold="0" |
||||||
|
android:maxLines="5" |
||||||
|
tools:ignore="LabelFor"/> |
||||||
|
|
||||||
|
<TextView |
||||||
|
android:id="@+id/tv_ok" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_margin="5dp" |
||||||
|
android:background="@drawable/selector_fillet_btn_bg" |
||||||
|
android:clickable="true" |
||||||
|
android:focusable="true" |
||||||
|
android:gravity="center" |
||||||
|
android:paddingTop="5dp" |
||||||
|
android:paddingBottom="5dp" |
||||||
|
android:text="@string/ok" |
||||||
|
android:textColor="@color/tv_text_default"/> |
||||||
|
|
||||||
|
</LinearLayout> |
@ -1,5 +1,8 @@ |
|||||||
<?xml version="1.0" encoding="utf-8"?> |
<?xml version="1.0" encoding="utf-8"?> |
||||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android" |
<TextView xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
android:id="@+id/tv_group" |
||||||
|
android:layout_width="wrap_content" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:background="@drawable/bg_ib_pre" |
||||||
android:layout_margin="5dp" |
android:layout_margin="5dp" |
||||||
android:layout_width="match_parent" |
android:padding="5dp" /> |
||||||
android:layout_height="match_parent" /> |
|
@ -0,0 +1,7 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<TextView xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="60dp" |
||||||
|
android:gravity="center" |
||||||
|
android:text="@string/add" |
||||||
|
android:textSize="24sp" /> |
@ -0,0 +1,10 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"> |
||||||
|
|
||||||
|
<item |
||||||
|
android:id="@+id/action_choose_from_gallery" |
||||||
|
android:title="@string/gallery" |
||||||
|
app:showAsAction="always" /> |
||||||
|
|
||||||
|
</menu> |
Loading…
Reference in new issue