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

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

View File

@ -259,3 +259,19 @@ object Styler {
Math.min(lp.width, lp.height).toFloat() * round_ratio 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 package jp.juggler.subwaytooter.span
import android.content.Context import android.content.Context
import android.graphics.Canvas import android.graphics.*
import android.graphics.Paint import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.support.annotation.IntRange import android.support.annotation.IntRange
import android.support.v4.content.ContextCompat import android.support.v4.content.ContextCompat
@ -10,7 +10,11 @@ import android.text.style.ReplacementSpan
import java.lang.ref.WeakReference 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 { companion object {
@ -62,6 +66,9 @@ class EmojiImageSpan(context : Context, private val res_id : Int) : ReplacementS
return size return size
} }
private var lastColor :Int? = null
private var lastColorFilter: PorterDuffColorFilter? = null
override fun draw( override fun draw(
canvas : Canvas, canvas : Canvas,
text : CharSequence, text : CharSequence,
@ -82,7 +89,19 @@ class EmojiImageSpan(context : Context, private val res_id : Int) : ReplacementS
canvas.save() canvas.save()
canvas.translate(x, transY.toFloat()) canvas.translate(x, transY.toFloat())
d.setBounds(0, 0, size, size) 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() canvas.restore()
} }

View File

@ -693,6 +693,10 @@ fun runOnMainLooper(proc : () -> Unit) {
Handler(looper).post { proc() } Handler(looper).post { proc() }
} }
} }
fun runOnMainLooperDelayed(delayMs:Long, proc : () -> Unit) {
val looper = Looper.getMainLooper()
Handler(looper).postDelayed({ proc() },delayMs)
}
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// View // 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_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/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_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/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);