視頻文件插入系統(tǒng)相冊(適配Androird10.0(Q) API Level 29)新

1.前言

本文介紹兼容AndroidQ的“視頻文插入相冊”方案。
以前的發(fā)送廣播方式已失效。
因此需要使用新的插入相冊實現(xiàn)方式。并針對AndroidQ進行版本適配。

2. “視頻文件插入相冊”具體實現(xiàn):

這里有一個需要注意的地方:put(MediaStore.Images.Media.IS_PENDING, 1)。這個設置是做耗時操作時,需要獨占資源。但是使用結束后,務必注意解除獨占。

    fun insertVideo(context: Context, filePath: String) {
        if (!checkFile(filePath)) {
            return
        }
        val resolver = context.applicationContext.contentResolver
        val videoCollection = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
        } else {
            MediaStore.Video.Media.EXTERNAL_CONTENT_URI
        }

        val contentValues = ContentValues().apply {
            val saveFile = File(filePath)
            put(
                MediaStore.Video.Media.DISPLAY_NAME,
                saveFile.nameWithoutExtension + System.currentTimeMillis()
            )
            put(MediaStore.Video.Media.DURATION, getDurationOfVideo(filePath))
            put(MediaStore.MediaColumns.MIME_TYPE, getVideoMimeType(filePath))
            val videoTime = System.currentTimeMillis()
            put(MediaStore.MediaColumns.DATE_ADDED, videoTime / 1000)
            put(MediaStore.MediaColumns.DATE_MODIFIED, videoTime / 1000)
            // 判斷是否android10 以上
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                // 設置相對路徑(自動創(chuàng)建文件夾)
                val folder = context.resources.getString(R.string.app_name)
                val relativeName =
                    Environment.DIRECTORY_MOVIES + File.separator + folder + File.separator
                put(MediaStore.MediaColumns.RELATIVE_PATH, relativeName)
                // 設置獨占鎖:耗時操作,獨占訪問權限,完成操作需復位
                put(MediaStore.Video.Media.IS_PENDING, 1)
            }
        }
        resolver.insert(videoCollection, contentValues)?.let { insertUri ->
            val isSuccess = copyFile(context, resolver, filePath, insertUri)
            //判斷是否android10 以上
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                // 復位(解除)獨占權限
                contentValues.clear()
                contentValues.put(MediaStore.Video.Media.IS_PENDING, 0)
                resolver.update(insertUri, contentValues, null, null)
            }
        }
    }

3. 以下是其他工具方法:

3.1 獲取視頻類型(mine_type)
   /**
     * 獲取video的mine_type,暫時只支持mp4,3gp
     *
     * @param path
     * @return
     */
    private fun getVideoMimeType(path: String): String {
        val lowerPath = path.toLowerCase()
        if (lowerPath.endsWith("mp4") || lowerPath.endsWith("mpeg4")) {
            return "video/mp4"
        } else if (lowerPath.endsWith("3gp")) {
            return "video/3gp"
        }
        return "video/mp4"
    }
3.2 獲取視頻時長(duration)
    /**
     * 獲取視頻時長
     */
    private fun getDurationOfVideo(filePath: String): kotlin.Long {
        if (!checkFile(filePath)) {
            return 0
        }
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD_MR1) {
            try {
                val mmr = MediaMetadataRetriever()
                mmr.setDataSource(filePath)
                val durationStr = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)?: "0L"
                durationStr.toLong()
            } catch (e: java.lang.Exception) {
                0
            }
        } else 0
    }
3.3 檢測文件是否存在
   /**
     * 檢測文件存在
     *
     * @param filePath
     * @return
     */
    private fun checkFile(filePath: String): Boolean {
        //boolean result = FileUtil.fileIsExist(filePath);
        var result = false
        val mFile = File(filePath)
        if (mFile.exists()) {
            result = true
        }
        val logStr = if (result) "文件已存在" else "文件不存在"
        Log.e(TAG, "$logStr, path = $filePath")
        return result
    }
3.4 視頻文件使用流寫入相冊
/**
     * 通過兩個路徑地址生成對應的輸入輸出流
     * 主要方式獲取ContentResolver.openOutputStream(insertUri)
     */
    private fun copyFile(
        context: Context,
        contentResolver: ContentResolver,
        sourceFilePath: String,
        insertUri: Uri
    ): Boolean {
        var inputStream: InputStream? = null //輸入流
        var outputStream: OutputStream? = null //輸出流
        return try {
            var isCopySuccess = false
            outputStream = contentResolver.openOutputStream(insertUri)?.also { outStream ->
                val sourceFile = File(sourceFilePath)
                if (sourceFile.exists()) { // 文件存在時
                    // 讀入原文件
                    inputStream = FileInputStream(sourceFile).also { inStream ->
                        //輸入流讀取文件,輸出流寫入指定目錄
                        isCopySuccess = copyFileWithStream(outStream, inStream)
                    }
                }
            }
            isCopySuccess
        } catch (e: Exception) {
            e.printStackTrace()
            false
        } finally {
            try {
                inputStream?.close()
                outputStream?.close()
            } catch (e: IOException) {
                e.printStackTrace()
            }
        }
    }

    /**
     * 利用輸入輸出流邊讀邊寫,將文件(視頻)寫入指定路徑
     */
    private fun copyFileWithStream(outputStream: OutputStream, inputStream: InputStream): Boolean {
        var isSuccess = false
        try {
            inputStream.use { ins ->
                val buf = ByteArray(2048)
                var len: Int
                while (ins.read(buf).also { len = it } != -1) {
                    outputStream.write(buf, 0, len)
                }
                outputStream.flush()
            }
            isSuccess = true
        } catch (e: IOException) {
            Log.e(TAG, Log.getStackTraceString(e))
            isSuccess = false
        } finally {
            try {
                outputStream.close()
                inputStream.close()
            } catch (e: IOException) {
                e.printStackTrace()
            }
        }
        return isSuccess
    }

相關文檔:

參考文檔:

官方文檔地址:[https://developer.android.google.cn/training/data-storage/shared/media#add-item]

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容