iT邦幫忙

2021 iThome 鐵人賽

DAY 16
0
Mobile Development

重新瞭解Android硬體控制系列 第 11

110/16 - 整合Android 6到Android 11

都把權限寫完了,該來做個小整理,這次我們整合Android 6Android 11,沒有Android 5是因為太老舊,是該淘汰,沒有Android 12則是我沒有實機可以測試。

版本名稱 版號 上版日期 SDK版本 官方支援
Lollipop 5.0 – 5.1.1 2014年11月12日 21 - 22 不支援
Marshmallow 6.0 – 6.0.1 2015年10月05日 23 不支援
Nougat 7.0 – 7.1.2 2016年08月22日 24 - 25 不支援
Oreo 8.0 – 8.1 2017年08月21日 26 - 27 支援
Pie 9 2018年08月06日 28 支援
Q 10 2019年09月03日 29 支援
R 11 2020年09月08日 30 支援
S 12 2021年??月??日 31 支援

AndroidManifest.xml要記得新增權限

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
    tools:ignore="ScopedStorage" />

因為會重複呼叫,所以把拍照改成函式,主要是以Android 9為分水嶺,Android 9以下才需要權限,Android 10以上就使用MediaStore,所以不用權限;符合官方規範所說,不要隨便跟使用者要求權限

private fun startTakePicture() {
    
    var uri: Uri? = null
    
    if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
        val permissionList = arrayOf(
            Manifest.permission.WRITE_EXTERNAL_STORAGE,
            Manifest.permission.READ_EXTERNAL_STORAGE
        )
        
        if (!EasyPermissions.hasPermissions(this, *permissionList)) {
            EasyPermissions.requestPermissions(
                this,
                "請提供讀寫檔案權限",
                BaseConstants.READ_WRITE_PERMISSIONS,
                *permissionList
            )
            return
        }
        
        if (!isCreateFolder(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES))) {
            Log.d("maho", "資料夾建立失敗")
            return
        }
        
        val phoneFile = File(  
        Environment.getExternalStoragePublicDirectory("${Environment.DIRECTORY_PICTURES}/AndroidSystem"),
            "004.jpg"
        )
        
        uri = getPictureUri(phoneFile)
    }
    
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        val contentValue = ContentValues().apply {
            this.put(MediaStore.Images.ImageColumns.DISPLAY_NAME, "004.jpg")
            this.put(MediaStore.Images.ImageColumns.MIME_TYPE, "image/jpeg")
            this.put(
                MediaStore.Images.ImageColumns.RELATIVE_PATH,
                "${Environment.DIRECTORY_PICTURES}/AndroidSystem"
                )
            )
        }
        
        uri = contentResolver.insert(
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
            contentValue
        )
    }
    
    createVersionCheckResultLauncher.launch(uri)
}

是點擊的部份

aclMbCreateVersionCheckPicture.setOnClickListener {
    startTakePicture()
}

使用者同意權限的部份

override fun onPermissionsGranted(requestCode: Int, perms: MutableList<String>) {
    if (BaseConstants.READ_WRITE_PERMISSIONS == requestCode) {
        startTakePicture()
    }
}

啟動器的部份

private val createVersionCheckResultLauncher =
    registerForActivityResult(ActivityResultContracts.TakePicture()) { isTakePicture ->
        
        if (!isTakePicture) {
            Log.d("maho", "拍照建立檔案失敗")
            return@registerForActivityResult
        }
        
        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
            val phoneFile = File(                Environment.getExternalStoragePublicDirectory("${Environment.DIRECTORY_PICTURES}/AndroidSystem"),
                "004.jpg"
            )
            
            MediaScannerConnection.scanFile(this, arrayOf(phoneFile.toString()), null) { _, _ ->
            }
            
            aclIvVersionCheckPicture.setImageURI(getPictureUri(phoneFile))
        }
        
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            val selection = "${MediaStore.Images.ImageColumns.DISPLAY_NAME} = '004.jpg'"
            val orderBy = "${MediaStore.Images.ImageColumns.DATE_ADDED} DESC"
            
            val uriQuery = contentResolver.query(
                MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                null,
                selection,
                null,
                orderBy
            ) ?: return@registerForActivityResult
            
            uriQuery.moveToFirst()
            
            val pictureId =
                uriQuery.getLong(uriQuery.getColumnIndex(MediaStore.Images.ImageColumns._ID))
                
            val uri = ContentUris.withAppendedId(
                MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                pictureId
            )
            
            MediaScannerConnection.scanFile(this, arrayOf(uri.toString()), null) { _, _ ->
            }
            
            aclIvVersionCheckPicture.setImageURI(uri)
        }
    }

這樣的話,就能順利的在Andoird 6Android 11執行,拍照後會在Pictures/應用程式名稱資料夾中建立檔案,雖然應用程式移除後照片還是會留在手機中,但也符合官方建議。


上一篇
110/14 - EasyPermissions與他的快樂伙伴
下一篇
110/17 - Android 6圖片剪裁
系列文
重新瞭解Android硬體控制14
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言