18034727885
2026-05-14 dad2ded3e9e9d2edf55c7ce10e791588b526961f
优化升级相册选择逻辑
升级版本
4个文件已添加
8个文件已修改
455 ■■■■■ 已修改文件
app/build.gradle.kts 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/proguard-rules.pro 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/AndroidManifest.xml 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/example/smart/image/CompressEngine.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/example/smart/image/GlideEngine.java 116 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/example/smart/image/ImageProvider.kt 149 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/example/smart/image/SandboxEngine.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/example/smart/reqeust/RetrofitClient.kt 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/example/smart/ui/item/CreateItemActivity.kt 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/com/example/smart/ui/item/adapter/ImageShowAdapter.kt 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/res/drawable/ps_image_placeholder.xml 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
settings.gradle.kts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/build.gradle.kts
@@ -1,3 +1,6 @@
import java.text.SimpleDateFormat
import java.util.Date
plugins {
    id("com.android.application")
    id("org.jetbrains.kotlin.android")
@@ -19,8 +22,8 @@
        applicationId = "com.example.smart"
        minSdk = 28
        targetSdk = 34
        versionCode = 1103
        versionName = "1.1.03"
        versionCode = 1104
        versionName = "1.1.04"
        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
        vectorDrawables {
@@ -68,6 +71,16 @@
        dataBinding = true
    }
    applicationVariants.configureEach {
        outputs.configureEach {
            if (this is com.android.build.gradle.internal.api.BaseVariantOutputImpl) {
                outputFileName = "岱观智评-${buildType.name}-${versionName}-${
                    SimpleDateFormat("yyyy-MM-dd").format(Date())
                }.apk"
            }
        }
    }
}
dependencies {
@@ -79,7 +92,7 @@
    implementation(project(mapOf("path" to ":basecore")))
    implementation(project(mapOf("path" to ":waterdrop")))
    implementation(project(mapOf("path" to ":imageselector")))
//    implementation(project(mapOf("path" to ":imageselector")))
    implementation("com.google.android.material:material:1.10.0")
    implementation("androidx.legacy:legacy-support-v4:1.0.0")
    implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.6.1")
@@ -108,7 +121,7 @@
    implementation("com.github.loperSeven:DateTimePicker:0.1.0")
    //implementation("com.github.crazyandcoder:citypicker:6.0.2")
    implementation(project(":imageselector"))
//    implementation(project(":imageselector"))
    implementation("com.burhanrashid52:photoeditor:3.0.2")
    testImplementation("junit:junit:4.13.2")
@@ -118,4 +131,12 @@
    // dialog
    implementation("com.afollestad.material-dialogs:core:3.3.0")
    implementation("com.afollestad.material-dialogs:bottomsheets:3.3.0")
    // PictureSelector 基础 (必须)
    implementation("io.github.lucksiege:pictureselector:v3.11.2")
    // 图片压缩 (按需引入)
    implementation("io.github.lucksiege:compress:v3.11.2")
    // 图片裁剪 (按需引入)
    implementation("io.github.lucksiege:ucrop:v3.11.2")
    // 自定义相机 (按需引入)
    implementation("io.github.lucksiege:camerax:v3.11.2")
}
app/proguard-rules.pro
@@ -223,3 +223,11 @@
# 忽略高德地图 SDK 的可选依赖类
-dontwarn com.amap.ams.gnss.GnssSoftLocator
-dontwarn net.jafama.FastMath
#PictureSelector
-keep class com.luck.picture.lib.** { *; }
#如果引入了Camerax库请添加混淆
-keep class com.luck.lib.camerax.** { *; }
#如果引入了Ucrop库请添加混淆
-dontwarn com.yalantis.ucrop**
-keep class com.yalantis.ucrop** { *; }
-keep interface com.yalantis.ucrop** { *; }
app/src/main/AndroidManifest.xml
@@ -1,30 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <!--
     Required only if your app needs to access images or photos
     that other apps created.
    -->
    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
    <!--
 Required only if your app needs to access videos
         that other apps created.
    -->
    <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
    <!--
 Required only if your app needs to access audio files
         that other apps created.
    -->
    <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
    <uses-permission
        android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
        tools:ignore="ScopedStorage" />
    <uses-feature
        android:name="android.hardware.camera"
        android:required="false" />
    <uses-permission
        android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"
        tools:ignore="ProtectedPermissions" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.VIBRATE" />
    <!--    Android 13版本适配,细化存储权限-->
    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
    <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
    <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
    <queries package="${applicationId}">
        <intent>
            <action android:name="android.media.action.IMAGE_CAPTURE" />
        </intent>
        <intent>
            <action android:name="android.media.action.ACTION_VIDEO_CAPTURE" />
        </intent>
    </queries>
    <application
        android:name=".MyApp"
        android:allowBackup="true"
