Android image saving, photo album refreshing (version compatible)

Time:2022-6-10

Common Android functions are commonly used to save pictures. In recent years, with the update of Android version, the permission of APP has been gradually tightened, resulting in more and more compatibility problems for app to store pictures

reason:

  • Manufacturer customized storage mode
  • Different versions are stored in different ways
  • Android Q sandbox mechanism

Problems caused:

  • File storage exception
  • Album does not show downloaded pictures
  • Album showing duplicate downloaded pictures

Android Q (10) added partition storage
The filtered view for external storage can provide access to application specific files and media collections. Therefore, when saving pictures, you need to store them in the specified app folder to save the files

Compatible implementation:

1. handle the problem of Android Q storage address

/**
     *Address discrimination based on Android Q
     *
     * @param context
     * @return
     */
    public static String getPath(Context context) {
        //Equalsignorecase() ignores case
        String fileName = "";
        if (Build.VERSION.SDK_INT >= 29) {
            fileName = context.getExternalFilesDir("").getAbsolutePath() + "/current/";
        } else {
            If ("Xiaomi".Equalsignorecase (build.brand)) {// Xiaomi mobile phone
                fileName = Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera/";
            } else if ("HUAWEI".equalsIgnoreCase(Build.BRAND)) {
                fileName = Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera/";
            } else if ("HONOR".equalsIgnoreCase(Build.BRAND)) {
                fileName = Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera/";
            } else if ("OPPO".equalsIgnoreCase(Build.BRAND)) {
                fileName = Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera/";
            } else if ("vivo".equalsIgnoreCase(Build.BRAND)) {
                fileName = Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera/";
            } else if ("samsung".equalsIgnoreCase(Build.BRAND)) {
                fileName = Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera/";
            } else {
                fileName = Environment.getExternalStorageDirectory().getPath() + "/DCIM/";
            }
        }
        File file = new File(fileName);
        if (file.mkdirs()) {
            return fileName;
        }
        return fileName;
    }

2. judge Android Q version

/**
     *Judge Android Q (10) version
     *
     * @return
     */
    public static boolean isAdndroidQ() {
        return Build.VERSION.SDK_INT >= 29;
    }

3. copy files

/**
     *Copy file
     *
     * @param oldPathName
     * @param newPathName
     * @return
     */
    public static boolean copyFile(String oldPathName, String newPathName) {
        try {
            File oldFile = new File(oldPathName);
            if (!oldFile.exists()) {
                return false;
            } else if (!oldFile.isFile()) {
                return false;
            } else if (!oldFile.canRead()) {
                return false;
            }

            FileInputStream fileInputStream = new FileInputStream(oldPathName);
            FileOutputStream fileOutputStream = new FileOutputStream(newPathName);
            byte[] buffer = new byte[1024];
            int byteRead;
            while (-1 != (byteRead = fileInputStream.read(buffer))) {
                fileOutputStream.write(buffer, 0, byteRead);
            }
            fileInputStream.close();
            fileOutputStream.flush();
            fileOutputStream.close();
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

4. insert album

/**
     *Insert some model adaptations in the photo album (distinguish the mobile system version Android q)
     *
     * @param context
     * @param filePath
     * @return
     */
    public static boolean insertMediaPic(Context context, String filePath) {
        if (TextUtils.isEmpty(filePath)) return false;
        File file = new File(filePath);
        //Judge Android Q (10) version
        if (isAdndroidQ()) {
            if (file == null || !file.exists()) {
                return false;
            } else {
                try {
                    MediaStore.Images.Media.insertImage(context.getContentResolver(), file.getAbsolutePath(), file.getName(), null);
                    return true;
                } catch (Exception e) {
                    e.printStackTrace();
                    return false;
                }
            }
        } else {
            ContentValues values = new ContentValues();
            values.put(MediaStore.Images.Media.DATA, file.getAbsolutePath());
            values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
            values.put(MediaStore.Images.ImageColumns.DATE_TAKEN, System.currentTimeMillis() + "");
            context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
            context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + file.getAbsolutePath())));
            return true;
        }

    }

Project realization

1. Android Q image storage adaptation

1.1 create app under res/xml/ folder_ files. xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <paths xmlns:android="http://schemas.android.com/apk/res/android">
        <external-path
            name="external_files"
            path="" />
        <path>
            <root-path
                name="root_path"
                path="." />
        </path>

        <external-path
            name="camera_photos"
            path="" />

        <external-path
            name="external_storage_root"
            path="." />
        <grant-uri-permission
            android:path="string"
            android:pathPattern="string"
            android:pathPrefix="string" />
    </paths>
</resources>

1.2 AndroidManifest. App in XML_ Files file configuration

Android image saving, photo album refreshing (version compatible)

AndroidManifest.xml.png

2. picture downloading and saving (kotlin Ctrip downloads pictures)

Android image saving, photo album refreshing (version compatible)

Picture storage png
package com.wu.material.activity

