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

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

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