カラムの処理状況表示の改善

This commit is contained in:
tateisu 2018-04-21 08:16:44 +09:00
parent 26888a79f4
commit f080f36a1a
11 changed files with 149 additions and 108 deletions

View File

@ -22,19 +22,26 @@ import jp.juggler.subwaytooter.api.entity.*
import jp.juggler.subwaytooter.table.*
import jp.juggler.subwaytooter.util.*
enum class TaskIndicatorState {
NO_TASK,
SCHEDULED,
BG_START,
BG_END,
}
enum class StreamingIndicatorState {
NONE,
REGISTERED, // registered, but not listening
LISTENING,
}
enum class ColumnTaskType{
LOADING,
REFRESH_TOP,
REFRESH_BOTTOM,
GAP
}
abstract class ColumnTask(
val ctType: ColumnTaskType
) : AsyncTask<Void, Void, TootApiResult?>() {
val ctStarted = AtomicBoolean(false)
val ctClosed = AtomicBoolean(false)
}
class Column(
val app_state : AppState,
val context : Context,
@ -352,7 +359,8 @@ class Column(
//////////////////////////////////////////////////////////////////////////////////////
private var last_task : AsyncTask<Void, Void, TootApiResult?>? = null
internal var lastTask : ColumnTask? = null
internal var bInitialLoading : Boolean = false
internal var bRefreshLoading : Boolean = false
@ -1086,17 +1094,13 @@ class Column(
}
private fun cancelLastTask() {
if(last_task != null) {
last_task?.cancel(true)
last_task = null
if(lastTask != null) {
lastTask?.cancel(true)
lastTask = null
//
bInitialLoading = false
bRefreshLoading = false
mInitialLoadingError = context.getString(R.string.cancelled)
//
indicatorLoading = TaskIndicatorState.NO_TASK
indicatorRefresh = TaskIndicatorState.NO_TASK
indicatorGap = TaskIndicatorState.NO_TASK
}
}
@ -1396,10 +1400,6 @@ class Column(
env.update(client)
}
var indicatorLoading = TaskIndicatorState.NO_TASK
var indicatorRefresh = TaskIndicatorState.NO_TASK
var indicatorGap = TaskIndicatorState.NO_TASK
internal fun startLoading() {
cancelLastTask()
@ -1414,14 +1414,13 @@ class Column(
bRefreshLoading = false
max_id = ""
since_id = ""
indicatorLoading = TaskIndicatorState.SCHEDULED
duplicate_map.clear()
list_data.clear()
fireShowContent(reason = "loading start", reset = true)
val task = @SuppressLint("StaticFieldLeak")
object : AsyncTask<Void, Void, TootApiResult?>() {
object : ColumnTask(ColumnTaskType.LOADING){
internal var parser = TootParser(context, access_info, highlightTrie = highlight_trie)
internal var instance_tmp : TootInstance? = null
@ -1637,7 +1636,7 @@ class Column(
}
override fun doInBackground(vararg params : Void) : TootApiResult? {
indicatorLoading = TaskIndicatorState.BG_START
ctStarted.set(true)
val client = TootApiClient(context, callback = object : TootApiCallback {
override val isApiCancelled : Boolean
@ -1909,8 +1908,10 @@ class Column(
} catch(ex : Throwable) {
log.trace(ex)
}
indicatorLoading = TaskIndicatorState.BG_END
client.publishApiProgress("")
ctClosed.set(true)
runOnMainLooperDelayed(333L){
if( !isCancelled ) fireShowColumnStatus()
}
}
}
@ -1925,39 +1926,34 @@ class Column(
return
}
try {
bInitialLoading = false
last_task = null
if(result.error != null) {
this@Column.mInitialLoadingError = result.error ?: ""
} else {
duplicate_map.clear()
list_data.clear()
val list_tmp = this.list_tmp
if(list_tmp != null) {
val list_pinned = this.list_pinned
if(list_pinned?.isNotEmpty() == true) {
val list_new = duplicate_map.filterDuplicate(list_pinned)
list_data.addAll(list_new)
}
val list_new = duplicate_map.filterDuplicate(list_tmp)
bInitialLoading = false
lastTask = null
if(result.error != null) {
this@Column.mInitialLoadingError = result.error ?: ""
} else {
duplicate_map.clear()
list_data.clear()
val list_tmp = this.list_tmp
if(list_tmp != null) {
val list_pinned = this.list_pinned
if(list_pinned?.isNotEmpty() == true) {
val list_new = duplicate_map.filterDuplicate(list_pinned)
list_data.addAll(list_new)
}
resumeStreaming(false)
val list_new = duplicate_map.filterDuplicate(list_tmp)
list_data.addAll(list_new)
}
fireShowContent(reason = "loading updated", reset = true)
// 初期ロードの直後は先頭に移動する
viewHolder?.scrollToTop()
} finally {
indicatorLoading = TaskIndicatorState.NO_TASK
fireShowColumnStatus()
resumeStreaming(false)
}
fireShowContent(reason = "loading updated", reset = true)
// 初期ロードの直後は先頭に移動する
viewHolder?.scrollToTop()
}
}
this.last_task = task
this.lastTask = task
task.executeOnExecutor(App1.task_executor)
}
// int scroll_hack;
@ -2044,7 +2040,7 @@ class Column(
refresh_after_toot : Int
) {
if(last_task != null) {
if(lastTask != null) {
if(! bSilent) {
showToast(context, true, R.string.column_is_busy)
val holder = viewHolder
@ -2079,11 +2075,12 @@ class Column(
bRefreshLoading = true
mRefreshLoadingError = ""
indicatorRefresh = TaskIndicatorState.SCHEDULED
fireShowColumnStatus()
val task = @SuppressLint("StaticFieldLeak")
object : AsyncTask<Void, Void, TootApiResult?>() {
object : ColumnTask( when{
bBottom -> ColumnTaskType.REFRESH_BOTTOM
else->ColumnTaskType.REFRESH_TOP
}){
internal var parser = TootParser(context, access_info, highlightTrie = highlight_trie)
internal var list_tmp : ArrayList<TimelineItem>? = null
@ -2580,7 +2577,7 @@ class Column(
}
override fun doInBackground(vararg params : Void) : TootApiResult? {
indicatorRefresh = TaskIndicatorState.BG_START
ctStarted.set(true)
val client = TootApiClient(context, callback = object : TootApiCallback {
override val isApiCancelled : Boolean
@ -2739,8 +2736,10 @@ class Column(
} catch(ex : Throwable) {
log.trace(ex)
}
indicatorRefresh = TaskIndicatorState.BG_END
client.publishApiProgress("")
ctClosed.set(true)
runOnMainLooperDelayed(333L){
if( !isCancelled ) fireShowColumnStatus()
}
}
}
@ -2755,7 +2754,7 @@ class Column(
return
}
try {
last_task = null
lastTask = null
bRefreshLoading = false
val error = result.error
@ -2854,7 +2853,6 @@ class Column(
}
}
} finally {
indicatorRefresh = TaskIndicatorState.NO_TASK
fireShowColumnStatus()
if(! bBottom) {
@ -2864,8 +2862,9 @@ class Column(
}
}
}
this.last_task = task
this.lastTask = task
task.executeOnExecutor(App1.task_executor)
fireShowColumnStatus()
}
internal fun startGap(gap : TootGap?) {
@ -2873,7 +2872,7 @@ class Column(
showToast(context, true, "gap is null")
return
}
if(last_task != null) {
if(lastTask != null) {
showToast(context, true, R.string.column_is_busy)
return
}
@ -2882,11 +2881,9 @@ class Column(
bRefreshLoading = true
mRefreshLoadingError = ""
indicatorGap = TaskIndicatorState.SCHEDULED
fireShowColumnStatus()
val task = @SuppressLint("StaticFieldLeak")
object : AsyncTask<Void, Void, TootApiResult?>() {
object : ColumnTask(ColumnTaskType.GAP){
internal var max_id = gap.max_id
internal val since_id = gap.since_id
internal var list_tmp : ArrayList<TimelineItem>? = null
@ -3109,7 +3106,7 @@ class Column(
}
override fun doInBackground(vararg params : Void) : TootApiResult? {
indicatorGap = TaskIndicatorState.BG_START
ctStarted.set(true)
val client = TootApiClient(context, callback = object : TootApiCallback {
override val isApiCancelled : Boolean
@ -3192,8 +3189,11 @@ class Column(
} catch(ex : Throwable) {
log.trace(ex)
}
indicatorGap = TaskIndicatorState.BG_END
client.publishApiProgress("")
ctClosed.set(true)
runOnMainLooperDelayed(333L){
if( !isCancelled ) fireShowColumnStatus()
}
}
}
@ -3210,7 +3210,7 @@ class Column(
try {
last_task = null
lastTask = null
bRefreshLoading = false
val error = result.error
@ -3285,14 +3285,13 @@ class Column(
}
}
} finally {
indicatorGap = TaskIndicatorState.NO_TASK
fireShowColumnStatus()
}
}
}
this.last_task = task
this.lastTask = task
task.executeOnExecutor(App1.task_executor)
fireShowColumnStatus()
}
enum class HeaderType(val viewType : Int) {

View File

@ -28,6 +28,9 @@ import jp.juggler.subwaytooter.table.AcctColor
import android.support.v7.widget.ListRecyclerView
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.text.SpannableStringBuilder
import android.text.Spanned
import jp.juggler.subwaytooter.span.EmojiImageSpan
import jp.juggler.subwaytooter.util.*
import jp.juggler.subwaytooter.view.ListDivider
import java.io.Closeable
@ -515,46 +518,40 @@ class ColumnViewHolder(
fun showColumnStatus() {
val column = this.column
val sb = StringBuilder()
val sb = SpannableStringBuilder()
try {
if(column == null) {
sb.append('?')
} else {
when(column.indicatorLoading) {
TaskIndicatorState.NO_TASK -> {
}
TaskIndicatorState.SCHEDULED -> sb.append("L?")
TaskIndicatorState.BG_START -> sb.append("L")
TaskIndicatorState.BG_END -> sb.append("L!")
}
val tb = if(column.bRefreshingTop) {
'T'
} else {
'B'
}
when(column.indicatorRefresh) {
TaskIndicatorState.NO_TASK -> {
}
TaskIndicatorState.SCHEDULED -> sb.append("${tb}?")
TaskIndicatorState.BG_START -> sb.append(tb)
TaskIndicatorState.BG_END -> sb.append("${tb}!")
}
when(column.indicatorGap) {
TaskIndicatorState.NO_TASK -> {
}
TaskIndicatorState.SCHEDULED -> sb.append("G?")
TaskIndicatorState.BG_START -> sb.append("G")
TaskIndicatorState.BG_END -> sb.append("G!")
val task = column.lastTask
if( task != null){
sb.append(when(task.ctType){
ColumnTaskType.LOADING -> 'L'
ColumnTaskType.REFRESH_TOP ->'T'
ColumnTaskType.REFRESH_BOTTOM ->'B'
ColumnTaskType.GAP->'G'
})
sb.append(when{
task.isCancelled -> "~"
task.ctClosed.get() -> "!"
task.ctStarted.get() -> ""
else-> "?"
})
}
when(column.getStreamingStatus()) {
StreamingIndicatorState.NONE -> {
}
StreamingIndicatorState.REGISTERED -> sb.append("S?")
StreamingIndicatorState.LISTENING -> sb.append("S")
StreamingIndicatorState.REGISTERED ->{
sb.appendColorShadeIcon(activity,R.drawable.ic_pulse,"Streaming")
sb.append("?")
}
StreamingIndicatorState.LISTENING -> {
sb.appendColorShadeIcon(activity,R.drawable.ic_pulse,"Streaming")
}
}
}
@ -564,6 +561,10 @@ class ColumnViewHolder(
}
}
fun showColumnColor() {
val column = this.column

View File

@ -259,3 +259,19 @@ object Styler {
Math.min(lp.width, lp.height).toFloat() * round_ratio
}
fun SpannableStringBuilder.appendColorShadeIcon(
context:Context,
drawable_id:Int,
text:String
){
val start = this.length
this.append(text)
val end = this.length
this.setSpan(
EmojiImageSpan(context, drawable_id,useColorShader=true),
start,
end,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)
}

View File

@ -1,8 +1,8 @@
package jp.juggler.subwaytooter.span
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.*
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.support.annotation.IntRange
import android.support.v4.content.ContextCompat
@ -10,7 +10,11 @@ import android.text.style.ReplacementSpan
import java.lang.ref.WeakReference
class EmojiImageSpan(context : Context, private val res_id : Int) : ReplacementSpan() {
class EmojiImageSpan(
context : Context,
private val res_id : Int,
private val useColorShader:Boolean = false
) : ReplacementSpan() {
companion object {
@ -62,6 +66,9 @@ class EmojiImageSpan(context : Context, private val res_id : Int) : ReplacementS
return size
}
private var lastColor :Int? = null
private var lastColorFilter: PorterDuffColorFilter? = null
override fun draw(
canvas : Canvas,
text : CharSequence,
@ -82,7 +89,19 @@ class EmojiImageSpan(context : Context, private val res_id : Int) : ReplacementS
canvas.save()
canvas.translate(x, transY.toFloat())
d.setBounds(0, 0, size, size)
d.draw(canvas)
if( useColorShader && d is BitmapDrawable){
if( paint.color != lastColor || lastColorFilter == null) {
lastColor = paint.color
lastColorFilter = PorterDuffColorFilter(paint.color, PorterDuff.Mode.SRC_ATOP)
}
val saveColorFilter = d.colorFilter
d.colorFilter = lastColorFilter
d.draw(canvas)
d.colorFilter = saveColorFilter
}else {
d.draw(canvas)
}
canvas.restore()
}

View File

@ -693,6 +693,10 @@ fun runOnMainLooper(proc : () -> Unit) {
Handler(looper).post { proc() }
}
}
fun runOnMainLooperDelayed(delayMs:Long, proc : () -> Unit) {
val looper = Looper.getMainLooper()
Handler(looper).postDelayed({ proc() },delayMs)
}
////////////////////////////////////////////////////////////////////
// View

Binary file not shown.

After

Width:  |  Height:  |  Size: 896 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 651 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -111,7 +111,9 @@ my $res_dir = "app/src/main/res";
#resize_scales( "_ArtWork/ic_list_tl_dark.png" ,$res_dir,"drawable","ic_list_tl_dark",0,32);
#resize_scales( "_ArtWork/ic_list_member_dark.png" ,$res_dir,"drawable","ic_list_member_dark",0,32);
resize_scales( "_ArtWork/media_bg_dark.png" ,$res_dir,"drawable","media_bg_dark",0,24);
#resize_scales( "_ArtWork/media_bg_dark.png" ,$res_dir,"drawable","media_bg_dark",0,24);
#resize_scales( "_ArtWork/v0.5.1/ic_launcher_foreground.png" ,$res_dir,"mipmap","ic_launcher_foreground",0,108);
#resize_scales( "_ArtWork/v0.5.1/ic_launcher_background.png" ,$res_dir,"mipmap","ic_launcher_background",0,108);
resize_scales( "_ArtWork/ic_pulse.png" ,$res_dir,"drawable","ic_pulse",0,32);