@@ -44,22 +52,6 @@
            android:name="com.amap.api.v2.apikey"
            android:value="f7d1365236067d9c589141debd24f995" /> <!-- 定位需要的服务 -->
        <service android:name="com.amap.api.location.APSService" />
        <!--        图片选择Activity-->
        <activity android:name="com.donkingliang.imageselector.ImageSelectorActivity" />
        <!--        //图片预览Activity-->
        <activity
            android:name="com.donkingliang.imageselector.PreviewActivity"
            android:configChanges="orientation|keyboardHidden|screenSize"
            android:theme="@style/Theme.AppCompat.Light.NoActionBar" />
        <!--        //图片剪切Activity-->
        <activity
            android:name="com.donkingliang.imageselector.ClipImageActivity"
            android:theme="@style/Theme.AppCompat.Light.NoActionBar" />
        <activity
            android:name=".ui.exclude.ExcludeActivity"
app/src/main/java/com/example/smart/image/CompressEngine.java
New file
@@ -0,0 +1,44 @@
package com.example.smart.image;
import android.content.Context;
import android.net.Uri;
import com.luck.picture.lib.engine.CompressFileEngine;
import com.luck.picture.lib.interfaces.OnKeyValueResultCallbackListener;
import java.io.File;
import java.util.ArrayList;
import top.zibin.luban.Luban;
import top.zibin.luban.OnNewCompressListener;
/**
 * 压缩引擎
 */
public class CompressEngine implements CompressFileEngine {
    @Override
    public void onStartCompress(Context context, ArrayList<Uri> source, OnKeyValueResultCallbackListener call) {
        Luban.with(context).load(source).ignoreBy(100)
                .setCompressListener(new OnNewCompressListener() {
                    @Override
                    public void onStart() {
                    }
                    @Override
                    public void onSuccess(String source, File compressFile) {
                        if (call != null) {
                            call.onCallback(source, compressFile.getAbsolutePath());
                        }
                    }
                    @Override
                    public void onError(String source, Throwable e) {
                        if (call != null) {
                            call.onCallback(source, null);
                        }
                    }
                }).launch();
    }
}
app/src/main/java/com/example/smart/image/GlideEngine.java
New file
@@ -0,0 +1,116 @@
package com.example.smart.image;
import android.content.Context;
import android.widget.ImageView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.bitmap.CenterCrop;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.example.smart.R;
import com.luck.picture.lib.engine.ImageEngine;
import com.luck.picture.lib.utils.ActivityCompatHelper;
/**
 * @author:luck
 * @date:2019-11-13 17:02
 * @describe:Glide加载引擎
 */
