SubwayTooter-Android-App/sample_apng/src/main/java/jp/juggler/apng/sample/ActList.kt

221 lines
6.9 KiB
Kotlin

package jp.juggler.apng.sample
import android.Manifest
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import android.os.SystemClock
import android.util.Log
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.BaseAdapter
import android.widget.ListView
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import jp.juggler.apng.ApngFrames
import kotlinx.coroutines.*
import kotlin.coroutines.CoroutineContext
class ActList : AppCompatActivity(), CoroutineScope {
companion object {
const val TAG = "ActList"
const val PERMISSION_REQUEST_CODE_STORAGE = 1
}
class ListItem(val id: Int, val caption: String)
private lateinit var listView: ListView
private lateinit var listAdapter: MyAdapter
private var timeAnimationStart: Long = 0L
private lateinit var activityJob: Job
override val coroutineContext: CoroutineContext
get() = Dispatchers.Main + activityJob
override fun onCreate(savedInstanceState: Bundle?) {
activityJob = Job()
super.onCreate(savedInstanceState)
setContentView(R.layout.act_list)
this.listView = findViewById(R.id.listView)
listAdapter = MyAdapter()
listView.adapter = listAdapter
listView.onItemClickListener = listAdapter
timeAnimationStart = SystemClock.elapsedRealtime()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// Assume thisActivity is the current activity
if (PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(
this,
Manifest.permission.WRITE_EXTERNAL_STORAGE
)
) {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
PERMISSION_REQUEST_CODE_STORAGE
)
}
}
load()
}
override fun onDestroy() {
super.onDestroy()
activityJob.cancel()
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray
) {
when (requestCode) {
PERMISSION_REQUEST_CODE_STORAGE -> {
// If request is cancelled, the result arrays are empty.
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission was granted, yay! Do the
// contacts-related task you need to do.
} else {
// permission denied, boo! Disable the
// functionality that depends on this permission.
}
return
}
// other 'case' lines to check for other
// permissions this app might request
}
}
private fun load() = launch {
val list = withContext(Dispatchers.IO) {
// RawリソースのIDと名前の一覧
R.raw::class.java.fields
.mapNotNull { it.get(null) as? Int }
.map { id ->
ListItem(
id,
resources.getResourceName(id)
.replaceFirst(""".+/""".toRegex(), "")
)
}
.toMutableList()
.apply { sortBy { it.caption } }
}
listAdapter.list.addAll(list)
listAdapter.notifyDataSetChanged()
}
inner class MyAdapter : BaseAdapter(), AdapterView.OnItemClickListener {
val list = ArrayList<ListItem>()
override fun getCount(): Int {
return list.size
}
override fun getItem(position: Int): Any {
return list[position]
}
override fun getItemId(position: Int): Long {
return list[position].id.toLong()
}
override fun getView(
position: Int,
viewArg: View?,
parent: ViewGroup?
): View {
val view: View
val holder: MyViewHolder
if (viewArg == null) {
view = layoutInflater.inflate(R.layout.lv_item, parent, false)
holder = MyViewHolder(view, this@ActList)
view.tag = holder
} else {
view = viewArg
holder = view.tag as MyViewHolder
}
holder.bind(list[position])
return view
}
override fun onItemClick(
parent: AdapterView<*>?,
view: View?,
position: Int,
id: Long
) {
val item = list[position]
ActViewer.open(this@ActList, item.id, item.caption)
}
}
inner class MyViewHolder(
viewRoot: View,
_activity: ActList
) {
private val tvCaption: TextView = viewRoot.findViewById(R.id.tvCaption)
private val apngView: ApngView = viewRoot.findViewById(R.id.apngView)
init {
apngView.timeAnimationStart = _activity.timeAnimationStart
}
private var lastId: Int = 0
private var lastJob: Job? = null
fun bind(listItem: ListItem) {
tvCaption.text = listItem.caption
val resId = listItem.id
if (lastId != resId) {
lastId = resId
apngView.apngFrames?.dispose()
apngView.apngFrames = null
launch {
var apngFrames: ApngFrames? = null
try {
lastJob?.cancelAndJoin()
val job = async(Dispatchers.IO) {
try {
ApngFrames.parse(128) { resources?.openRawResource(resId) }
} catch (ex: Throwable) {
ex.printStackTrace()
null
}
}
lastJob = job
apngFrames = job.await()
if (apngFrames != null && lastId == resId) {
apngView.apngFrames = apngFrames
apngFrames = null
}
} catch (ex: Throwable) {
ex.printStackTrace()
Log.e(TAG, "load error: ${ex.javaClass.simpleName} ${ex.message}")
} finally {
apngFrames?.dispose()
}
}
}
}
}
}