package cn.eeepay.lib_webview.component
import androidx.fragment.app.FragmentActivity
/*
* ================================================
* 描述:定义通用接口
* 作者:zhuangzeqin
* 时间: 2025/4/3 12:26
* 邮箱:zzq@eeepay.cn
* 备注:
* ----------------------------------------------------------------
* You never know what you can do until you try !
* _ _ _ _ ____ _ _ _
* / \ _ __ __| |_ __ ___ (_) __| | / ___|| |_ _ _ __| (_) ___
* / _ \ | '_ \ / _` | '__/ _ | |/ _` | ___ | __| | | |/ _` | |/ _ \
* / ___ | | | | (_| | | | (_) | | (_| | ___) | |_| |_| | (_| | | (_) |
* /_/ __| |_|__,_|_| ___/|_|__,_| |____/ __|__,_|__,_|_|___/
*
* 签名:最痛苦的事不是我失败了,而是我本可以.--zzq
* ----------------------------------------------------------------
* ================================================
*/
interface IComponent {
fun executeFun(activity: FragmentActivity?,vararg params: Pair<String, Any?>,block: (Any?) -> Unit) //执行函数
}
2 具体的实现类; 实现该接口
package cn.eeepay.lib_webview.component
import android.Manifest
import android.content.Intent
import android.os.Build
import android.provider.MediaStore
import android.util.Log
import androidx.fragment.app.FragmentActivity
import cn.eeepay.lib_core.ktx.compressFileData
import cn.eeepay.lib_core.ktx.context
import cn.eeepay.lib_core.utils.ABFileUtil
import cn.eeepay.lib_webview.component.PicturePathTools.Companion.getPicturePath
import com.permissionx.guolindev.PermissionX
import java.io.File
/*
* ================================================
* 描述:相册
* 作者:zhuangzeqin
* 时间: 2025/4/3 13:39
* 邮箱:zzq@eeepay.cn
* 备注:
* ----------------------------------------------------------------
* You never know what you can do until you try !
* _ _ _ _ ____ _ _ _
* / \ _ __ __| |_ __ ___ (_) __| | / ___|| |_ _ _ __| (_) ___
* / _ \ | '_ \ / _` | '__/ _ | |/ _` | ___ | __| | | |/ _` | |/ _ \
* / ___ | | | | (_| | | | (_) | | (_| | ___) | |_| |_| | (_| | | (_) |
* /_/ __| |_|__,_|_| ___/|_|__,_| |____/ __|__,_|__,_|_|___/
*
* 签名:最痛苦的事不是我失败了,而是我本可以.--zzq
* ----------------------------------------------------------------
* ================================================
*/
class AlbumComponent : IComponent {
override fun executeFun(activity: FragmentActivity?,vararg params: Pair<String, Any?>, block: (Any?) -> Unit) {
activity ?: return
PermissionX.init(activity)
.permissions(
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE,
)
.onExplainRequestReason { scope, deniedList ->
scope.showRequestReasonDialog(deniedList, "你拒绝了这些权限", "继续申请", "取消")
}
.onForwardToSettings { scope, deniedList ->
scope.showForwardToSettingsDialog(
deniedList,
activity.getString(cn.eeepay.lib_res.R.string.lib_res_phone_check_hint),
"确定",
"取消"
)
}
.request { allGranted, grantedList, deniedList ->
if (allGranted) {//全部授予
goAlbumPage(activity){
block.invoke(it)
}
} else {//拒绝
// showMsg("这些权限被拒绝:$deniedList")
}
}
}
/**
* 跳转相册
*/
private fun goAlbumPage(activity: FragmentActivity?,block: (String?) -> Unit) {
activity ?: return
//TODO 兼容三星等低版本手机系统选择相册返回异常崩溃问题 2022年8月29日 19:57:58 liangan
var openAlbumIntent: Intent? = null
if (Build.VERSION.SDK_INT < 19) {
openAlbumIntent = Intent(Intent.ACTION_GET_CONTENT)
openAlbumIntent.type = "image/*"
} else {
openAlbumIntent =
Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
}
activity.gotoIntentForResult(intent = openAlbumIntent) { data ->
val uri = data?.data
val path = getPicturePath(activity.context)
path ?: return@gotoIntentForResult
var imgFile = File(path)
val filePathColumn = arrayOf(MediaStore.Images.Media.DATA)
val cursor = activity.contentResolver.query(uri!!, filePathColumn, null, null, null)
if (cursor != null) {
cursor.moveToFirst()
val columnIndex = cursor.getColumnIndex(filePathColumn[0])
val picPath = cursor.getString(columnIndex)
cursor.close()
imgFile = ABFileUtil.write2SD(picPath, imgFile.path)
Log.d("goAlbumPage", "gotoIntentForResult: " + imgFile.isFile)
Log.d("goAlbumPage", "gotoIntentForResult: " + imgFile.length())
}
//1 获取图片路径
val picturePath = imgFile.absolutePath
//2 压缩文件
val compressFile = activity.compressFileData(File(picturePath))
//1 路径
block.invoke(compressFile.absolutePath)
}
//改造一下; 方便使用
// activity.startActivityForResult(openAlbumIntent, CommonConstants.requestCode100)
// //这里用到了startActivityForResult 需要各自的页面去实现--->onActivityResult回调 CommonConstants.requestCode100 -> {//相册}
}
}
package cn.eeepay.lib_webview.component
import android.Manifest
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.provider.MediaStore
import androidx.core.content.FileProvider
import androidx.fragment.app.FragmentActivity
import cn.eeepay.lib_core.ktx.compressFileData
import cn.eeepay.lib_core.ktx.context
import cn.eeepay.lib_webview.component.PicturePathTools.Companion.getPicturePath
import com.permissionx.guolindev.PermissionX
import java.io.File
/*
* ================================================
* 描述:拍照
* 作者:zhuangzeqin
* 时间: 2025/4/3 12:33
* 邮箱:zzq@eeepay.cn
* 备注:
* ----------------------------------------------------------------
* You never know what you can do until you try !
* _ _ _ _ ____ _ _ _
* / \ _ __ __| |_ __ ___ (_) __| | / ___|| |_ _ _ __| (_) ___
* / _ \ | '_ \ / _` | '__/ _ | |/ _` | ___ | __| | | |/ _` | |/ _ \
* / ___ | | | | (_| | | | (_) | | (_| | ___) | |_| |_| | (_| | | (_) |
* /_/ __| |_|__,_|_| ___/|_|__,_| |____/ __|__,_|__,_|_|___/
*
* 签名:最痛苦的事不是我失败了,而是我本可以.--zzq
* ----------------------------------------------------------------
* ================================================
*/
class CameraComponent : IComponent {
override fun executeFun(activity: FragmentActivity?,vararg params: Pair<String, Any?>, block: (Any?) -> Unit) {
activity ?: return
PermissionX.init(activity)
.permissions(
Manifest.permission.CAMERA,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE,
)
.onExplainRequestReason { scope, deniedList ->
scope.showRequestReasonDialog(deniedList, "你拒绝了这些权限", "继续申请", "取消")
}
.onForwardToSettings { scope, deniedList ->
scope.showForwardToSettingsDialog(
deniedList,
activity.getString(cn.eeepay.lib_res.R.string.lib_res_camera_check_hint),
"确定",
"取消"
)
}
.request { allGranted, grantedList, deniedList ->
if (allGranted) {//全部授予
openCamera(activity) {
block.invoke(it)
}
} else {//拒绝
// showMsg("这些权限被拒绝:$deniedList")
}
}
}
private fun openCamera(activity: FragmentActivity?, block: (String?) -> Unit) {
activity ?: return
val path = getPicturePath(activity.context)
path ?: return
val imgFile = File(path)
// 判断版本大于等于7.0
// add by zhuangzeqin 2017年11月29日09:44:33 android 7.0 因为Uri.fromFile引起的FileUriExposedException异常
// 判断版本大于等于7.0
val intent1 = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
val data: Uri
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
data = FileProvider.getUriForFile(
activity.context,
activity.context.packageName.toString() + ".FileProvider",
imgFile
)
// 给目标应用一个临时授权
intent1.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
} else {
data = Uri.fromFile(imgFile)
}
intent1.putExtra(MediaStore.EXTRA_OUTPUT, data)
activity.gotoIntentForResult(intent = intent1){
//1 获取图片路径
val picturePath = imgFile.absolutePath
//2 压缩文件
val compressFile = activity.compressFileData(File(picturePath))
//1 路径
block.invoke(compressFile.absolutePath)
}
// activity.startActivityForResult(intent1, CommonConstants.requestCode101)
//这里用到了startActivityForResult 需要各自的页面去实现--->onActivityResult回调 CommonConstants.requestCode101 -> {//拍照}
// val uri = Uri.parse("file:///path/to/your/file.txt")
// val file = File(uri.path)
// val filePath = file.absolutePath
}
}
package cn.eeepay.lib_webview.component
import android.Manifest
import android.content.Intent
import android.text.TextUtils
import android.widget.ImageView
import androidx.core.content.ContextCompat
import androidx.fragment.app.FragmentActivity
import cn.eeepay.lib_core.ktx.context
import cn.eeepay.lib_core.utils.ColorUtil
import cn.eeepay.lib_webview.R
import cn.eeepay.net.show
import com.maning.mlkitscanner.scan.MNScanManager
import com.maning.mlkitscanner.scan.model.MNScanConfig
import com.permissionx.guolindev.PermissionX
/*
* ================================================
* 描述:扫一扫
* 作者:zhuangzeqin
* 时间: 2025/4/3 11:54
* 邮箱:zzq@eeepay.cn
* 备注:
* ----------------------------------------------------------------
* You never know what you can do until you try !
* _ _ _ _ ____ _ _ _
* / \ _ __ __| |_ __ ___ (_) __| | / ___|| |_ _ _ __| (_) ___
* / _ \ | '_ \ / _` | '__/ _ | |/ _` | ___ | __| | | |/ _` | |/ _ \
* / ___ | | | | (_| | | | (_) | | (_| | ___) | |_| |_| | (_| | | (_) |
* /_/ __| |_|__,_|_| ___/|_|__,_| |____/ __|__,_|__,_|_|___/
*
* 签名:最痛苦的事不是我失败了,而是我本可以.--zzq
* ----------------------------------------------------------------
* ================================================
*/
class OpenQrCodeComponent : IComponent {
override fun executeFun(activity: FragmentActivity?,vararg params: Pair<String, Any?>, block: (Any?) -> Unit) {
activity ?: return
PermissionX.init(activity)
.permissions(
Manifest.permission.CAMERA,
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
)
.onExplainRequestReason { scope, deniedList ->
scope.showRequestReasonDialog(deniedList, "你拒绝了这些权限", "继续申请", "取消")
}
.onForwardToSettings { scope, deniedList ->
scope.showForwardToSettingsDialog(
deniedList,
activity.getString(cn.eeepay.lib_res.R.string.lib_res_camera_check_hint),
"确定",
"取消"
)
}
.request { allGranted, grantedList, deniedList ->
if (allGranted) {//全部授予
toScanData(activity){
block.invoke(it)
}
} else {//拒绝
activity.getString(cn.eeepay.lib_res.R.string.lib_res_permissiontip).show()
}
}
}
private var colorLine = "#3888EC"
private var colorResultPoint = "#3888EC"
private val colorResultPointStroke = "#FFFFFFFF"
private var colorBackground = "#616C82"
//自定义扫描号
private fun toScanData(activity: FragmentActivity?, block: (String) -> Unit) {
activity ?: return
colorLine = ColorUtil.color2HexARGB(ContextCompat.getColor(activity.context, cn.eeepay.lib_res.R.color.lib_res_themeBtnColor))
colorResultPoint =
ColorUtil.color2HexARGB(ContextCompat.getColor(activity.context, cn.eeepay.lib_res.R.color.lib_res_themeBtnColor))
val scanConfig = MNScanConfig.Builder()
//设置完成震动
.isShowVibrate(true)
//扫描完成声音
.isShowBeep(true)
//扫描线的颜色
.setScanColor(colorLine)
//是否支持手势缩放
.setSupportZoom(true)
//是否全屏扫描,默认全屏
.setFullScreenScan(true)
//背景颜色
.setBgColor(colorBackground)
.setScanHintText("")
//设置支持的条形码 如果不传默认所有
// .setBarcodeFormats(barcodeFormats)
.setBarcodeFormats(intArrayOf())
//单位dp
.setResultPointConfigs(36, 18, 3, colorResultPointStroke, colorResultPoint)
.setCustomShadeViewLayoutID(R.layout.lib_webview_layout_scan_custom_view) {
val iv_back = it.findViewById<ImageView>(R.id.iv_back)//返回
iv_back.setOnClickListener {
//关闭扫描页面
MNScanManager.closeScanPage()
}
}.builder()
MNScanManager.startScan(
activity, scanConfig
) { resultCode, data ->
handlerResult(resultCode, data) {
block.invoke(it)
}
}
}
//扫码后的处理结果
private fun handlerResult(resultCode: Int, data: Intent?, block: (String) -> Unit) {
if (data == null) {
return
}
when (resultCode) {
MNScanManager.RESULT_SUCCESS -> {
val results = data.getStringArrayListExtra(MNScanManager.INTENT_KEY_RESULT_SUCCESS)
if (results.isNullOrEmpty()) return
val sn = results[0].toString().trim()//机具的sn号
//扫码回来走接口查询明文sn号
if (!TextUtils.isEmpty(sn)) {
block.invoke(sn)
}
//关闭扫描页面
MNScanManager.closeScanPage()
}
MNScanManager.RESULT_FAIL -> {
val resultError = data.getStringExtra(MNScanManager.INTENT_KEY_RESULT_ERROR)
resultError?.show()
}
else -> {}
}
}
}
实现的管理类
package cn.eeepay.lib_webview.component
import androidx.fragment.app.FragmentActivity
/*
* ================================================
* 描述:ComponentManager 管理类方便统一使用
* 作者:zhuangzeqin
* 时间: 2025/4/3 17:44
* 邮箱:zzq@eeepay.cn
* 备注:
* ----------------------------------------------------------------
* You never know what you can do until you try !
* _ _ _ _ ____ _ _ _
* / \ _ __ __| |_ __ ___ (_) __| | / ___|| |_ _ _ __| (_) ___
* / _ \ | '_ \ / _` | '__/ _ | |/ _` | ___ | __| | | |/ _` | |/ _ \
* / ___ | | | | (_| | | | (_) | | (_| | ___) | |_| |_| | (_| | | (_) |
* /_/ __| |_|__,_|_| ___/|_|__,_| |____/ __|__,_|__,_|_|___/
*
* 签名:最痛苦的事不是我失败了,而是我本可以.--zzq
* ----------------------------------------------------------------
* ================================================
* ComponentManager.build { component = AlbumComponent() }.start(activity){
it.toString().show()
dataBinding.ivTestimg.loadImage(it.toString())
}
*/
class ComponentManager(var component: IComponent?) {
constructor(builder: Builder) : this(builder.component)
companion object {
inline fun build(block: Builder.() -> Unit): ComponentManager{
return Builder().apply(block).build()
}
}
class Builder {
var component: IComponent? = null
fun build(): ComponentManager{
return ComponentManager(this)
}
}
/**
* 启动执行
*/
fun start(activity: FragmentActivity?, vararg params: Pair<String, Any?>,block: (Any?) -> Unit) {
this.component?:return
this.component!!.executeFun(activity, *params){
block.invoke(it)
}
}
}
封装Activity Result的API
package cn.eeepay.lib_webview.component
import android.app.Activity
import android.content.Context
import android.content.Intent
import androidx.fragment.app.Fragment
/*
* ================================================
* 描述:
* 封装Activity Result的API
* 使用空Fragemnt的形式调用startActivityForResult并返回回调
* Activty/Fragment——>add GhostFragment——>onAttach中startActivityForResult
* ——>GhostFragment onActivityResult接收结果——>callback回调给Activty/Fragment
* 作者:zhuangzeqin
* 时间: 2025/4/3 15:25
* 邮箱:zzq@eeepay.cn
* 备注:
* ----------------------------------------------------------------
* You never know what you can do until you try !
* _ _ _ _ ____ _ _ _
* / \ _ __ __| |_ __ ___ (_) __| | / ___|| |_ _ _ __| (_) ___
* / _ \ | '_ \ / _` | '__/ _ | |/ _` | ___ | __| | | |/ _` | |/ _ \
* / ___ | | | | (_| | | | (_) | | (_| | ___) | |_| |_| | (_| | | (_) |
* /_/ __| |_|__,_|_| ___/|_|__,_| |____/ __|__,_|__,_|_|___/
*
* 签名:最痛苦的事不是我失败了,而是我本可以.--zzq
* ----------------------------------------------------------------
* ================================================
*/
class GhostFragment : Fragment(){
private var requestCode = -1
private var intent: Intent? = null
private var callback: ((result: Intent?) -> Unit)? = null
fun init(requestCode: Int, intent: Intent, callback: ((result: Intent?) -> Unit)) {
this.requestCode = requestCode
this.intent = intent
this.callback = callback
}
private var activityStarted = false
override fun onAttach(activity: Activity) {
super.onAttach(activity)
if (!activityStarted) {
activityStarted = true
intent?.let { startActivityForResult(it, requestCode) }
}
}
override fun onAttach(context: Context) {
super.onAttach(context)
if (!activityStarted) {
activityStarted = true
intent?.let { startActivityForResult(it, requestCode) }
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK && requestCode == this.requestCode) {
callback?.let { it1 -> it1(data) }
}
}
override fun onDetach() {
super.onDetach()
intent = null
callback = null
}
}
管理GhostFragment用于StartActivityForResult
package cn.eeepay.lib_webview.component
import android.content.Intent
import androidx.fragment.app.FragmentActivity
import cn.eeepay.lib_core.activitymessenger.putExtras
/*
* ================================================
* 描述:
* 管理GhostFragment用于StartActivityForResult
* 启动的时候添加Fragment 返回的时移除Fragment
* 作者:zhuangzeqin
* 时间: 2025/4/3 17:45
* 邮箱:zzq@eeepay.cn
* 备注:
* ----------------------------------------------------------------
* You never know what you can do until you try !
* _ _ _ _ ____ _ _ _
* / \ _ __ __| |_ __ ___ (_) __| | / ___|| |_ _ _ __| (_) ___
* / _ \ | '_ \ / _` | '__/ _ | |/ _` | ___ | __| | | |/ _` | |/ _ \
* / ___ | | | | (_| | | | (_) | | (_| | ___) | |_| |_| | (_| | | (_) |
* /_/ __| |_|__,_|_| ___/|_|__,_| |____/ __|__,_|__,_|_|___/
*
* 签名:最痛苦的事不是我失败了,而是我本可以.--zzq
* ----------------------------------------------------------------
* ================================================
*/
object Ghost {
var requestCode = 0
set(value) {
field = if (value >= Integer.MAX_VALUE) 1 else value
}
inline fun launchActivityForResult(
starter: FragmentActivity?,
intent: Intent,
crossinline callback: ((result: Intent?) -> Unit)
) {
starter ?: return
val fm = starter.supportFragmentManager
val fragment = GhostFragment()
fragment.init(++requestCode, intent) { result ->
callback(result)
fm.beginTransaction().remove(fragment).commitAllowingStateLoss()
}
fm.beginTransaction().add(fragment, GhostFragment::class.java.simpleName)
.commitAllowingStateLoss()
}
}
//真正执行AcytivityForResult的方法,使用Ghost的方式执行
inline fun <reified T> FragmentActivity.gotoActivityForResult(
flag: Int = -1,
vararg params: Pair<String, Any?>,
crossinline callback: ((result: Intent?) -> Unit)
) {
val intent = Intent(this, T::class.java).apply {
if (flag != -1) {
this.addFlags(flag)
}
putExtras(*params)
}
Ghost.launchActivityForResult(this, intent, callback)
}
inline fun FragmentActivity.gotoIntentForResult(
flag: Int = -1,
intent:Intent?,
vararg params: Pair<String, Any?>,
crossinline callback: ((result: Intent?) -> Unit)
) {
intent?:return
intent.apply {
if (flag != -1) {
this.addFlags(flag)
}
putExtras(*params)
}
Ghost.launchActivityForResult(this, intent, callback)
}
抽离出图片路径的工具类
package cn.eeepay.lib_webview.component
import android.content.Context
import android.os.Build
import android.os.Environment
import cn.eeepay.lib_core.utils.ABFileUtil
import java.io.File
import java.text.SimpleDateFormat
import java.util.*
/*
* ================================================
* 描述:抽离出图片路径的工具类;
* 作者:zhuangzeqin
* 时间: 2025/4/3 16:08
* 邮箱:zzq@eeepay.cn
* 备注:
* ----------------------------------------------------------------
* You never know what you can do until you try !
* _ _ _ _ ____ _ _ _
* / \ _ __ __| |_ __ ___ (_) __| | / ___|| |_ _ _ __| (_) ___
* / _ \ | '_ \ / _` | '__/ _ | |/ _` | ___ | __| | | |/ _` | |/ _ \
* / ___ | | | | (_| | | | (_) | | (_| | ___) | |_| |_| | (_| | | (_) |
* /_/ __| |_|__,_|_| ___/|_|__,_| |____/ __|__,_|__,_|_|___/
*
* 签名:最痛苦的事不是我失败了,而是我本可以.--zzq
* ----------------------------------------------------------------
* ================================================
*/
class PicturePathTools {
companion object{
fun getPicturePath(context: Context): String? {
// String cameraPath = Environment.getExternalStorageDirectory().getPath() + File.separator + "DCIM" + File.separator + "Camera";
var cameraPath: String? = null
cameraPath = if (ABFileUtil.checkSD()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)
.toString() + File.separator + "DCIM" + File.separator + "Camera"
} else {
Environment.getExternalStorageDirectory().path + File.separator + "DCIM" + File.separator + "Camera"
}
} else {
context.filesDir.toString() + File.separator
}
val cameraFolder = File(cameraPath)
if (!cameraFolder.exists()) {
cameraFolder.mkdirs()
}
val simpleDateFormat = SimpleDateFormat("yyyyMMdd_HHmmss")
return cameraFolder.absolutePath + File.separator + "IMG_" + simpleDateFormat.format(Date()) + ".jpg"
}
}
}
最终使用
ComponentManager.build { component = AlbumComponent() }.start(activity){ it.toString().show() dataBinding.ivTestimg.loadImage(it.toString()) }