Android AppGlideModule,DataFetcher,ModelLoader视频MediaMetadataRetriever失败后定制视频,Kotlin(2)

Android AppGlideModule,DataFetcher,ModelLoaderFactory,ModelLoader视频MediaMetadataRetriever解析Video封面失败后定制视频封面作为永久正式缓存,Kotlin(2)

plugins {
    id 'org.jetbrains.kotlin.kapt'
}


    implementation 'com.github.bumptech.glide:glide:4.16.0'
    kapt 'com.github.bumptech.glide:compiler:4.16.0'

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
    <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />

import android.content.Context
import android.os.Bundle
import android.provider.MediaStore
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.launch


class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val recyclerView: RecyclerView = findViewById(R.id.recyclerview)
        val layoutManager = GridLayoutManager(this, 5)
        recyclerView.layoutManager = layoutManager

        lifecycleScope.launch {
            val list = readAllVideo(this@MainActivity)

            var photoAdapter = VideoAdapter(this@MainActivity, list)
            recyclerView.adapter = photoAdapter
        }
    }

    private fun readAllVideo(context: Context): ArrayList<MyData> {
        val videos = ArrayList<MyData>()

        val cursor = context.contentResolver.query(
            MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
            null,
            null,
            null,
            null
        )

        var index = 0

        while (cursor!!.moveToNext()) {
            //路径 uri
            val path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA))

            //名称
            val name =
                cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DISPLAY_NAME))

            //大小
            val size = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.SIZE))

            videos.add(MyData(path, index++))
        }
        cursor.close()

        return videos
    }
}

class MyData(var path: String, val index: Int)

import android.content.Context
import android.util.Log
import com.bumptech.glide.Glide
import com.bumptech.glide.GlideBuilder
import com.bumptech.glide.Registry
import com.bumptech.glide.annotation.GlideModule
import com.bumptech.glide.module.AppGlideModule
import java.io.InputStream


@GlideModule
class MyGlideModule : AppGlideModule() {

    override fun applyOptions(context: Context, builder: GlideBuilder) {
        super.applyOptions(context, builder)
        builder.setLogLevel(Log.DEBUG)
    }

    override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
        super.registerComponents(context, glide, registry)

        registry.append(
            VideoCover::class.java,
            InputStream::class.java,
            VideoCoverLoaderFactory()
        )
    }
}

import android.content.Context
import android.graphics.drawable.Drawable
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.target.Target


class VideoAdapter(private val context: Context, private val items: ArrayList<MyData>) :
    RecyclerView.Adapter<MyViewHolder>() {
    val TAG = "Glide/VideoAdapter"

    private fun createView(parent: ViewGroup): View {
        return LayoutInflater.from(parent.context).inflate(R.layout.item, parent, false)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        return MyViewHolder(createView(parent))
    }

    override fun onBindViewHolder(holder: MyViewHolder, pos: Int) {
        GlideApp.with(context)
            .load(VideoCover(items[pos].path))
            .override(150)
            .addListener(object : RequestListener<Drawable> {
                override fun onLoadFailed(
                    e: GlideException?,
                    model: Any?,
                    target: Target<Drawable>,
                    isFirstResource: Boolean
                ): Boolean {
                    //永远不会进入,因为VideoCoverFetcher的loadData里面总是onResourceReady
                    Log.d(TAG, "onLoadFailed")
                    return false
                }

                override fun onResourceReady(
                    resource: Drawable,
                    model: Any,
                    target: Target<Drawable>?,
                    dataSource: DataSource,
                    isFirstResource: Boolean
                ): Boolean {
                    Log.d(TAG, "onResourceReady")
                    return false
                }
            })
            .centerInside()
            .into(holder.image)

        holder.pos.text = "$pos"
        holder.text.text = "-$pos-"
    }

    override fun getItemCount(): Int {
        return items.size
    }
}

class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    val image: ImageView = itemView.findViewById(R.id.image)
    val pos: TextView = itemView.findViewById(R.id.pos)
    val text: TextView = itemView.findViewById(R.id.text)
}

class VideoCover {
    var path: String? = null

    constructor(path: String) {
        this.path = path
    }
}

import android.content.res.Resources
import android.graphics.Bitmap
import android.graphics.Bitmap.CompressFormat
import android.graphics.BitmapFactory
import android.media.MediaMetadataRetriever
import android.util.Log
import com.bumptech.glide.Priority
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.data.DataFetcher
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.InputStream

class VideoCoverFetcher : DataFetcher<InputStream> {
    val TAG = "Glide/VideoCoverFetcher"

    private var model: VideoCover? = null
    private val resId = android.R.drawable.stat_notify_error

    constructor(model: VideoCover) {
        this.model = model
    }

