221 lines
6.9 KiB
Kotlin
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()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|