import android.Manifest
import android.app.Activity
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import android.text.TextUtils
import android.util.Log
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.databinding.DataBindingUtil
import com.bumptech.glide.Glide
import com.wu.material.R
import com.wu.material.databinding.ActivityCoroutinesBinding
import com.wu.material.util.FileSaveUtil
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.*


/**
 * @author wkq
 *
 *@date March 3, 2022 12:44
 *
 *@des
 *
 */

class CoroutinesActivity : AppCompatActivity() {

    var databinding: ActivityCoroutinesBinding? = null
    //Permission code
    var REQUEST_CODE_LAUNCH = 10011
    var permissionsREAD = arrayOf(
        Manifest.permission.READ_EXTERNAL_STORAGE,
        Manifest.permission.WRITE_EXTERNAL_STORAGE)

    var path = "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Ffile02.16sucai.com%2Fd%2Ffile%2F2014%2F0829%2F372edfeb74c3119b666237bd4af92be5.jpg&refer=http%3A%2F%2Ffile02.16sucai.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1648708406&t=ca9d3a371ddad53fbc5fa074db2090cc"

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        databinding = DataBindingUtil.setContentView<ActivityCoroutinesBinding>(
            this,
            R.layout.activity_coroutines
        )
        //Judgment authority
       var isHave= checkPermissions(this,permissionsREAD,REQUEST_CODE_LAUNCH)
        if (isHave){
            showView()
        }
    }

    private fun showView() {
        Glide.with(this).load(path).into(databinding!!.ivIcon)
        databinding!!.btSave.setOnClickListener {
            savePic(path)
        }
    }




    fun savePic(path: String) {

        //Ctrip
        GlobalScope.launch(Dispatchers.IO) {
            var file = Glide.with([email protected]).asFile().load(path).submit().get()
            Log.e("",file.absolutePath)
            //Folder location
           var parentPath= FileSaveUtil.getPath([email protected])
            //File name
           var fileName= System.currentTimeMillis().toString()+".png"
            //New file file address
            var filePath=parentPath+fileName
            //Copy address (some models will not be copied to the specified folder, and the album will not be updated)
            FileSaveUtil.copyFile(file.path,filePath)

            var isSave=FileSaveUtil.insertMediaPic([email protected],filePath)

            withContext(Dispatchers.Main) {
                //Update UI in main process
                if (isSave){
                    Toast. makeText( [email protected] , "succeeded", toast LENGTH_ SHORT). show()
                }else{
                    Toast. makeText( [email protected] , "failed", toast LENGTH_ SHORT). show()
                }
            }

        }
    }

    /**
     *Judgment authority
     */
    fun onRequestPermissionsResult(
        activity: Activity?,
        requestCode: Int,
        permissions: Array<String?>,
        grantResults: IntArray
    ): BooleanArray? {
        var result = true
        var isNerverAsk = false
        val length = grantResults.size
        for (i in 0 until length) {
            val permission = permissions[i]
            val grandResult = grantResults[i]
            if (grandResult == PackageManager.PERMISSION_DENIED) {
                result = false
                if (!ActivityCompat.shouldShowRequestPermissionRationale(
                        activity!!,
                        permission!!
                    )
                ) isNerverAsk = true
            }
        }
        return booleanArrayOf(result, isNerverAsk)
    }

    /**
     *Authorization result callback
     */
    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String?>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode == REQUEST_CODE_LAUNCH) {
            val hasPermissions = onRequestPermissionsResult(this, requestCode, permissions, grantResults)
            if (hasPermissions!![0]) {
                showView()
            } else {
                Toast. makeText( [email protected] , "no permission", toast LENGTH_ SHORT). show()

            }
        }
    }

    /**
     *Verification authority
     */
    fun checkPermissions(
        activity: Activity?,
        permissions: Array<String>,
        requestCode: Int
    ): Boolean {// android6.0 has permission by default
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return true
        val needList: MutableList<String> = ArrayList()
        var needShowRationale = false
        val length = permissions.size
        for (i in 0 until length) {
            val permisson = permissions[i]
            if (TextUtils.isEmpty(permisson)) continue
            if (ActivityCompat.checkSelfPermission(activity!!, permisson)
                != PackageManager.PERMISSION_GRANTED
            ) {
                needList.add(permisson)
                if (ActivityCompat.shouldShowRequestPermissionRationale(
                        activity,
                        permisson
                    )
                ) needShowRationale = true
            }
        }
        return if (needList.size != 0) {
            if (needShowRationale) {
                //
                return false
            }
            ActivityCompat.requestPermissions(activity!!, needList.toTypedArray(), requestCode)
            false
        } else {
            true
        }
    }


}

be careful:

  • Meizu mobile phone can not refresh the local photo album downloaded from individual versions
  • Refresh of individual mobile photo albums will repeat

summary

With the update of the Android system version and the magic changes of major domestic manufacturers, there are problems in updating the picture download album. Here, the compatibility made in the project is recorded, and then the problems that gradually appear in the project are updated

It’s not easy to write. You’re welcome to like it

reference

1. Android Q behavior change

2. Demo source code address