public class GlideEngine implements ImageEngine {
    /**
     * 加载图片
     *
     * @param context   上下文
     * @param url       资源url
     * @param imageView 图片承载控件
     */
    @Override
    public void loadImage(Context context, String url, ImageView imageView) {
        if (!ActivityCompatHelper.assertValidRequest(context)) {
            return;
        }
        Glide.with(context)
                .load(url)
                .into(imageView);
    }
    @Override
    public void loadImage(Context context, ImageView imageView, String url, int maxWidth, int maxHeight) {
        if (!ActivityCompatHelper.assertValidRequest(context)) {
            return;
        }
        Glide.with(context)
                .load(url)
                .override(maxWidth, maxHeight)
                .into(imageView);
    }
    /**
     * 加载相册目录封面
     *
     * @param context   上下文
     * @param url       图片路径
     * @param imageView 承载图片ImageView
     */
    @Override
    public void loadAlbumCover(Context context, String url, ImageView imageView) {
        if (!ActivityCompatHelper.assertValidRequest(context)) {
            return;
        }
        Glide.with(context)
                .asBitmap()
                .load(url)
                .override(180, 180)
                .sizeMultiplier(0.5f)
                .transform(new CenterCrop(), new RoundedCorners(8))
                .placeholder(R.drawable.ps_image_placeholder)
                .into(imageView);
    }
    /**
     * 加载图片列表图片
     *
     * @param context   上下文
     * @param url       图片路径
     * @param imageView 承载图片ImageView
     */
    @Override
    public void loadGridImage(Context context, String url, ImageView imageView) {
        if (!ActivityCompatHelper.assertValidRequest(context)) {
            return;
        }
        Glide.with(context)
                .load(url)
                .override(200, 200)
                .centerCrop()
                .placeholder(R.drawable.ps_image_placeholder)
                .into(imageView);
    }
    @Override
    public void pauseRequests(Context context) {
        if (!ActivityCompatHelper.assertValidRequest(context)) {
            return;
        }
        Glide.with(context).pauseRequests();
    }
    @Override
    public void resumeRequests(Context context) {
        if (!ActivityCompatHelper.assertValidRequest(context)) {
            return;
        }
        Glide.with(context).resumeRequests();
    }
    private GlideEngine() {
    }
    private static final class InstanceHolder {
        static final GlideEngine instance = new GlideEngine();
    }
    public static GlideEngine createGlideEngine() {
        return InstanceHolder.instance;
    }
}
app/src/main/java/com/example/smart/image/ImageProvider.kt
@@ -1,36 +1,31 @@
package com.example.smart.image
import FileUploader
import android.R
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.Settings
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.FragmentActivity
import com.donkingliang.imageselector.ImageSelectorActivity
import com.donkingliang.imageselector.entry.RequestConfig
import com.donkingliang.imageselector.utils.ImageSelector
import com.example.basecore.util.FileTools
import com.example.basecore.util.LogUtils
import com.example.smart.bean.ImageRef
import com.example.smart.widget.dialog.LoadingDialog
import java.util.ArrayList
import com.luck.picture.lib.basic.PictureSelector
import com.luck.picture.lib.config.SelectMimeType
import com.luck.picture.lib.entity.LocalMedia
import com.luck.picture.lib.interfaces.OnResultCallbackListener
import com.luck.picture.lib.style.PictureSelectorStyle
import com.luck.picture.lib.style.SelectMainStyle
import com.luck.picture.lib.style.TitleBarStyle
class ImageProvider(val activity: Activity, val callback: IImageCallback) {
    private val REQUEST_CODE: Int = 6666
    private var ossObjectName = ""
    private var maxCount = 1
    val loadingDialog: LoadingDialog = LoadingDialog(activity)
    /**
     * oss object name
     */
@@ -54,79 +49,47 @@
    }
    fun showImageSelect(maxCount: Int = 9) {
//        .backResId(android.support.v7.appcompat.R.drawable.abc_ic_ab_back_mtrl_am_alpha) // 标题
//        val config = ISListConfig.Builder() // 是否多选, 默认true
//            .multiSelect(false) // 是否记住上次选中记录, 仅当multiSelect为true的时候配置,默认为true
//            .rememberSelected(false) // “确定”按钮背景色
//            .btnBgColor(Color.GRAY) // “确定”按钮文字颜色
//            .btnTextColor(Color.BLUE) // 使用沉浸式状态栏
//            .statusBarColor(Color.parseColor("#3F51B5")) // 返回图标ResId
//
//            .title("图片") // 标题文字颜色
//            .titleColor(Color.WHITE) // TitleBar背景色
//            .titleBgColor(Color.parseColor("#3F51B5")) // 裁剪大小。needCrop为true的时候配置
//            .cropSize(1, 1, 200, 200)
//            .needCrop(true) // 第一个是否显示相机,默认true
//            .needCamera(false) // 最大选择图片数量,默认9
//            .maxNum(maxCount)
//            .build()
//
//
//// 跳转到图片选择器
//        ISNav.getInstance().toListActivity(activity, config, REQUEST_CODE)
        if (!checkPer()) {
            val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
            intent.data = Uri.parse("package:" + activity.packageName)
            activity.startActivityForResult(intent, 0x00000012)
            return
        }
        val config = RequestConfig()
        config.useCamera = true
        config.isSingle = false
        config.maxSelectCount = maxCount
        config.canPreview = true
        this.maxCount = maxCount
        val intent = Intent(activity, ImageSelectorActivity::class.java)
        intent.putExtra(ImageSelector.KEY_CONFIG, config)
        if (activity is FragmentActivity) {
            val register = activity.activityResultRegistry.register(
                "101",
                ActivityResultContracts.StartActivityForResult()
            ) {
                if (it == null) {
                    return@register
        //https://gitcode.com/gh_mirrors/pict/PictureSelector/?utm_source=gitcode_aigc_v1_t1&index=bottom&type=card&&uuid_tt_dd=10_37197039080-1778553766662-368769&isLogin=9&from_id=156450437&from_link=2b5eaab318b433d2570db6b0ce95c381
        PictureSelector.create(activity)
            .openGallery(SelectMimeType.ofImage())
            .setImageEngine(GlideEngine.createGlideEngine())
            .setCompressEngine(CompressEngine())
            .setSandboxFileEngine(SandboxEngine())
            .isPreviewImage(true)
            .isDisplayCamera(true)
            .setImageSpanCount(3)
            .setSelectorUIStyle(PictureSelectorStyle().apply {//https://github.com/LuckSiege/PictureSelector/wiki/PictureSelector-3.0-%E4%B8%BB%E9%A2%98api%E8%AF%B4%E6%98%8E
                titleBarStyle = TitleBarStyle().apply {
                    isHideCancelButton = true
                }
                onActivityResult(REQUEST_CODE, it.resultCode, it.data)
                selectMainStyle = SelectMainStyle().apply {
                    isCompleteSelectRelativeTop = true//完成按钮从底部放在右上角
                    isPreviewSelectRelativeBottom = true//预览页选择按钮从顶部放在右下角
            }
            register.launch(intent)
        }
    }
    fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        if (resultCode != AppCompatActivity.RESULT_OK) {
            })
            .setMaxSelectNum(maxCount)
            .forResult(object : OnResultCallbackListener<LocalMedia?> {
                override fun onResult(result: ArrayList<LocalMedia?>?) {
                    if (result == null) {
            return
        }
        if (requestCode == REQUEST_CODE && data != null) {
            onSelectPhotoSuccess(data)
                    val filterNotNull = result.filterNotNull()
                    if (filterNotNull.isEmpty()) {
                        return
        }
    }
    private fun onSelectPhotoSuccess(data: Intent) {
        //获取选择器返回的数据
        val images: ArrayList<String> =
            data.getStringArrayListExtra(ImageSelector.SELECT_RESULT) as ArrayList<String>
        val isCameraImage: Boolean = data.getBooleanExtra(ImageSelector.IS_CAMERA_IMAGE, false)
                    val images = filterNotNull.mapNotNull { it.getAvailablePath() }
        LogUtils.e("images:$images")
                    if (images.isEmpty()) {
                        return
                    }
        loadingDialog.show()
        if (maxCount == 1) {
            FileUploader.uploadFile(images[0], callback) {
@@ -139,6 +102,10 @@
        }
    }
                override fun onCancel() {
                }
            })
    }
    /**
     * 上传绘制后的图片
@@ -149,44 +116,6 @@
            loadingDialog.dismiss()
        }
    }
//    private val callBack = OssCallBack()
//
//    private inner class OssCallBack : IOssCallBack {
//        override fun onAllListSuccess(ossPaths: MutableList<EntityRef>) {
//            super.onAllListSuccess(ossPaths)
//            loadingDialog.dismiss()
//        }
//
//        override fun onSuccess(entityRef: EntityRef) {
//            activity.runOnUiThread {
//                if (maxCount == 1) {
//                    loadingDialog.dismiss()
//                }
//                onUploadSuccess(entityRef)
//            }
//        }
//
//        override fun onFailed() {
//            activity.runOnUiThread {
//                loadingDialog.dismiss()
//                onUploadFailed()
//                activity.toast("图片上传失败,请重新选择")
//            }
//        }
//    }
//
//    private fun onUploadSuccess(entity: EntityRef) {
//        synchronized(this) {
//            callback.onUploadSuccess(entity)
//        }
//    }
//
//    private fun onUploadFailed() {
//        synchronized(this) {
//            callback.onUploadFailed()
//        }
//    }
    interface IImageCallback {
app/src/main/java/com/example/smart/image/SandboxEngine.java
New file
@@ -0,0 +1,20 @@
package com.example.smart.image;
import android.content.Context;
import com.luck.picture.lib.engine.UriToFileTransformEngine;
import com.luck.picture.lib.interfaces.OnKeyValueResultCallbackListener;
import com.luck.picture.lib.utils.SandboxTransformUtils;
/**
 * 沙盒引擎 访问沙盒外资源
 */
public class SandboxEngine implements UriToFileTransformEngine {
    @Override
    public void onUriToFileAsyncTransform(Context context, String srcPath, String mineType, OnKeyValueResultCallbackListener call) {
        if (call != null) {
            String sandboxPath = SandboxTransformUtils.copyPathToSandbox(context, srcPath, mineType);
            call.onCallback(srcPath, sandboxPath);
        }
    }
}
app/src/main/java/com/example/smart/reqeust/RetrofitClient.kt
@@ -21,8 +21,8 @@
    private const val DEFAULT_TIMEOUT = 30L
//    const val serverUrl = "http://47.108.113.190:8182/"
    const val serverUrl = "http://192.168.110.111:8182/"
    const val serverUrl = "http://47.108.113.190:8182/"
//    const val serverUrl = "http://192.168.110.111:8182/"
    private fun rootUrl(): String {
        return serverUrl
app/src/main/java/com/example/smart/ui/item/CreateItemActivity.kt
@@ -390,9 +390,4 @@
        super.onDestroy()
        MemberLiveData.clear()
    }
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        mImageProvider.onActivityResult(requestCode, resultCode, data)
    }
}
app/src/main/java/com/example/smart/ui/item/adapter/ImageShowAdapter.kt
@@ -79,7 +79,7 @@
        }
        holder.clear_img.setOnClickListener {
            data.remove(bean)
            notifyItemRemoved(position)
            notifyDataSetChanged()
        }
    }
app/src/main/res/drawable/ps_image_placeholder.xml
New file
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape>
            <size
                android:width="100dp"
                android:height="100dp" />
            <solid android:color="@color/ps_color_light_grey" />
        </shape>
    </item>
    <item>
        <bitmap
            android:gravity="center"
            android:src="@drawable/ps_ic_placeholder" />
    </item>
</layer-list>
settings.gradle.kts
@@ -19,4 +19,4 @@
include(":app")
include(":basecore")
include(":waterdrop")
include(":imageselector")
//include(":imageselector")