    override fun loadData(priority: Priority, callback: DataFetcher.DataCallback<in InputStream>) {
        Log.d(TAG, "loadData ${model?.path}")

        var bmp: Bitmap? = getVideoThumbnail(model?.path)
        if (bmp == null) {
            Log.d(TAG, "loadData 使用默认的图")
            //这张图将成为uri对应的正式的缓存图。
            bmp = BitmapFactory.decodeResource(Resources.getSystem(), resId)
        }

        //无论如何都返回正确。
        callback.onDataReady(ByteArrayInputStream(bitmapToByteArray(bmp!!)))
    }

    override fun cleanup() {
        Log.d(TAG, "cleanup")
    }

    override fun cancel() {
        Log.d(TAG, "cancel")
    }

    override fun getDataClass(): Class<InputStream> {
        return InputStream::class.java
    }

    override fun getDataSource(): DataSource {
        return DataSource.LOCAL
    }


    private fun bitmapToByteArray(bitmap: Bitmap): ByteArray {
        val bos = ByteArrayOutputStream()
        bitmap.compress(CompressFormat.PNG, 0, bos)
        return bos.toByteArray()
    }

    private fun getVideoThumbnail(uriString: String?): Bitmap? {
        val retriever = MediaMetadataRetriever()

        try {
            retriever.setDataSource(uriString)
            return retriever.frameAtTime
        } catch (e: Exception) {
            Log.d(TAG, e.toString())
        } finally {
            retriever.release()
            retriever.close()
        }

        return null
    }
}

import android.util.Log
import com.bumptech.glide.load.model.ModelLoader
import com.bumptech.glide.load.model.ModelLoaderFactory
import com.bumptech.glide.load.model.MultiModelLoaderFactory
import java.io.InputStream

class VideoCoverLoaderFactory : ModelLoaderFactory<VideoCover, InputStream> {
    val TAG = "Glide/VideoCoverLoaderFactory"

    override fun build(multiFactory: MultiModelLoaderFactory): ModelLoader<VideoCover, InputStream> {
        return VideoCoverModuleLoader()
    }

    override fun teardown() {
        Log.d(TAG, "teardown")
    }
}

import android.util.Log
import com.bumptech.glide.load.Options
import com.bumptech.glide.load.model.ModelLoader
import com.bumptech.glide.load.model.ModelLoader.LoadData
import com.bumptech.glide.signature.ObjectKey
import java.io.InputStream

class VideoCoverModuleLoader : ModelLoader<VideoCover, InputStream> {
    val TAG = "Glide/VideoCoverModuleLoader"

    override fun buildLoadData(
        model: VideoCover,
        width: Int,
        height: Int,
        options: Options
    ): ModelLoader.LoadData<InputStream>? {
        Log.d(TAG, "buildLoadData")
        return LoadData(
            VideoCoverSignature(model.path!!), //简单时候可以考虑ObjectKey(model.path!!)
            VideoCoverFetcher(model)
        )
    }

    override fun handles(model: VideoCover): Boolean {
        return true
    }
}

import com.bumptech.glide.load.Key
import java.security.MessageDigest

class VideoCoverSignature() : Key {
    private var path: String? = null

    constructor(path: String) : this() {
        this.path = path
    }

    override fun updateDiskCacheKey(messageDigest: MessageDigest) {
        val ba: ByteArray = path?.toByteArray()!!
        messageDigest.update(ba, 0, ba.size)
    }
}

<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/recyclerview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" />

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:padding="1dp">

    <ImageView
        android:id="@+id/image"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:background="@drawable/ic_launcher_background" />

    <TextView
        android:id="@+id/pos"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:textSize="10dp" />

    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:textSize="10dp" />

    <View
        android:layout_width="20dp"
        android:layout_height="1px"
        android:layout_gravity="center_horizontal"
        android:layout_marginBottom="10dp"
        android:background="@color/cardview_dark_background" />
</LinearLayout>

Android自定义AppGlideModule,DataFetcher ,ModelLoaderFactory,ModelLoader,Kotlin(1)-CSDN博客假设实现一个简单的功能,对传入要加载的path路径增加一定的筛选、容错或“重定向”,需要自定义一个模型,基于这个模型,让Glide自动匹配模型展开加载。), //简单时候可以考虑ObjectKey(model.path!!= null!})!https://blog.csdn.net/zhangphil/article/details/133862536

Android读取设备所有Video视频,Kotlin-CSDN博客【Android设置头像,手机拍照或从本地相册选取图片作为头像】像微信、QQ、微博等社交类的APP,通常都有设置头像的功能,设置头像通常有两种方式:1,让用户通过选择本地相册之类的图片库中已有的图像,裁剪后作为头像。Android设置头像,手机拍照或从本地相册选取图片作为头像_android 头像拍照_zhangphil的博客-CSDN博客。Android图片添加文字水印并保存水印文字图片到指定文件_zhangphil的博客-CSDN博客。Android读取设备所有视频,Kotlin。https://blog.csdn.net/zhangphil/article/details/132173745

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhangphil

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值