# 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 |
||||
|
||||
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<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"?> |
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
xmlns:tools="http://schemas.android.com/tools" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="match_parent" |
||||
tools:context="io.legado.app.ui.about.AboutActivity"> |
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
xmlns:tools="http://schemas.android.com/tools" |
||||
xmlns:app="http://schemas.android.com/apk/res-auto" |
||||
android:layout_width="match_parent" android:layout_height="match_parent" |
||||
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"?> |
||||
<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_width="match_parent" |
||||
android:layout_height="match_parent" /> |
||||
android:padding="5dp" /> |
@ -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