- RecyclerView.RecycledViewPool を使ってタブレットモードでの応答性を改善
- そのかわりFastScroller機能がなくなりました… - アプリ設定に「リフレッシュ時に常にギャップを挟む」を追加。主にデバッグ用途 - TLからプロフを開いた時にヘッダ画像の下の方にフェードされない余白ができるバグの修正
This commit is contained in:
parent
4acc51dd24
commit
3bc2a7384d
|
@ -10,6 +10,7 @@
|
|||
<w>apng</w>
|
||||
<w>bumptech</w>
|
||||
<w>coroutine</w>
|
||||
<w>doctype</w>
|
||||
<w>dont</w>
|
||||
<w>emoji</w>
|
||||
<w>emojione</w>
|
||||
|
|
|
@ -116,6 +116,9 @@ dependencies {
|
|||
// Coroutine listeners for Anko Layouts
|
||||
compile "org.jetbrains.anko:anko-sdk25-coroutines:$anko_version"
|
||||
compile "org.jetbrains.anko:anko-appcompat-v7-coroutines:$anko_version"
|
||||
|
||||
|
||||
// compile 'com.simplecityapps:recyclerview-fastscroll:1.0.16'
|
||||
}
|
||||
|
||||
repositories {
|
||||
|
|
|
@ -227,7 +227,7 @@ class TestTootApiClient {
|
|||
return true
|
||||
}
|
||||
|
||||
override fun close(code : Int, reason : String?) : Boolean { // TODO
|
||||
override fun close(code : Int, reason : String?) : Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
|
@ -1,96 +0,0 @@
|
|||
package jp.juggler.subwaytooter.mock
|
||||
|
||||
import android.content.SharedPreferences
|
||||
|
||||
class MockSharedPreferences(
|
||||
val map : HashMap<String, Any> = HashMap()
|
||||
) : SharedPreferences {
|
||||
|
||||
override fun contains(key : String?) = map.contains(key)
|
||||
|
||||
override fun getBoolean(key : String?, defValue : Boolean)
|
||||
= map.get(key) as? Boolean ?: defValue
|
||||
|
||||
override fun getInt(key : String?, defValue : Int)
|
||||
= map.get(key) as? Int ?: defValue
|
||||
|
||||
override fun getLong(key : String?, defValue : Long)
|
||||
= map.get(key) as? Long ?: defValue
|
||||
|
||||
override fun getFloat(key : String?, defValue : Float)
|
||||
= map.get(key) as? Float ?: defValue
|
||||
|
||||
override fun getString(key : String?, defValue : String?)
|
||||
= map.get(key) as? String ?: defValue
|
||||
|
||||
override fun getStringSet(key : String?, defValues : MutableSet<String>?)
|
||||
= map.get(key) as? MutableSet<String> ?: defValues
|
||||
|
||||
|
||||
override fun edit() : SharedPreferences.Editor {
|
||||
return Editor(this)
|
||||
}
|
||||
|
||||
override fun getAll() : MutableMap<String, *> {
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun registerOnSharedPreferenceChangeListener(
|
||||
listener : SharedPreferences.OnSharedPreferenceChangeListener?
|
||||
) {
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
override fun unregisterOnSharedPreferenceChangeListener(
|
||||
listener : SharedPreferences.OnSharedPreferenceChangeListener?
|
||||
) {
|
||||
TODO("not implemented")
|
||||
}
|
||||
|
||||
companion object {
|
||||
val REMOVED_OBJECT = Any()
|
||||
}
|
||||
|
||||
class Editor(private val pref : MockSharedPreferences) : SharedPreferences.Editor {
|
||||
private val changeSet = HashMap<String, Any>()
|
||||
|
||||
override fun commit() : Boolean {
|
||||
for((k, v) in changeSet) {
|
||||
if(v === REMOVED_OBJECT) {
|
||||
pref.map.remove(k)
|
||||
} else {
|
||||
pref.map.put(k, v)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun apply() {
|
||||
commit()
|
||||
}
|
||||
|
||||
override fun clear() : SharedPreferences.Editor {
|
||||
changeSet.clear()
|
||||
return this
|
||||
}
|
||||
|
||||
override fun remove(key : String) : SharedPreferences.Editor {
|
||||
changeSet.put(key, REMOVED_OBJECT)
|
||||
return this
|
||||
}
|
||||
|
||||
private fun putAny(k : String, v : Any?) : SharedPreferences.Editor {
|
||||
changeSet.put(k, v ?: REMOVED_OBJECT)
|
||||
return this
|
||||
}
|
||||
|
||||
override fun putLong(key : String, value : Long) = putAny(key, value)
|
||||
override fun putInt(key : String, value : Int) = putAny(key, value)
|
||||
override fun putBoolean(key : String, value : Boolean) = putAny(key, value)
|
||||
override fun putFloat(key : String, value : Float) = putAny(key, value)
|
||||
override fun putString(key : String, value : String?) = putAny(key, value)
|
||||
override fun putStringSet(key : String, value : MutableSet<String>?) = putAny(key, value)
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package android.support.v7.widget
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
|
||||
class ListRecyclerView : RecyclerView {
|
||||
|
||||
companion object {
|
||||
// private val log = LogCategory("ListRecyclerView")
|
||||
}
|
||||
|
||||
constructor(context : Context) : super(context)
|
||||
constructor(context : Context, attrs : AttributeSet) : super(context, attrs)
|
||||
constructor(context : Context, attrs : AttributeSet, defStyleAttr : Int) : super(context, attrs, defStyleAttr)
|
||||
|
||||
}
|
|
@ -1,322 +0,0 @@
|
|||
//package com.bumptech.glide.load.resource.gif
|
||||
//
|
||||
//import android.annotation.SuppressLint
|
||||
//import android.annotation.TargetApi
|
||||
//import android.graphics.*
|
||||
//import android.graphics.drawable.Animatable
|
||||
//import android.graphics.drawable.Drawable
|
||||
//import android.os.Build
|
||||
//import android.view.Gravity
|
||||
//
|
||||
//import com.bumptech.glide.gifdecoder.GifDecoder
|
||||
//import com.bumptech.glide.load.Transformation
|
||||
//import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool
|
||||
//
|
||||
//import java.lang.reflect.Field
|
||||
//
|
||||
//import jp.juggler.subwaytooter.util.LogCategory
|
||||
//
|
||||
//import com.bumptech.glide.gifdecoder.GifDecoder.TOTAL_ITERATION_COUNT_FOREVER
|
||||
//
|
||||
//@SuppressLint("ObsoleteSdkInt")
|
||||
//@Suppress("unused")
|
||||
//class MyGifDrawableOld : Drawable, GifFrameLoader.FrameCallback, Animatable {
|
||||
//
|
||||
//
|
||||
// private val paint : Paint
|
||||
// private val destRect = Rect()
|
||||
// private val state : GifDrawable.GifState
|
||||
// private val decoder : GifDecoder
|
||||
// private val frameLoader : GifFrameLoader
|
||||
//
|
||||
// /**
|
||||
// * True if the drawable is currently animating.
|
||||
// */
|
||||
// private var isRunning : Boolean = false
|
||||
// /**
|
||||
// * True if the drawable should animate while visible.
|
||||
// */
|
||||
// private var isStarted : Boolean = false
|
||||
// /**
|
||||
// * True if the drawable's resources have been recycled.
|
||||
// */
|
||||
// // For testing.
|
||||
// private var isRecycled : Boolean = false
|
||||
//
|
||||
// override fun onFrameReady() {
|
||||
// TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * True if the drawable is currently visible. Default to true because on certain platforms (at least 4.1.1),
|
||||
// * setVisible is not called on [Drawables][android.graphics.drawable.Drawable] during
|
||||
// * [android.widget.ImageView.setImageDrawable]. See issue #130.
|
||||
// */
|
||||
// private var isVisibleX = true
|
||||
// /**
|
||||
// * The number of times we've looped over all the frames in the gif.
|
||||
// */
|
||||
// private var loopCount : Int = 0
|
||||
// /**
|
||||
// * The number of times to loop through the gif animation.
|
||||
// */
|
||||
// private var maxLoopCount = Animatable.LOOP_FOREVER
|
||||
//
|
||||
// private var applyGravity : Boolean = false
|
||||
//
|
||||
// private var mCornerRadius : Float = 0.toFloat()
|
||||
//
|
||||
// val firstFrame : Bitmap
|
||||
// get() = state.firstFrame
|
||||
//
|
||||
// val frameTransformation : Transformation<Bitmap>
|
||||
// get() = state.frameTransformation
|
||||
//
|
||||
// val data : ByteArray
|
||||
// get() = state.data
|
||||
//
|
||||
// val frameCount : Int
|
||||
// get() = decoder.frameCount
|
||||
//
|
||||
//
|
||||
// constructor(other : GifDrawable, radius : Float) : this(cloneState(other)) {
|
||||
// this.mCornerRadius = radius
|
||||
// }
|
||||
//
|
||||
//
|
||||
// private constructor(state : GifDrawable.GifState) {
|
||||
//
|
||||
// this.state = state
|
||||
// this.decoder = GifDecoder(state.bitmapProvider)
|
||||
// this.paint = Paint(Paint.FILTER_BITMAP_FLAG or Paint.ANTI_ALIAS_FLAG)
|
||||
//
|
||||
// decoder.setData(state.gifHeader, state.data)
|
||||
// frameLoader = GifFrameLoader(
|
||||
// state.context,
|
||||
// this,
|
||||
// decoder,
|
||||
// state.targetWidth,
|
||||
// state.targetHeight
|
||||
// )
|
||||
// frameLoader.setFrameTransformation(
|
||||
// state.frameTransformation
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// // Visible for testing.
|
||||
// internal constructor(
|
||||
// decoder : GifDecoder,
|
||||
// frameLoader : GifFrameLoader,
|
||||
// firstFrame : Bitmap,
|
||||
// bitmapPool : BitmapPool,
|
||||
// paint : Paint
|
||||
// ) {
|
||||
// this.decoder = decoder
|
||||
// this.frameLoader = frameLoader
|
||||
// this.state = GifDrawable.GifState(null)
|
||||
// this.paint = paint
|
||||
// state.bitmapPool = bitmapPool
|
||||
// state.firstFrame = firstFrame
|
||||
// }
|
||||
//
|
||||
// fun setFrameTransformation(frameTransformation : Transformation<Bitmap>?, firstFrame : Bitmap?) {
|
||||
// if(firstFrame == null) {
|
||||
// throw NullPointerException("The first frame of the GIF must not be null")
|
||||
// }
|
||||
// if(frameTransformation == null) {
|
||||
// throw NullPointerException("The frame transformation must not be null")
|
||||
// }
|
||||
// state.frameTransformation = frameTransformation
|
||||
// state.firstFrame = firstFrame
|
||||
// frameLoader.setFrameTransformation(frameTransformation)
|
||||
// }
|
||||
//
|
||||
// private fun resetLoopCount() {
|
||||
// loopCount = 0
|
||||
// }
|
||||
//
|
||||
// override fun start() {
|
||||
// log.d("start")
|
||||
// isStarted = true
|
||||
// resetLoopCount()
|
||||
// if(isVisibleX) {
|
||||
// startRunning()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// override fun stop() {
|
||||
// isStarted = false
|
||||
// stopRunning()
|
||||
//
|
||||
// // On APIs > honeycomb we know our drawable is not being displayed anymore when it's callback is cleared and so
|
||||
// // we can use the absence of a callback as an indication that it's ok to clear our temporary data. Prior to
|
||||
// // honeycomb we can't tell if our callback is null and instead eagerly reset to avoid holding on to resources we
|
||||
// // no longer need.
|
||||
// if(Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
|
||||
// reset()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Clears temporary data and resets the drawable back to the first frame.
|
||||
// */
|
||||
// private fun reset() {
|
||||
// frameLoader.clear()
|
||||
// invalidateSelf()
|
||||
// }
|
||||
//
|
||||
// private fun startRunning() {
|
||||
// // If we have only a single frame, we don't want to decode it endlessly.
|
||||
// if(decoder.frameCount == 1) {
|
||||
// invalidateSelf()
|
||||
// } else if(! isRunning) {
|
||||
// isRunning = true
|
||||
// frameLoader.start()
|
||||
// invalidateSelf()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private fun stopRunning() {
|
||||
// isRunning = false
|
||||
// frameLoader.stop()
|
||||
// }
|
||||
//
|
||||
// override fun setVisible(visible : Boolean, restart : Boolean) : Boolean {
|
||||
// isVisibleX = visible
|
||||
// if(! visible) {
|
||||
// stopRunning()
|
||||
// } else if(isStarted) {
|
||||
// startRunning()
|
||||
// }
|
||||
// return super.setVisible(visible, restart)
|
||||
// }
|
||||
//
|
||||
// override fun getIntrinsicWidth() : Int {
|
||||
// return state.firstFrame.width
|
||||
// }
|
||||
//
|
||||
// override fun getIntrinsicHeight() : Int {
|
||||
// return state.firstFrame.height
|
||||
// }
|
||||
//
|
||||
// override fun isRunning() : Boolean {
|
||||
// return isRunning
|
||||
// }
|
||||
//
|
||||
// // For testing.
|
||||
// internal fun setIsRunning(isRunning : Boolean) {
|
||||
// this.isRunning = isRunning
|
||||
// }
|
||||
//
|
||||
// override fun onBoundsChange(bounds : Rect) {
|
||||
// super.onBoundsChange(bounds)
|
||||
// applyGravity = true
|
||||
// }
|
||||
//
|
||||
// override fun draw(canvas : Canvas) {
|
||||
// if(isRecycled) {
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// if(applyGravity) {
|
||||
// Gravity.apply(Gravity.FILL, intrinsicWidth, intrinsicHeight, bounds, destRect)
|
||||
// applyGravity = false
|
||||
// }
|
||||
//
|
||||
// val currentFrame = frameLoader.currentFrame
|
||||
// val toDraw = currentFrame ?: state.firstFrame
|
||||
//
|
||||
// if(mCornerRadius <= 0f) {
|
||||
// paint.shader = null
|
||||
// canvas.drawBitmap(toDraw, null, destRect, paint)
|
||||
// } else {
|
||||
// drawRoundImage(canvas, toDraw)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private val mShaderMatrix = Matrix()
|
||||
// private val mDstRectF = RectF()
|
||||
//
|
||||
// private fun drawRoundImage(canvas : Canvas, src : Bitmap?) {
|
||||
// if(src == null) return
|
||||
// val src_w = src.width
|
||||
// val src_h = src.height
|
||||
// if(src_w < 1 || src_h < 1) return
|
||||
// // int outWidth = destRect.width();
|
||||
// // int outHeight = destRect.height();
|
||||
//
|
||||
// mDstRectF.set(destRect)
|
||||
// mShaderMatrix.preScale(mDstRectF.width() / src_w, mDstRectF.height() / src_h)
|
||||
//
|
||||
// val mBitmapShader = BitmapShader(src, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
|
||||
// mBitmapShader.setLocalMatrix(mShaderMatrix)
|
||||
//
|
||||
// paint.shader = mBitmapShader
|
||||
// canvas.drawRoundRect(mDstRectF, mCornerRadius, mCornerRadius, paint)
|
||||
//
|
||||
// }
|
||||
//
|
||||
// override fun setAlpha(i : Int) {
|
||||
// paint.alpha = i
|
||||
// }
|
||||
//
|
||||
// override fun setColorFilter(colorFilter : ColorFilter?) {
|
||||
// paint.colorFilter = colorFilter
|
||||
// }
|
||||
//
|
||||
// override fun getOpacity() : Int {
|
||||
// // We can't tell, so default to transparent to be safe.
|
||||
// return PixelFormat.TRANSPARENT
|
||||
// }
|
||||
//
|
||||
// @TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
// override fun onFrameReady(frameIndex : Int) {
|
||||
// if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB && callback == null) {
|
||||
// stop()
|
||||
// reset()
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// invalidateSelf()
|
||||
//
|
||||
// if(frameIndex == decoder.frameCount - 1) {
|
||||
// loopCount ++
|
||||
// }
|
||||
//
|
||||
// if(maxLoopCount != GlideDrawable.LOOP_FOREVER && loopCount >= maxLoopCount) {
|
||||
// stop()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// override fun getConstantState() : Drawable.ConstantState? {
|
||||
// return state
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Clears any resources for loading frames that are currently held on to by this object.
|
||||
// */
|
||||
// fun recycle() {
|
||||
// isRecycled = true
|
||||
// state.bitmapPool.put(state.firstFrame)
|
||||
// frameLoader.clear()
|
||||
// frameLoader.stop()
|
||||
// }
|
||||
//
|
||||
// override fun isAnimated() : Boolean {
|
||||
// return true
|
||||
// }
|
||||
//
|
||||
// override fun setLoopCount(loopCount : Int) {
|
||||
// if(loopCount <= 0 && loopCount != GlideDrawable.LOOP_FOREVER && loopCount != GlideDrawable.LOOP_INTRINSIC) {
|
||||
// throw IllegalArgumentException("Loop count must be greater than 0, or equal to " + "GlideDrawable.LOOP_FOREVER, or equal to GlideDrawable.LOOP_INTRINSIC")
|
||||
// }
|
||||
//
|
||||
// maxLoopCount = if(loopCount == GlideDrawable.LOOP_INTRINSIC) {
|
||||
// val intrinsicCount = decoder.totalIterationCount
|
||||
// if(intrinsicCount == TOTAL_ITERATION_COUNT_FOREVER) GlideDrawable.LOOP_FOREVER else intrinsicCount
|
||||
// } else {
|
||||
// loopCount
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//}
|
|
@ -140,7 +140,7 @@ class MyGifDrawable internal constructor(state : GifDrawable.GifState) : Drawabl
|
|||
) : this(
|
||||
GifDrawable.GifState(
|
||||
GifFrameLoader(
|
||||
// TODO(b/27524013): Factor out this call to Glide.get()
|
||||
// XXX(b/27524013): Factor out this call to Glide.get()
|
||||
Glide.get(context),
|
||||
gifDecoder,
|
||||
targetFrameWidth,
|
||||
|
|
|
@ -155,7 +155,7 @@ class ActAppSetting : AppCompatActivity()
|
|||
|
||||
// initialize Switch and CheckBox
|
||||
for(info in Pref.map.values) {
|
||||
if( info is Pref.BooleanPref) {
|
||||
if( info is Pref.BooleanPref && info.id != 0 ) {
|
||||
val view = findViewById<CompoundButton>(info.id)
|
||||
view.setOnCheckedChangeListener(this)
|
||||
booleanViewList.add(BooleanViewInfo(info, view))
|
||||
|
|
|
@ -232,6 +232,15 @@ class ActColumnCustomize : AppCompatActivity(), View.OnClickListener, ColorPicke
|
|||
|
||||
Styler.fixHorizontalPadding(findViewById(R.id.svContent))
|
||||
|
||||
llColumnHeader = findViewById(R.id.llColumnHeader)
|
||||
ivColumnHeader = findViewById(R.id.ivColumnHeader)
|
||||
tvColumnName = findViewById(R.id.tvColumnName)
|
||||
flColumnBackground = findViewById(R.id.flColumnBackground)
|
||||
ivColumnBackground = findViewById(R.id.ivColumnBackground)
|
||||
tvSampleAcct = findViewById(R.id.tvSampleAcct)
|
||||
tvSampleContent = findViewById(R.id.tvSampleContent)
|
||||
|
||||
|
||||
findViewById<View>(R.id.btnHeaderBackgroundEdit).setOnClickListener(this)
|
||||
findViewById<View>(R.id.btnHeaderBackgroundReset).setOnClickListener(this)
|
||||
findViewById<View>(R.id.btnHeaderTextEdit).setOnClickListener(this)
|
||||
|
@ -245,14 +254,6 @@ class ActColumnCustomize : AppCompatActivity(), View.OnClickListener, ColorPicke
|
|||
findViewById<View>(R.id.btnContentColor).setOnClickListener(this)
|
||||
findViewById<View>(R.id.btnContentColorReset).setOnClickListener(this)
|
||||
|
||||
llColumnHeader = findViewById(R.id.llColumnHeader)
|
||||
ivColumnHeader = findViewById(R.id.ivColumnHeader)
|
||||
tvColumnName = findViewById(R.id.tvColumnName)
|
||||
|
||||
flColumnBackground = findViewById(R.id.flColumnBackground)
|
||||
ivColumnBackground = findViewById(R.id.ivColumnBackground)
|
||||
tvSampleAcct = findViewById(R.id.tvSampleAcct)
|
||||
tvSampleContent = findViewById(R.id.tvSampleContent)
|
||||
|
||||
content_color_default = tvSampleContent.textColors.defaultColor
|
||||
|
||||
|
|
|
@ -146,6 +146,9 @@ class ActMain : AppCompatActivity()
|
|||
private lateinit var vFooterDivider1 : View
|
||||
private lateinit var vFooterDivider2 : View
|
||||
|
||||
val viewPool = RecyclerView.RecycledViewPool()
|
||||
|
||||
|
||||
var timeline_font : Typeface? = null
|
||||
var timeline_font_bold : Typeface? = null
|
||||
|
||||
|
@ -210,7 +213,7 @@ class ActMain : AppCompatActivity()
|
|||
if(tag is ItemViewHolder) {
|
||||
column = tag.column
|
||||
break
|
||||
} else if(tag is HeaderViewHolderProfile) {
|
||||
} else if(tag is ViewHolderHeaderProfile) {
|
||||
column = tag.column
|
||||
break
|
||||
} else if(tag is TabletColumnViewHolder) {
|
||||
|
@ -1033,7 +1036,7 @@ class ActMain : AppCompatActivity()
|
|||
|
||||
var media_thumb_height = 64
|
||||
sv = Pref.spMediaThumbHeight(pref)
|
||||
if(sv?.isNotEmpty() == true) {
|
||||
if(sv.isNotEmpty()) {
|
||||
try {
|
||||
val iv = Integer.parseInt(sv)
|
||||
if(iv >= 32) {
|
||||
|
|
|
@ -5,8 +5,7 @@ import android.content.Context
|
|||
import android.net.Uri
|
||||
import android.os.AsyncTask
|
||||
import android.os.SystemClock
|
||||
import android.view.View
|
||||
import android.widget.ListView
|
||||
import android.support.v7.widget.RecyclerView
|
||||
import jp.juggler.subwaytooter.api.*
|
||||
|
||||
import org.json.JSONException
|
||||
|
@ -35,6 +34,7 @@ import jp.juggler.subwaytooter.util.VersionString
|
|||
import jp.juggler.subwaytooter.util.WordTrieTree
|
||||
import jp.juggler.subwaytooter.util.ScrollPosition
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.view.ListDivider
|
||||
|
||||
class Column(
|
||||
val app_state : AppState,
|
||||
|
@ -226,16 +226,6 @@ class Column(
|
|||
@Suppress("HasPlatformType")
|
||||
private val reSinceId = Pattern.compile("[&?]since_id=(\\d+)") // より新しいデータの取得に使う
|
||||
|
||||
private val heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
|
||||
|
||||
private fun getListItemHeight(listView : ListView, idx : Int) : Int {
|
||||
val item_width = listView.width - listView.paddingLeft - listView.paddingRight
|
||||
val widthSpec = View.MeasureSpec.makeMeasureSpec(item_width, View.MeasureSpec.EXACTLY)
|
||||
val childView = listView.adapter.getView(idx, null, listView)
|
||||
childView.measure(widthSpec, heightSpec)
|
||||
return childView.measuredHeight
|
||||
}
|
||||
|
||||
val COLUMN_REGEX_FILTER_DEFAULT = { _ : CharSequence? -> false }
|
||||
|
||||
}
|
||||
|
@ -1687,8 +1677,7 @@ class Column(
|
|||
|
||||
// 初期ロードの直後は先頭に移動する
|
||||
try {
|
||||
val holder = viewHolder
|
||||
holder?.listView?.setSelection(0)
|
||||
viewHolder?.listLayoutManager?.scrollToPositionWithOffset(0,0)
|
||||
} catch(ignored : Throwable) {
|
||||
}
|
||||
|
||||
|
@ -1809,6 +1798,7 @@ class Column(
|
|||
var src = TootAccount.parseList(context, access_info, jsonArray)
|
||||
list_tmp = addAll(null, src)
|
||||
if(! bBottom) {
|
||||
var bGapAdded = false
|
||||
while(true) {
|
||||
if(isCancelled) {
|
||||
log.d("refresh-account-top: cancelled.")
|
||||
|
@ -1830,6 +1820,7 @@ class Column(
|
|||
// タイムアウト
|
||||
// 隙間ができるかもしれない。後ほど手動で試してもらうしかない
|
||||
addOne(list_tmp, TootGap(max_id, last_since_id))
|
||||
bGapAdded = true
|
||||
break
|
||||
}
|
||||
|
||||
|
@ -1841,12 +1832,16 @@ class Column(
|
|||
// エラー
|
||||
// 隙間ができるかもしれない。後ほど手動で試してもらうしかない
|
||||
addOne(list_tmp, TootGap(max_id, last_since_id))
|
||||
bGapAdded = true
|
||||
break
|
||||
}
|
||||
|
||||
src = TootAccount.parseList(context, access_info, jsonArray)
|
||||
addAll(list_tmp, src)
|
||||
}
|
||||
if(Pref.bpForceGap(context) && !isCancelled && !bGapAdded && list_tmp?.isNotEmpty() ==true ){
|
||||
addOne(list_tmp, TootGap(max_id, last_since_id))
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
|
@ -1912,6 +1907,7 @@ class Column(
|
|||
src.sort()
|
||||
list_tmp = addAll(null, src)
|
||||
if(! bBottom) {
|
||||
var bGapAdded = false
|
||||
while(true) {
|
||||
if(isCancelled) {
|
||||
log.d("refresh-list-top: cancelled.")
|
||||
|
@ -1933,7 +1929,7 @@ class Column(
|
|||
// タイムアウト
|
||||
// 隙間ができるかもしれない。後ほど手動で試してもらうしかない
|
||||
addOne(list_tmp, TootGap(max_id, last_since_id))
|
||||
|
||||
bGapAdded =true
|
||||
break
|
||||
}
|
||||
|
||||
|
@ -1945,6 +1941,7 @@ class Column(
|
|||
// エラー
|
||||
// 隙間ができるかもしれない。後ほど手動で試してもらうしかない
|
||||
addOne(list_tmp, TootGap(max_id, last_since_id))
|
||||
bGapAdded =true
|
||||
break
|
||||
}
|
||||
|
||||
|
@ -1952,6 +1949,9 @@ class Column(
|
|||
src.sort()
|
||||
addAll(list_tmp, src)
|
||||
}
|
||||
if(Pref.bpForceGap(context) && !isCancelled && !bGapAdded && list_tmp?.isNotEmpty() ==true ){
|
||||
addOne(list_tmp, TootGap(max_id, last_since_id))
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
|
@ -1968,6 +1968,7 @@ class Column(
|
|||
var src = parseList(::TootReport, jsonArray)
|
||||
list_tmp = addAll(null, src)
|
||||
if(! bBottom) {
|
||||
var bGapAdded = false
|
||||
while(true) {
|
||||
if(isCancelled) {
|
||||
log.d("refresh-report-top: cancelled.")
|
||||
|
@ -1989,7 +1990,7 @@ class Column(
|
|||
// タイムアウト
|
||||
// 隙間ができるかもしれない。後ほど手動で試してもらうしかない
|
||||
addOne(list_tmp, TootGap(max_id, last_since_id))
|
||||
|
||||
bGapAdded = true
|
||||
break
|
||||
}
|
||||
|
||||
|
@ -2001,12 +2002,16 @@ class Column(
|
|||
// エラー
|
||||
// 隙間ができるかもしれない。後ほど手動で試してもらうしかない
|
||||
addOne(list_tmp, TootGap(max_id, last_since_id))
|
||||
bGapAdded = true
|
||||
break
|
||||
}
|
||||
|
||||
src = parseList(::TootReport, jsonArray)
|
||||
addAll(list_tmp, src)
|
||||
}
|
||||
if(Pref.bpForceGap(context) && !isCancelled && !bGapAdded && list_tmp?.isNotEmpty() ==true ){
|
||||
addOne(list_tmp, TootGap(max_id, last_since_id))
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
|
@ -2029,7 +2034,7 @@ class Column(
|
|||
if(! src.isEmpty()) {
|
||||
PollingWorker.injectData(context, access_info.db_id, src)
|
||||
}
|
||||
|
||||
var bGapAdded = false
|
||||
while(true) {
|
||||
if(isCancelled) {
|
||||
log.d("refresh-notification-top: cancelled.")
|
||||
|
@ -2051,6 +2056,7 @@ class Column(
|
|||
// タイムアウト
|
||||
// 隙間ができるかもしれない。後ほど手動で試してもらうしかない
|
||||
addOne(list_tmp, TootGap(max_id, last_since_id))
|
||||
bGapAdded = true
|
||||
break
|
||||
}
|
||||
|
||||
|
@ -2062,6 +2068,7 @@ class Column(
|
|||
// エラー
|
||||
// 隙間ができるかもしれない。後ほど手動で試してもらうしかない
|
||||
addOne(list_tmp, TootGap(max_id, last_since_id))
|
||||
bGapAdded = true
|
||||
break
|
||||
}
|
||||
|
||||
|
@ -2071,6 +2078,9 @@ class Column(
|
|||
PollingWorker.injectData(context, access_info.db_id, src)
|
||||
}
|
||||
}
|
||||
if(Pref.bpForceGap(context) && !isCancelled && !bGapAdded && list_tmp?.isNotEmpty() ==true ){
|
||||
addOne(list_tmp, TootGap(max_id, last_since_id))
|
||||
}
|
||||
} else {
|
||||
while(true) {
|
||||
if(isCancelled) {
|
||||
|
@ -2189,6 +2199,7 @@ class Column(
|
|||
}
|
||||
}
|
||||
} else {
|
||||
var bGapAdded = false
|
||||
while(true) {
|
||||
if(isCancelled) {
|
||||
log.d("refresh-status-top: cancelled.")
|
||||
|
@ -2211,6 +2222,7 @@ class Column(
|
|||
log.d("refresh-status-top: read enough. make gap.")
|
||||
// 隙間ができるかもしれない。後ほど手動で試してもらうしかない
|
||||
addOne(list_tmp, TootGap(max_id, last_since_id))
|
||||
bGapAdded = true
|
||||
break
|
||||
}
|
||||
|
||||
|
@ -2219,6 +2231,7 @@ class Column(
|
|||
// タイムアウト
|
||||
// 隙間ができるかもしれない。後ほど手動で試してもらうしかない
|
||||
addOne(list_tmp, TootGap(max_id, last_since_id))
|
||||
bGapAdded = true
|
||||
break
|
||||
}
|
||||
|
||||
|
@ -2230,12 +2243,16 @@ class Column(
|
|||
// エラー
|
||||
// 隙間ができるかもしれない。後ほど手動で試してもらうしかない
|
||||
addOne(list_tmp, TootGap(max_id, last_since_id))
|
||||
bGapAdded = true
|
||||
break
|
||||
}
|
||||
|
||||
src = parser.statusList(jsonArray)
|
||||
addWithFilterStatus(list_tmp, src)
|
||||
}
|
||||
if(Pref.bpForceGap(context) && !isCancelled && !bGapAdded && list_tmp?.isNotEmpty() ==true ){
|
||||
addOne(list_tmp, TootGap(max_id, last_since_id))
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
|
@ -2862,44 +2879,76 @@ class Column(
|
|||
|
||||
// 特定の要素が特定の位置に来るようにスクロール位置を調整する
|
||||
private fun setItemTop(holder : ColumnViewHolder, idxArg : Int, yArg : Int) {
|
||||
var idx = idxArg
|
||||
var y = yArg
|
||||
|
||||
val listView = holder.listView
|
||||
val hasHeader = holder.headerView != null
|
||||
if(hasHeader) {
|
||||
// Adapter中から見たpositionとListViewから見たpositionにズレができる
|
||||
++ idx
|
||||
}
|
||||
val headerViewHolder = listView.findViewHolderForAdapterPosition(0)
|
||||
var idx = if(headerViewHolder!= null) idxArg+1 else idxArg
|
||||
|
||||
var y = yArg
|
||||
while(y > 0 && idx > 0) {
|
||||
-- idx
|
||||
y -= getListItemHeight(listView, idx)
|
||||
y -= listView.dividerHeight
|
||||
y -= holder.getListItemHeight( idx)
|
||||
y -= ListDivider.height
|
||||
}
|
||||
listView.setSelectionFromTop(idx, y)
|
||||
holder.listLayoutManager.scrollToPositionWithOffset(idx, y)
|
||||
}
|
||||
|
||||
private fun getItemTop(holder : ColumnViewHolder, idxArg : Int) : Int {
|
||||
var idx = idxArg
|
||||
|
||||
val listView = holder.listView
|
||||
val hasHeader = holder.headerView != null
|
||||
val headerViewHolder = listView.findViewHolderForAdapterPosition(0)
|
||||
val layoutManager = holder.listLayoutManager
|
||||
val idx = if(headerViewHolder!= null) idxArg+1 else idxArg
|
||||
|
||||
if(hasHeader) {
|
||||
// Adapter中から見たpositionとListViewから見たpositionにズレができる
|
||||
++ idx
|
||||
}
|
||||
|
||||
val vs = listView.firstVisiblePosition
|
||||
val ve = listView.lastVisiblePosition
|
||||
if(idx < vs || ve < idx) {
|
||||
val vs = layoutManager.findFirstVisibleItemPosition()
|
||||
val ve = layoutManager.findLastVisibleItemPosition()
|
||||
if( vs == RecyclerView.NO_POSITION || ve == RecyclerView.NO_POSITION ){
|
||||
throw IndexOutOfBoundsException("listView has no visible position")
|
||||
}else if(idx < vs || ve < idx) {
|
||||
throw IndexOutOfBoundsException("not in visible range")
|
||||
}
|
||||
val child_idx = idx - vs
|
||||
return listView.getChildAt(child_idx).top
|
||||
}
|
||||
|
||||
enum class HeaderType(val viewType:Int){
|
||||
Profile(1),
|
||||
Search(2),
|
||||
Instance(3),
|
||||
}
|
||||
|
||||
fun getHeaderType():HeaderType?{
|
||||
return when(column_type) {
|
||||
Column.TYPE_PROFILE -> HeaderType.Profile
|
||||
Column.TYPE_SEARCH -> HeaderType.Search
|
||||
Column.TYPE_SEARCH_MSP -> HeaderType.Search
|
||||
Column.TYPE_SEARCH_TS -> HeaderType.Search
|
||||
Column.TYPE_INSTANCE_INFORMATION -> HeaderType.Instance
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadSearchDesc(raw_en : Int, raw_ja : Int) : String {
|
||||
val res_id = if("ja" == context.getString(R.string.language_code)) raw_ja else raw_en
|
||||
val data = Utils.loadRawResource(context, res_id)
|
||||
return if(data == null) "?" else Utils.decodeUTF8(data)
|
||||
}
|
||||
|
||||
private var cacheHeaderDesc: String? = null
|
||||
|
||||
fun getHeaderDesc() : String? {
|
||||
var cache = cacheHeaderDesc
|
||||
if( cache != null ) return cache
|
||||
cache = when(column_type) {
|
||||
Column.TYPE_SEARCH -> context.getString(R.string.search_desc_mastodon_api)
|
||||
Column.TYPE_SEARCH_MSP -> loadSearchDesc(R.raw.search_desc_msp_en, R.raw.search_desc_msp_ja)
|
||||
Column.TYPE_SEARCH_TS -> loadSearchDesc(R.raw.search_desc_ts_en, R.raw.search_desc_ts_ja)
|
||||
else -> ""
|
||||
}
|
||||
cacheHeaderDesc = cache
|
||||
return cache
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Streaming
|
||||
|
||||
|
@ -3163,7 +3212,7 @@ class Column(
|
|||
if(holder != null) {
|
||||
if(list_data.size > 0) {
|
||||
try {
|
||||
restore_idx = holder.listView.firstVisiblePosition
|
||||
restore_idx = holder.listLayoutManager.findFirstVisibleItemPosition()
|
||||
restore_y = getItemTop(holder, restore_idx)
|
||||
} catch(ex : IndexOutOfBoundsException) {
|
||||
restore_idx = - 1
|
||||
|
|
|
@ -26,12 +26,20 @@ import jp.juggler.subwaytooter.action.Action_List
|
|||
import jp.juggler.subwaytooter.action.Action_Notification
|
||||
import jp.juggler.subwaytooter.table.AcctColor
|
||||
import jp.juggler.subwaytooter.util.LogCategory
|
||||
import jp.juggler.subwaytooter.view.MyListView
|
||||
import jp.juggler.subwaytooter.util.ScrollPosition
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import android.support.v7.widget.ListRecyclerView
|
||||
import android.support.v7.widget.LinearLayoutManager
|
||||
import android.support.v7.widget.RecyclerView
|
||||
import jp.juggler.subwaytooter.view.ListDivider
|
||||
import java.lang.reflect.Field
|
||||
|
||||
internal class ColumnViewHolder(val activity : ActMain, root : View) : View.OnClickListener, SwipyRefreshLayout.OnRefreshListener, CompoundButton.OnCheckedChangeListener {
|
||||
|
||||
class ColumnViewHolder(
|
||||
val activity : ActMain,
|
||||
root : View
|
||||
) : View.OnClickListener,
|
||||
SwipyRefreshLayout.OnRefreshListener,
|
||||
CompoundButton.OnCheckedChangeListener {
|
||||
|
||||
companion object {
|
||||
private val log = LogCategory("ColumnViewHolder")
|
||||
|
@ -39,6 +47,21 @@ internal class ColumnViewHolder(val activity : ActMain, root : View) : View.OnCl
|
|||
private fun vg(v : View, visible : Boolean) {
|
||||
v.visibility = if(visible) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
val fieldRecycler : Field by lazy{
|
||||
val field = RecyclerView::class.java.getDeclaredField("mRecycler")
|
||||
field.isAccessible = true
|
||||
field
|
||||
}
|
||||
|
||||
val fieldState :Field by lazy{
|
||||
val field = RecyclerView::class.java.getDeclaredField("mState")
|
||||
field.isAccessible = true
|
||||
field
|
||||
}
|
||||
|
||||
val heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
|
||||
|
||||
}
|
||||
|
||||
var column : Column? = null
|
||||
|
@ -46,9 +69,10 @@ internal class ColumnViewHolder(val activity : ActMain, root : View) : View.OnCl
|
|||
private var page_idx : Int = 0
|
||||
|
||||
private val tvLoading : TextView
|
||||
val listView : MyListView
|
||||
val listView : ListRecyclerView
|
||||
val refreshLayout : SwipyRefreshLayout
|
||||
|
||||
lateinit var listLayoutManager : LinearLayoutManager
|
||||
|
||||
private val llColumnHeader : View
|
||||
private val tvColumnIndex : TextView
|
||||
private val tvColumnContext : TextView
|
||||
|
@ -97,7 +121,6 @@ internal class ColumnViewHolder(val activity : ActMain, root : View) : View.OnCl
|
|||
private var last_image_bitmap : Bitmap? = null
|
||||
private var last_image_task : AsyncTask<Void, Void, Bitmap?>? = null
|
||||
|
||||
|
||||
private val isRegexValid : Boolean
|
||||
get() {
|
||||
val s = etRegexFilter.text.toString()
|
||||
|
@ -105,13 +128,13 @@ internal class ColumnViewHolder(val activity : ActMain, root : View) : View.OnCl
|
|||
tvRegexFilterError.text = ""
|
||||
return true
|
||||
}
|
||||
try{
|
||||
try {
|
||||
val m = Pattern.compile(s).matcher("")
|
||||
if( m.find() ) {
|
||||
if(m.find()) {
|
||||
// XXX: 空文字列にマッチする正規表現はエラー扱いにする? しなくてもよい?
|
||||
// tvRegexFilterError.text = activity.getString(R.string.)
|
||||
}
|
||||
}catch(ex : Throwable) {
|
||||
} catch(ex : Throwable) {
|
||||
val message = ex.message
|
||||
tvRegexFilterError.text = if(message != null && message.isNotEmpty()) {
|
||||
message
|
||||
|
@ -126,18 +149,15 @@ internal class ColumnViewHolder(val activity : ActMain, root : View) : View.OnCl
|
|||
val isColumnSettingShown : Boolean
|
||||
get() = llColumnSetting.visibility == View.VISIBLE
|
||||
|
||||
|
||||
val headerView : HeaderViewHolderBase?
|
||||
get() = status_adapter?.header
|
||||
// val headerView : HeaderViewHolderBase?
|
||||
// get() = status_adapter?.header
|
||||
|
||||
val scrollPosition : ScrollPosition
|
||||
get() = ScrollPosition(listView)
|
||||
|
||||
get() = ScrollPosition(this)
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// Column から呼ばれる
|
||||
|
||||
|
||||
init {
|
||||
|
||||
if(activity.timeline_font != null) {
|
||||
|
@ -170,6 +190,7 @@ internal class ColumnViewHolder(val activity : ActMain, root : View) : View.OnCl
|
|||
|
||||
tvLoading = root.findViewById(R.id.tvLoading)
|
||||
listView = root.findViewById(R.id.listView)
|
||||
listView.recycledViewPool = activity.viewPool
|
||||
|
||||
btnSearch = root.findViewById(R.id.btnSearch)
|
||||
etSearch = root.findViewById(R.id.etSearch)
|
||||
|
@ -272,7 +293,6 @@ internal class ColumnViewHolder(val activity : ActMain, root : View) : View.OnCl
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private val proc_restoreScrollPosition = object : Runnable {
|
||||
override fun run() {
|
||||
activity.handler.removeCallbacks(this)
|
||||
|
@ -294,7 +314,7 @@ internal class ColumnViewHolder(val activity : ActMain, root : View) : View.OnCl
|
|||
}
|
||||
|
||||
if(column.hasMultipleViewHolder()) {
|
||||
log.d("restoreScrollPosition [%d] %s , column has multiple view holder. retry later.", page_idx, column .getColumnName(true))
|
||||
log.d("restoreScrollPosition [%d] %s , column has multiple view holder. retry later.", page_idx, column.getColumnName(true))
|
||||
|
||||
// タブレットモードでカラムを追加/削除した際に発生する。
|
||||
// このタイミングでスクロール位置を復元してもうまくいかないので延期する
|
||||
|
@ -302,19 +322,19 @@ internal class ColumnViewHolder(val activity : ActMain, root : View) : View.OnCl
|
|||
return
|
||||
}
|
||||
|
||||
val sp = column .scroll_save ?: //復元後にもここを通るがこれは正常である
|
||||
val sp = column.scroll_save ?: //復元後にもここを通るがこれは正常である
|
||||
// log.d( "restoreScrollPosition [%d] %s , column has no saved scroll position.", page_idx, column.getColumnName( true ) );
|
||||
return
|
||||
|
||||
column .scroll_save = null
|
||||
column.scroll_save = null
|
||||
|
||||
if(listView.visibility != View.VISIBLE) {
|
||||
log.d("restoreScrollPosition [%d] %s , listView is not visible. saved position %s,%s is dropped.", page_idx, column .getColumnName(true), sp.pos, sp.top
|
||||
log.d("restoreScrollPosition [%d] %s , listView is not visible. saved position %s,%s is dropped.", page_idx, column.getColumnName(true), sp.pos, sp.top
|
||||
)
|
||||
} else {
|
||||
log.d("restoreScrollPosition [%d] %s , listView is visible. resume %s,%s", page_idx, column .getColumnName(true), sp.pos, sp.top
|
||||
log.d("restoreScrollPosition [%d] %s , listView is visible. resume %s,%s", page_idx, column.getColumnName(true), sp.pos, sp.top
|
||||
)
|
||||
sp.restore(listView)
|
||||
sp.restore(this@ColumnViewHolder)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -349,18 +369,19 @@ internal class ColumnViewHolder(val activity : ActMain, root : View) : View.OnCl
|
|||
tvColumnIndex.text = activity.getString(R.string.column_index, page_idx + 1, page_count)
|
||||
|
||||
listView.adapter = null
|
||||
|
||||
val status_adapter = ItemListAdapter(activity, column, bSimpleList)
|
||||
listView.addItemDecoration(ListDivider(activity))
|
||||
val status_adapter = ItemListAdapter(activity, column, this, bSimpleList)
|
||||
this.status_adapter = status_adapter
|
||||
|
||||
status_adapter.header = when(column.column_type) {
|
||||
Column.TYPE_PROFILE -> HeaderViewHolderProfile(activity, column, listView)
|
||||
Column.TYPE_SEARCH -> HeaderViewHolderSearchDesc(activity, column, listView, activity.getString(R.string.search_desc_mastodon_api))
|
||||
Column.TYPE_SEARCH_MSP -> HeaderViewHolderSearchDesc(activity, column, listView, getSearchDesc(R.raw.search_desc_msp_en, R.raw.search_desc_msp_ja))
|
||||
Column.TYPE_SEARCH_TS -> HeaderViewHolderSearchDesc(activity, column, listView, getSearchDesc(R.raw.search_desc_ts_en, R.raw.search_desc_ts_ja))
|
||||
Column.TYPE_INSTANCE_INFORMATION -> HeaderViewHolderInstance(activity, column, listView)
|
||||
else -> null
|
||||
}
|
||||
|
||||
// status_adapter.header = when(column.column_type) {
|
||||
// Column.TYPE_PROFILE -> ViewHolderHeaderProfile(activity, column, listView)
|
||||
// Column.TYPE_SEARCH -> ViewHolderHeaderSearch(activity, column, listView, activity.getString(R.string.search_desc_mastodon_api))
|
||||
// Column.TYPE_SEARCH_MSP -> ViewHolderHeaderSearch(activity, column, listView, getSearchDesc(R.raw.search_desc_msp_en, R.raw.search_desc_msp_ja))
|
||||
// Column.TYPE_SEARCH_TS -> ViewHolderHeaderSearch(activity, column, listView, getSearchDesc(R.raw.search_desc_ts_en, R.raw.search_desc_ts_ja))
|
||||
// Column.TYPE_INSTANCE_INFORMATION -> ViewHolderHeaderInstance(activity, column, listView)
|
||||
// else -> null
|
||||
// }
|
||||
|
||||
val isNotificationColumn = column.column_type == Column.TYPE_NOTIFICATIONS
|
||||
|
||||
|
@ -381,7 +402,7 @@ internal class ColumnViewHolder(val activity : ActMain, root : View) : View.OnCl
|
|||
cbHideMediaDefault.isChecked = column.hide_media_default
|
||||
cbEnableSpeech.isChecked = column.enable_speech
|
||||
|
||||
etRegexFilter.setText(column.regex_text )
|
||||
etRegexFilter.setText(column.regex_text)
|
||||
etSearch.setText(column.search_query)
|
||||
cbResolve.isChecked = column.search_resolve
|
||||
|
||||
|
@ -418,6 +439,7 @@ internal class ColumnViewHolder(val activity : ActMain, root : View) : View.OnCl
|
|||
refreshLayout.isEnabled = true
|
||||
refreshLayout.direction = SwipyRefreshLayoutDirection.TOP
|
||||
}
|
||||
|
||||
else -> {
|
||||
refreshLayout.isEnabled = true
|
||||
refreshLayout.direction = SwipyRefreshLayoutDirection.BOTH
|
||||
|
@ -425,9 +447,12 @@ internal class ColumnViewHolder(val activity : ActMain, root : View) : View.OnCl
|
|||
}
|
||||
|
||||
//
|
||||
listLayoutManager = LinearLayoutManager(activity)
|
||||
listView.layoutManager = listLayoutManager
|
||||
listView.adapter = status_adapter
|
||||
listView.isFastScrollEnabled = ! Pref.bpDisableFastScroller(Pref.pref(activity))
|
||||
listView.onItemClickListener = status_adapter
|
||||
|
||||
//XXX FastScrollerのサポートを諦める。ライブラリはいくつかあるんだけど、設定でON/OFFできなかったり頭文字バブルを無効にできなかったり
|
||||
// listView.isFastScrollEnabled = ! Pref.bpDisableFastScroller(Pref.pref(activity))
|
||||
|
||||
column.addColumnViewHolder(this)
|
||||
|
||||
|
@ -439,12 +464,6 @@ internal class ColumnViewHolder(val activity : ActMain, root : View) : View.OnCl
|
|||
}
|
||||
}
|
||||
|
||||
private fun getSearchDesc(raw_en : Int, raw_ja : Int) : String {
|
||||
val res_id = if("ja" == activity.getString(R.string.language_code)) raw_ja else raw_en
|
||||
val data = Utils.loadRawResource(activity, res_id)
|
||||
return if(data == null) "?" else Utils.decodeUTF8(data)
|
||||
}
|
||||
|
||||
fun showColumnColor() {
|
||||
val column = this.column
|
||||
|
||||
|
@ -491,7 +510,7 @@ internal class ColumnViewHolder(val activity : ActMain, root : View) : View.OnCl
|
|||
|
||||
loadBackgroundImage(ivColumnBackgroundImage, column.column_bg_image)
|
||||
|
||||
status_adapter?.header?.showColor()
|
||||
status_adapter?.getHeaderViewHolder(listView)?.showColor()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -503,7 +522,7 @@ internal class ColumnViewHolder(val activity : ActMain, root : View) : View.OnCl
|
|||
last_image_bitmap?.recycle()
|
||||
last_image_bitmap = null
|
||||
|
||||
last_image_task ?.cancel(true)
|
||||
last_image_task?.cancel(true)
|
||||
last_image_task = null
|
||||
|
||||
last_image_uri = null
|
||||
|
@ -517,7 +536,7 @@ internal class ColumnViewHolder(val activity : ActMain, root : View) : View.OnCl
|
|||
@SuppressLint("StaticFieldLeak")
|
||||
private fun loadBackgroundImage(iv : ImageView, url : String?) {
|
||||
try {
|
||||
if(url == null || url.isEmpty() ) {
|
||||
if(url == null || url.isEmpty()) {
|
||||
// 指定がないなら閉じる
|
||||
closeBitmaps()
|
||||
return
|
||||
|
@ -589,9 +608,9 @@ internal class ColumnViewHolder(val activity : ActMain, root : View) : View.OnCl
|
|||
|
||||
// カラムを追加/削除したときに ColumnからColumnViewHolderへの参照が外れることがある
|
||||
// リロードやリフレッシュ操作で直るようにする
|
||||
column .addColumnViewHolder(this)
|
||||
column.addColumnViewHolder(this)
|
||||
|
||||
if(direction == SwipyRefreshLayoutDirection.TOP && column .canReloadWhenRefreshTop()) {
|
||||
if(direction == SwipyRefreshLayoutDirection.TOP && column.canReloadWhenRefreshTop()) {
|
||||
refreshLayout.isRefreshing = false
|
||||
activity.handler.post {
|
||||
this@ColumnViewHolder.column?.startLoading()
|
||||
|
@ -599,7 +618,7 @@ internal class ColumnViewHolder(val activity : ActMain, root : View) : View.OnCl
|
|||
return
|
||||
}
|
||||
|
||||
column .startRefresh(false, direction == SwipyRefreshLayoutDirection.BOTTOM, - 1L, - 1)
|
||||
column.startRefresh(false, direction == SwipyRefreshLayoutDirection.BOTTOM, - 1L, - 1)
|
||||
}
|
||||
|
||||
override fun onCheckedChanged(view : CompoundButton, isChecked : Boolean) {
|
||||
|
@ -609,76 +628,75 @@ internal class ColumnViewHolder(val activity : ActMain, root : View) : View.OnCl
|
|||
|
||||
// カラムを追加/削除したときに ColumnからColumnViewHolderへの参照が外れることがある
|
||||
// リロードやリフレッシュ操作で直るようにする
|
||||
column .addColumnViewHolder(this)
|
||||
column.addColumnViewHolder(this)
|
||||
|
||||
when(view.id) {
|
||||
|
||||
R.id.cbDontCloseColumn -> {
|
||||
column .dont_close = isChecked
|
||||
column.dont_close = isChecked
|
||||
showColumnCloseButton()
|
||||
activity.app_state.saveColumnList()
|
||||
}
|
||||
|
||||
R.id.cbWithAttachment -> {
|
||||
column .with_attachment = isChecked
|
||||
column.with_attachment = isChecked
|
||||
activity.app_state.saveColumnList()
|
||||
column .startLoading()
|
||||
column.startLoading()
|
||||
}
|
||||
|
||||
R.id.cbWithHighlight -> {
|
||||
column .with_highlight = isChecked
|
||||
column.with_highlight = isChecked
|
||||
activity.app_state.saveColumnList()
|
||||
column .startLoading()
|
||||
column.startLoading()
|
||||
}
|
||||
|
||||
|
||||
R.id.cbDontShowBoost -> {
|
||||
column .dont_show_boost = isChecked
|
||||
column.dont_show_boost = isChecked
|
||||
activity.app_state.saveColumnList()
|
||||
column .startLoading()
|
||||
column.startLoading()
|
||||
}
|
||||
|
||||
R.id.cbDontShowReply -> {
|
||||
column .dont_show_reply = isChecked
|
||||
column.dont_show_reply = isChecked
|
||||
activity.app_state.saveColumnList()
|
||||
column .startLoading()
|
||||
column.startLoading()
|
||||
}
|
||||
|
||||
R.id.cbDontShowFavourite -> {
|
||||
column .dont_show_favourite = isChecked
|
||||
column.dont_show_favourite = isChecked
|
||||
activity.app_state.saveColumnList()
|
||||
column .startLoading()
|
||||
column.startLoading()
|
||||
}
|
||||
|
||||
R.id.cbDontShowFollow -> {
|
||||
column .dont_show_follow = isChecked
|
||||
column.dont_show_follow = isChecked
|
||||
activity.app_state.saveColumnList()
|
||||
column .startLoading()
|
||||
column.startLoading()
|
||||
}
|
||||
|
||||
R.id.cbDontStreaming -> {
|
||||
column .dont_streaming = isChecked
|
||||
column.dont_streaming = isChecked
|
||||
activity.app_state.saveColumnList()
|
||||
if(isChecked) {
|
||||
column .stopStreaming()
|
||||
column.stopStreaming()
|
||||
} else {
|
||||
column .onStart(activity)
|
||||
column.onStart(activity)
|
||||
}
|
||||
}
|
||||
|
||||
R.id.cbDontAutoRefresh -> {
|
||||
column .dont_auto_refresh = isChecked
|
||||
column.dont_auto_refresh = isChecked
|
||||
activity.app_state.saveColumnList()
|
||||
}
|
||||
|
||||
R.id.cbHideMediaDefault -> {
|
||||
column .hide_media_default = isChecked
|
||||
column.hide_media_default = isChecked
|
||||
activity.app_state.saveColumnList()
|
||||
column .fireShowContent()
|
||||
column.fireShowContent()
|
||||
}
|
||||
|
||||
R.id.cbEnableSpeech -> {
|
||||
column .enable_speech = isChecked
|
||||
column.enable_speech = isChecked
|
||||
activity.app_state.saveColumnList()
|
||||
}
|
||||
}
|
||||
|
@ -716,7 +734,11 @@ internal class ColumnViewHolder(val activity : ActMain, root : View) : View.OnCl
|
|||
column.startLoading()
|
||||
}
|
||||
|
||||
R.id.llColumnHeader -> if(status_adapter .count > 0) listView.setSelectionFromTop(0, 0)
|
||||
R.id.llColumnHeader ->{
|
||||
if(status_adapter.itemCount > 0){
|
||||
listLayoutManager.scrollToPositionWithOffset(0,0)
|
||||
}
|
||||
}
|
||||
|
||||
R.id.btnColumnSetting -> llColumnSetting.visibility = if(llColumnSetting.visibility == View.VISIBLE) View.GONE else View.VISIBLE
|
||||
|
||||
|
@ -750,10 +772,10 @@ internal class ColumnViewHolder(val activity : ActMain, root : View) : View.OnCl
|
|||
|
||||
private fun showColumnCloseButton() {
|
||||
val column = this@ColumnViewHolder.column ?: return
|
||||
|
||||
|
||||
// カラム保護の状態
|
||||
btnColumnClose.isEnabled = ! column .dont_close
|
||||
btnColumnClose.alpha = if(column .dont_close) 0.3f else 1f
|
||||
btnColumnClose.isEnabled = ! column.dont_close
|
||||
btnColumnClose.alpha = if(column.dont_close) 0.3f else 1f
|
||||
}
|
||||
|
||||
// カラムヘッダなど、負荷が低い部分の表示更新
|
||||
|
@ -764,10 +786,10 @@ internal class ColumnViewHolder(val activity : ActMain, root : View) : View.OnCl
|
|||
val ac = AcctColor.load(acct)
|
||||
|
||||
val nickname = ac.nickname
|
||||
tvColumnContext.text = if( nickname != null && nickname.isNotEmpty() ) nickname else acct
|
||||
tvColumnContext.text = if(nickname != null && nickname.isNotEmpty()) nickname else acct
|
||||
|
||||
var c : Int
|
||||
|
||||
|
||||
c = ac.color_fg
|
||||
tvColumnContext.setTextColor(if(c != 0) c else Styler.getAttributeColor(activity, R.attr.colorTimeSmall))
|
||||
|
||||
|
@ -790,7 +812,7 @@ internal class ColumnViewHolder(val activity : ActMain, root : View) : View.OnCl
|
|||
// クラッシュレポートにadapterとリストデータの状態不整合が多かったので、
|
||||
// とりあえずリストデータ変更の通知だけは最優先で行っておく
|
||||
try {
|
||||
status_adapter ?.notifyDataSetChanged()
|
||||
status_adapter?.notifyDataSetChanged()
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
|
@ -816,14 +838,14 @@ internal class ColumnViewHolder(val activity : ActMain, root : View) : View.OnCl
|
|||
}
|
||||
|
||||
val initialLoadingError = column.mInitialLoadingError
|
||||
if( initialLoadingError.isNotEmpty() ) {
|
||||
if(initialLoadingError.isNotEmpty()) {
|
||||
showError(initialLoadingError)
|
||||
return
|
||||
}
|
||||
|
||||
val status_adapter = this.status_adapter
|
||||
|
||||
if(status_adapter == null || status_adapter.count == 0) {
|
||||
if(status_adapter == null || status_adapter.itemCount == 0) {
|
||||
showError(activity.getString(R.string.list_empty))
|
||||
return
|
||||
}
|
||||
|
@ -832,12 +854,12 @@ internal class ColumnViewHolder(val activity : ActMain, root : View) : View.OnCl
|
|||
|
||||
refreshLayout.visibility = View.VISIBLE
|
||||
|
||||
status_adapter.header?.bindData(column)
|
||||
status_adapter.getHeaderViewHolder(listView)?.bindData(column)
|
||||
|
||||
if(! column.bRefreshLoading) {
|
||||
refreshLayout.isRefreshing = false
|
||||
val refreshError = column.mRefreshLoadingError
|
||||
if(refreshError.isNotEmpty() ) {
|
||||
if(refreshError.isNotEmpty()) {
|
||||
Utils.showToast(activity, true, refreshError)
|
||||
column.mRefreshLoadingError = ""
|
||||
}
|
||||
|
@ -867,7 +889,7 @@ internal class ColumnViewHolder(val activity : ActMain, root : View) : View.OnCl
|
|||
}
|
||||
|
||||
else -> {
|
||||
val scroll_save = ScrollPosition(listView)
|
||||
val scroll_save = ScrollPosition(this)
|
||||
column.scroll_save = scroll_save
|
||||
log.d("saveScrollPosition [%d] %s , listView is visible, save %s,%s"
|
||||
, page_idx
|
||||
|
@ -879,17 +901,49 @@ internal class ColumnViewHolder(val activity : ActMain, root : View) : View.OnCl
|
|||
}
|
||||
}
|
||||
|
||||
fun setScrollPosition(sp : ScrollPosition, delta : Float) {
|
||||
fun setScrollPosition(sp : ScrollPosition, deltaDp : Float) {
|
||||
val last_adapter = listView.adapter
|
||||
if(column == null || last_adapter == null) return
|
||||
|
||||
sp.restore(listView)
|
||||
sp.restore(this)
|
||||
|
||||
listView.postDelayed(Runnable {
|
||||
val dy = (deltaDp * activity.density +0.5f).toInt()
|
||||
if(dy != 0) listView.postDelayed(Runnable {
|
||||
if(column == null || listView.adapter !== last_adapter) return@Runnable
|
||||
listView.scrollListBy((delta * activity.density).toInt())
|
||||
|
||||
try {
|
||||
val recycler = fieldRecycler.get(listView) as RecyclerView.Recycler
|
||||
val state = fieldState.get(listView) as RecyclerView.State
|
||||
listLayoutManager.scrollVerticallyBy(dy, recycler,state)
|
||||
}catch(ex:Throwable){
|
||||
log.trace(ex)
|
||||
log.e("can't access field in class %s",RecyclerView::class.java.simpleName)
|
||||
}
|
||||
}, 20L)
|
||||
}
|
||||
|
||||
|
||||
|
||||
fun getListItemHeight( idx : Int) : Int {
|
||||
val item_width = listView.width - listView.paddingLeft - listView.paddingRight
|
||||
val widthSpec = View.MeasureSpec.makeMeasureSpec(item_width, View.MeasureSpec.EXACTLY)
|
||||
|
||||
var childViewHolder = listView.findViewHolderForAdapterPosition(idx)
|
||||
if( childViewHolder != null ) {
|
||||
childViewHolder.itemView.measure(widthSpec, heightSpec)
|
||||
return childViewHolder.itemView.measuredHeight
|
||||
}
|
||||
|
||||
val adapter = status_adapter
|
||||
if( adapter != null) {
|
||||
log.d("getListItemHeight idx=$idx createView")
|
||||
val viewType = adapter.getItemViewType(idx)
|
||||
childViewHolder = adapter.createViewHolder(listView, viewType)
|
||||
adapter.bindViewHolder(childViewHolder, idx)
|
||||
childViewHolder.itemView.measure(widthSpec, heightSpec)
|
||||
return childViewHolder.itemView.measuredHeight
|
||||
}
|
||||
log.e("getListItemHeight: missing status adapter")
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
package jp.juggler.subwaytooter
|
||||
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import android.widget.TextView
|
||||
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.subwaytooter.util.LogCategory
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
|
||||
internal abstract class HeaderViewHolderBase(val activity : ActMain, val column : Column, val viewRoot : View) {
|
||||
|
||||
companion object {
|
||||
|
||||
private val log = LogCategory("HeaderViewHolderBase")
|
||||
|
||||
}
|
||||
|
||||
val access_info : SavedAccount
|
||||
|
||||
internal abstract fun showColor()
|
||||
|
||||
internal abstract fun bindData(column : Column)
|
||||
|
||||
init {
|
||||
this.access_info = column.access_info
|
||||
|
||||
// 初期化の間にthisを使うと警告が出るが、必要な処理なので…
|
||||
@Suppress("LeakingThis")
|
||||
viewRoot.tag = this
|
||||
|
||||
if(activity.timeline_font != null) {
|
||||
Utils.scanView(viewRoot) { v ->
|
||||
try {
|
||||
if(v is Button) {
|
||||
// ボタンは太字なので触らない
|
||||
} else if(v is TextView) {
|
||||
v.typeface = activity.timeline_font
|
||||
}
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
package jp.juggler.subwaytooter
|
||||
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import android.widget.TextView
|
||||
|
||||
import jp.juggler.subwaytooter.util.DecodeOptions
|
||||
import jp.juggler.subwaytooter.util.LogCategory
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.view.MyLinkMovementMethod
|
||||
import jp.juggler.subwaytooter.view.MyListView
|
||||
|
||||
internal class HeaderViewHolderSearchDesc(
|
||||
arg_activity : ActMain,
|
||||
arg_column : Column,
|
||||
parent : MyListView,
|
||||
html : String
|
||||
) : HeaderViewHolderBase(
|
||||
arg_activity,
|
||||
arg_column,
|
||||
arg_activity.layoutInflater.inflate(R.layout.lv_header_search_desc, parent, false)
|
||||
) {
|
||||
|
||||
init {
|
||||
|
||||
Utils.scanView(viewRoot) { v ->
|
||||
try {
|
||||
if(v is Button) {
|
||||
// ボタンは太字なので触らない
|
||||
} else if(v is TextView) {
|
||||
if(activity.timeline_font != null) {
|
||||
v.typeface = activity.timeline_font
|
||||
}
|
||||
if(! activity.timeline_font_size_sp.isNaN()) {
|
||||
v.textSize = activity.timeline_font_size_sp
|
||||
}
|
||||
}
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
}
|
||||
|
||||
val sv = DecodeOptions(decodeEmoji = true).decodeHTML(activity, access_info, html)
|
||||
|
||||
val tvSearchDesc = viewRoot.findViewById<TextView>(R.id.tvSearchDesc)
|
||||
tvSearchDesc.visibility = View.VISIBLE
|
||||
tvSearchDesc.movementMethod = MyLinkMovementMethod
|
||||
tvSearchDesc.text = sv
|
||||
}
|
||||
|
||||
override fun showColor() {
|
||||
//
|
||||
}
|
||||
|
||||
override fun bindData(column : Column) {
|
||||
//
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val log = LogCategory("HeaderViewHolderSearchDesc")
|
||||
}
|
||||
}
|
|
@ -1,80 +1,131 @@
|
|||
package jp.juggler.subwaytooter
|
||||
|
||||
import android.view.View
|
||||
import android.support.v7.widget.RecyclerView
|
||||
import android.view.ViewGroup
|
||||
import android.widget.AdapterView
|
||||
import android.widget.BaseAdapter
|
||||
|
||||
import jp.juggler.subwaytooter.view.MyListView
|
||||
internal class ItemListAdapter(
|
||||
private val activity : ActMain,
|
||||
private val column : Column,
|
||||
internal val columnVh : ColumnViewHolder,
|
||||
private val bSimpleList : Boolean
|
||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
|
||||
internal class ItemListAdapter(private val activity : ActMain, private val column : Column, private val bSimpleList : Boolean)
|
||||
: BaseAdapter()
|
||||
, AdapterView.OnItemClickListener {
|
||||
private val list : List<Any>
|
||||
|
||||
var header : HeaderViewHolderBase? = null
|
||||
|
||||
|
||||
init {
|
||||
this.list = column.list_data
|
||||
}
|
||||
|
||||
|
||||
override fun getCount() : Int {
|
||||
return (if(header != null) 1 else 0) + column.list_data.size
|
||||
override fun getItemCount() : Int {
|
||||
return when(column.getHeaderType()){
|
||||
null-> column.list_data.size
|
||||
else-> column.list_data.size +1
|
||||
}
|
||||
}
|
||||
|
||||
override fun getViewTypeCount() : Int {
|
||||
return if(header != null) 2 else 1
|
||||
}
|
||||
|
||||
|
||||
override fun getItemViewType(position : Int) : Int {
|
||||
if(header != null) {
|
||||
if(position == 0) return 1
|
||||
}
|
||||
return 0
|
||||
val headerType = column.getHeaderType()
|
||||
if( headerType == null || position>0 ) return 0
|
||||
return headerType.viewType
|
||||
}
|
||||
|
||||
override fun getItem(positionArg : Int) : Any? {
|
||||
var position = positionArg
|
||||
if(header != null) {
|
||||
if(position == 0) return header
|
||||
-- position
|
||||
override fun onCreateViewHolder(parent : ViewGroup?, viewType : Int) : RecyclerView.ViewHolder {
|
||||
when(viewType) {
|
||||
0 -> {
|
||||
val holder = ItemViewHolder(activity)
|
||||
holder.viewRoot.tag = holder
|
||||
return ViewHolderItem(holder)
|
||||
}
|
||||
Column.HeaderType.Profile.viewType -> {
|
||||
val viewRoot = activity.layoutInflater.inflate(R.layout.lv_header_profile, parent, false)
|
||||
val holder = ViewHolderHeaderProfile(activity,viewRoot)
|
||||
viewRoot.tag = holder
|
||||
return holder
|
||||
}
|
||||
Column.HeaderType.Search.viewType -> {
|
||||
val viewRoot = activity.layoutInflater.inflate(R.layout.lv_header_search_desc, parent, false)
|
||||
val holder = ViewHolderHeaderSearch(activity,viewRoot)
|
||||
viewRoot.tag = holder
|
||||
return holder
|
||||
}
|
||||
Column.HeaderType.Instance.viewType -> {
|
||||
val viewRoot = activity.layoutInflater.inflate(R.layout.lv_header_instance, parent, false)
|
||||
val holder = ViewHolderHeaderInstance(activity,viewRoot)
|
||||
viewRoot.tag = holder
|
||||
return holder
|
||||
}
|
||||
else -> throw RuntimeException("unknown viewType: $viewType")
|
||||
}
|
||||
return if(position >= 0 && position < column.list_data.size) list[position] else null
|
||||
}
|
||||
|
||||
fun getHeaderViewHolder(listView:RecyclerView): ViewHolderHeaderBase?{
|
||||
return when(column.getHeaderType()){
|
||||
null-> null
|
||||
else-> listView.findViewHolderForAdapterPosition(0) as? ViewHolderHeaderBase
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder : RecyclerView.ViewHolder, positionArg : Int) {
|
||||
val headerType = column.getHeaderType()
|
||||
if(holder is ViewHolderItem) {
|
||||
val position = if(headerType != null) positionArg - 1 else positionArg
|
||||
val o = if(position >= 0 && position < list.size) list[position] else null
|
||||
holder.ivh.bind(this, column, bSimpleList, o)
|
||||
} else if(holder is ViewHolderHeaderBase) {
|
||||
holder.bindData(column)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewRecycled(holder : RecyclerView.ViewHolder) {
|
||||
if(holder is ViewHolderItem) {
|
||||
holder.ivh.onViewRecycled()
|
||||
} else if(holder is ViewHolderHeaderBase) {
|
||||
holder.onViewRecycled()
|
||||
}
|
||||
}
|
||||
|
||||
// override fun getViewTypeCount() : Int {
|
||||
// return if(header != null) 2 else 1
|
||||
// }
|
||||
|
||||
|
||||
// override fun getItem(positionArg : Int) : Any? {
|
||||
// var position = positionArg
|
||||
// if(header != null) {
|
||||
// if(position == 0) return header
|
||||
// -- position
|
||||
// }
|
||||
// return if(position >= 0 && position < column.list_data.size) list[position] else null
|
||||
// }
|
||||
|
||||
override fun getItemId(position : Int) : Long {
|
||||
return 0
|
||||
}
|
||||
// override fun hasStableIds():Boolean = false
|
||||
|
||||
override fun getView(positionArg : Int, viewOld : View?, parent : ViewGroup) : View {
|
||||
var position = positionArg
|
||||
val header = this.header
|
||||
if(header != null) {
|
||||
if(position == 0) return header.viewRoot
|
||||
-- position
|
||||
}
|
||||
|
||||
val o = if(position >= 0 && position < list.size) list[position] else null
|
||||
|
||||
val holder : ItemViewHolder
|
||||
val view : View
|
||||
if(viewOld == null) {
|
||||
holder = ItemViewHolder(activity, column, this, bSimpleList)
|
||||
view = holder.viewRoot
|
||||
view.tag = holder
|
||||
} else {
|
||||
view = viewOld
|
||||
holder = view.tag as ItemViewHolder
|
||||
}
|
||||
holder.bind(o)
|
||||
return view
|
||||
}
|
||||
|
||||
override fun onItemClick(parent : AdapterView<*>, view : View, position : Int, id : Long) {
|
||||
if(bSimpleList) {
|
||||
(view.tag as? ItemViewHolder)?.onItemClick(parent as MyListView, view)
|
||||
}
|
||||
}
|
||||
// override fun getView(positionArg : Int, viewOld : View?, parent : ViewGroup) : View {
|
||||
// var position = positionArg
|
||||
// val header = this.header
|
||||
// if(header != null) {
|
||||
// if(position == 0) return header.viewRoot
|
||||
// -- position
|
||||
// }
|
||||
//
|
||||
// val o = if(position >= 0 && position < list.size) list[position] else null
|
||||
//
|
||||
// val holder : ItemViewHolder
|
||||
// val view : View
|
||||
// if(viewOld == null) {
|
||||
//
|
||||
// } else {
|
||||
// view = viewOld
|
||||
// holder = view.tag as ItemViewHolder
|
||||
// }
|
||||
// holder.bind(o)
|
||||
// return view
|
||||
// }
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ package jp.juggler.subwaytooter
|
|||
import android.content.Context
|
||||
import android.graphics.Typeface
|
||||
import android.net.Uri
|
||||
import android.os.SystemClock
|
||||
import android.support.v4.content.ContextCompat
|
||||
import android.support.v4.view.ViewCompat
|
||||
import android.support.v7.app.AlertDialog
|
||||
|
@ -44,18 +45,20 @@ import org.jetbrains.anko.*
|
|||
import org.json.JSONObject
|
||||
|
||||
internal class ItemViewHolder(
|
||||
val activity : ActMain,
|
||||
val column : Column,
|
||||
private val list_adapter : ItemListAdapter,
|
||||
private val bSimpleList : Boolean
|
||||
val activity : ActMain
|
||||
) : View.OnClickListener, View.OnLongClickListener {
|
||||
|
||||
companion object {
|
||||
private val log = LogCategory("ItemViewHolder")
|
||||
}
|
||||
|
||||
var viewRoot : View
|
||||
|
||||
val viewRoot : View
|
||||
|
||||
private var bSimpleList : Boolean = false
|
||||
|
||||
lateinit var column : Column
|
||||
|
||||
private lateinit var list_adapter : ItemListAdapter
|
||||
private lateinit var llBoosted : View
|
||||
private lateinit var ivBoosted : ImageView
|
||||
private lateinit var tvBoosted : TextView
|
||||
|
@ -112,10 +115,9 @@ internal class ItemViewHolder(
|
|||
|
||||
private lateinit var tvApplication : TextView
|
||||
|
||||
private lateinit var access_info : SavedAccount
|
||||
|
||||
private val access_info : SavedAccount
|
||||
|
||||
private val buttons_for_status : StatusButtons?
|
||||
private var buttons_for_status : StatusButtons? = null
|
||||
|
||||
private var item : Any? = null
|
||||
|
||||
|
@ -136,51 +138,6 @@ internal class ItemViewHolder(
|
|||
|
||||
init {
|
||||
this.viewRoot = inflate(activity.UI {})
|
||||
this.access_info = column.access_info
|
||||
|
||||
if(activity.timeline_font != null || activity.timeline_font_bold != null) {
|
||||
Utils.scanView(this.viewRoot) { v ->
|
||||
try {
|
||||
if(v is Button) {
|
||||
// ボタンは太字なので触らない
|
||||
} else if(v is TextView) {
|
||||
val typeface = when {
|
||||
v === tvName || v === tvFollowerName || v === tvBoosted -> activity.timeline_font_bold ?: activity.timeline_font
|
||||
else -> activity.timeline_font ?: activity.timeline_font_bold
|
||||
}
|
||||
if(typeface != null) v.typeface = typeface
|
||||
}
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
tvName.typeface = Typeface.DEFAULT_BOLD
|
||||
tvFollowerName.typeface = Typeface.DEFAULT_BOLD
|
||||
tvBoosted.typeface = Typeface.DEFAULT_BOLD
|
||||
}
|
||||
|
||||
if(bSimpleList) {
|
||||
llButtonBar.visibility = View.GONE
|
||||
this.buttons_for_status = null
|
||||
} else {
|
||||
llButtonBar.visibility = View.VISIBLE
|
||||
this.buttons_for_status = StatusButtons(
|
||||
activity,
|
||||
column,
|
||||
false,
|
||||
|
||||
btnConversation = btnConversation,
|
||||
btnReply = btnReply,
|
||||
btnBoost = btnBoost,
|
||||
btnFavourite = btnFavourite,
|
||||
llFollow2 = llFollow2,
|
||||
btnFollow2 = btnFollow2,
|
||||
ivFollowedBy2 = ivFollowedBy2,
|
||||
btnMore = btnMore
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
btnListTL.setOnClickListener(this)
|
||||
btnListMore.setOnClickListener(this)
|
||||
|
@ -251,7 +208,91 @@ internal class ItemViewHolder(
|
|||
this.name_invalidator = NetworkEmojiInvalidator(activity.handler, tvName)
|
||||
}
|
||||
|
||||
fun bind(item : Any?) {
|
||||
fun onViewRecycled() {
|
||||
|
||||
}
|
||||
|
||||
fun bind(list_adapter : ItemListAdapter, column : Column, bSimpleList : Boolean, item : Any?) {
|
||||
this.list_adapter = list_adapter
|
||||
this.column = column
|
||||
this.bSimpleList = bSimpleList
|
||||
|
||||
this.access_info = column.access_info
|
||||
|
||||
if(activity.timeline_font != null || activity.timeline_font_bold != null) {
|
||||
Utils.scanView(this.viewRoot) { v ->
|
||||
try {
|
||||
if(v is Button) {
|
||||
// ボタンは太字なので触らない
|
||||
} else if(v is TextView) {
|
||||
val typeface = when {
|
||||
v === tvName || v === tvFollowerName || v === tvBoosted -> activity.timeline_font_bold
|
||||
?: activity.timeline_font
|
||||
else -> activity.timeline_font ?: activity.timeline_font_bold
|
||||
}
|
||||
if(typeface != null) v.typeface = typeface
|
||||
}
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
tvName.typeface = Typeface.DEFAULT_BOLD
|
||||
tvFollowerName.typeface = Typeface.DEFAULT_BOLD
|
||||
tvBoosted.typeface = Typeface.DEFAULT_BOLD
|
||||
}
|
||||
|
||||
if(bSimpleList) {
|
||||
|
||||
viewRoot.setOnTouchListener { _, ev ->
|
||||
// ポップアップを閉じた時にクリックでリストを触ったことになってしまう不具合の回避
|
||||
val now = SystemClock.elapsedRealtime()
|
||||
// ポップアップを閉じた直後はタッチダウンを無視する
|
||||
if(now - StatusButtonsPopup.last_popup_close >= 30L) {
|
||||
false
|
||||
} else {
|
||||
val action = ev.action
|
||||
log.d("onTouchEvent action=$action")
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
viewRoot.setOnClickListener { viewClicked ->
|
||||
activity.closeListItemPopup()
|
||||
status__showing?.let { status ->
|
||||
val popup = StatusButtonsPopup(activity, column, bSimpleList)
|
||||
activity.listItemPopup = popup
|
||||
popup.show(
|
||||
list_adapter.columnVh.listView,
|
||||
viewClicked,
|
||||
status,
|
||||
item as? TootNotification
|
||||
)
|
||||
}
|
||||
}
|
||||
llButtonBar.visibility = View.GONE
|
||||
this.buttons_for_status = null
|
||||
} else {
|
||||
viewRoot.isClickable = false
|
||||
llButtonBar.visibility = View.VISIBLE
|
||||
this.buttons_for_status = StatusButtons(
|
||||
activity,
|
||||
column,
|
||||
false,
|
||||
|
||||
btnConversation = btnConversation,
|
||||
btnReply = btnReply,
|
||||
btnBoost = btnBoost,
|
||||
btnFavourite = btnFavourite,
|
||||
llFollow2 = llFollow2,
|
||||
btnFollow2 = btnFollow2,
|
||||
ivFollowedBy2 = ivFollowedBy2,
|
||||
btnMore = btnMore
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
this.item = null
|
||||
this.status__showing = null
|
||||
this.status_account = null
|
||||
|
@ -265,8 +306,6 @@ internal class ItemViewHolder(
|
|||
llList.visibility = View.GONE
|
||||
llExtra.removeAllViews()
|
||||
|
||||
|
||||
|
||||
if(item == null) return
|
||||
|
||||
var c : Int
|
||||
|
@ -281,7 +320,10 @@ internal class ItemViewHolder(
|
|||
//NSFWは文字色固定 btnShowMedia.setTextColor( c );
|
||||
tvApplication.setTextColor(c)
|
||||
|
||||
c = if(column.acct_color != 0) column.acct_color else Styler.getAttributeColor(activity, R.attr.colorTimeSmall)
|
||||
c = if(column.acct_color != 0) column.acct_color else Styler.getAttributeColor(
|
||||
activity,
|
||||
R.attr.colorTimeSmall
|
||||
)
|
||||
this.acct_color = c
|
||||
tvBoostedTime.setTextColor(c)
|
||||
tvTime.setTextColor(c)
|
||||
|
@ -303,9 +345,16 @@ internal class ItemViewHolder(
|
|||
if(reblog != null) {
|
||||
showBoost(
|
||||
item.account
|
||||
, item.time_created_at
|
||||
, R.attr.btn_boost
|
||||
, Utils.formatSpannable1(activity, R.string.display_name_boosted_by, item.account.decoded_display_name)
|
||||
,
|
||||
item.time_created_at
|
||||
,
|
||||
R.attr.btn_boost
|
||||
,
|
||||
Utils.formatSpannable1(
|
||||
activity,
|
||||
R.string.display_name_boosted_by,
|
||||
item.account.decoded_display_name
|
||||
)
|
||||
)
|
||||
showStatus(activity, reblog)
|
||||
} else {
|
||||
|
@ -325,9 +374,16 @@ internal class ItemViewHolder(
|
|||
TootNotification.TYPE_FAVOURITE -> {
|
||||
if(n_account != null) showBoost(
|
||||
n_account
|
||||
, n.time_created_at
|
||||
, if(access_info.isNicoru(n_account)) R.attr.ic_nicoru else R.attr.btn_favourite
|
||||
, Utils.formatSpannable1(activity, R.string.display_name_favourited_by, n_account.decoded_display_name)
|
||||
,
|
||||
n.time_created_at
|
||||
,
|
||||
if(access_info.isNicoru(n_account)) R.attr.ic_nicoru else R.attr.btn_favourite
|
||||
,
|
||||
Utils.formatSpannable1(
|
||||
activity,
|
||||
R.string.display_name_favourited_by,
|
||||
n_account.decoded_display_name
|
||||
)
|
||||
)
|
||||
if(n_status != null) showStatus(activity, n_status)
|
||||
}
|
||||
|
@ -335,9 +391,16 @@ internal class ItemViewHolder(
|
|||
TootNotification.TYPE_REBLOG -> {
|
||||
if(n_account != null) showBoost(
|
||||
n_account
|
||||
, n.time_created_at
|
||||
, R.attr.btn_boost
|
||||
, Utils.formatSpannable1(activity, R.string.display_name_boosted_by, n_account.decoded_display_name)
|
||||
,
|
||||
n.time_created_at
|
||||
,
|
||||
R.attr.btn_boost
|
||||
,
|
||||
Utils.formatSpannable1(
|
||||
activity,
|
||||
R.string.display_name_boosted_by,
|
||||
n_account.decoded_display_name
|
||||
)
|
||||
)
|
||||
if(n_status != null) showStatus(activity, n_status)
|
||||
|
||||
|
@ -347,9 +410,16 @@ internal class ItemViewHolder(
|
|||
if(n_account != null) {
|
||||
showBoost(
|
||||
n_account
|
||||
, n.time_created_at
|
||||
, R.attr.ic_follow_plus
|
||||
, Utils.formatSpannable1(activity, R.string.display_name_followed_by, n_account.decoded_display_name)
|
||||
,
|
||||
n.time_created_at
|
||||
,
|
||||
R.attr.ic_follow_plus
|
||||
,
|
||||
Utils.formatSpannable1(
|
||||
activity,
|
||||
R.string.display_name_followed_by,
|
||||
n_account.decoded_display_name
|
||||
)
|
||||
)
|
||||
showAccount(n_account)
|
||||
}
|
||||
|
@ -359,9 +429,16 @@ internal class ItemViewHolder(
|
|||
if(! bSimpleList) {
|
||||
if(n_account != null) showBoost(
|
||||
n_account
|
||||
, n.time_created_at
|
||||
, R.attr.btn_reply
|
||||
, Utils.formatSpannable1(activity, R.string.display_name_replied_by, n_account.decoded_display_name)
|
||||
,
|
||||
n.time_created_at
|
||||
,
|
||||
R.attr.btn_reply
|
||||
,
|
||||
Utils.formatSpannable1(
|
||||
activity,
|
||||
R.string.display_name_replied_by,
|
||||
n_account.decoded_display_name
|
||||
)
|
||||
)
|
||||
}
|
||||
if(n_status != null) showStatus(activity, n_status)
|
||||
|
@ -435,7 +512,10 @@ internal class ItemViewHolder(
|
|||
tvName.text = who.decoded_display_name
|
||||
name_invalidator.register(who.decoded_display_name)
|
||||
ivThumbnail.setImageUrl(
|
||||
activity.pref, 16f, access_info.supplyBaseUrl(who.avatar_static), access_info.supplyBaseUrl(who.avatar)
|
||||
activity.pref,
|
||||
16f,
|
||||
access_info.supplyBaseUrl(who.avatar_static),
|
||||
access_info.supplyBaseUrl(who.avatar)
|
||||
)
|
||||
// }
|
||||
|
||||
|
@ -544,11 +624,12 @@ internal class ItemViewHolder(
|
|||
|
||||
val application = status.application
|
||||
if(application != null
|
||||
&&( column.column_type == Column.TYPE_CONVERSATION || Pref.bpShowAppName(activity.pref) )
|
||||
&& (column.column_type == Column.TYPE_CONVERSATION || Pref.bpShowAppName(activity.pref))
|
||||
) {
|
||||
tvApplication.visibility = View.VISIBLE
|
||||
tvApplication.text = activity.getString(R.string.application_is, application?.name ?: "")
|
||||
}else{
|
||||
tvApplication.text =
|
||||
activity.getString(R.string.application_is, application.name ?: "")
|
||||
} else {
|
||||
tvApplication.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
@ -563,7 +644,12 @@ internal class ItemViewHolder(
|
|||
sb.append("NSFW")
|
||||
val end = sb.length
|
||||
val icon_id = Styler.getAttributeResourceId(activity, R.attr.ic_eye_off)
|
||||
sb.setSpan(EmojiImageSpan(activity, icon_id), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
sb.setSpan(
|
||||
EmojiImageSpan(activity, icon_id),
|
||||
start,
|
||||
end,
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
}
|
||||
|
||||
val visIconAttrId = Styler.getVisibilityIconAttr(status.visibility)
|
||||
|
@ -573,7 +659,12 @@ internal class ItemViewHolder(
|
|||
sb.append(status.visibility)
|
||||
val end = sb.length
|
||||
val iconResId = Styler.getAttributeResourceId(activity, visIconAttrId)
|
||||
sb.setSpan(EmojiImageSpan(activity, iconResId), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
sb.setSpan(
|
||||
EmojiImageSpan(activity, iconResId),
|
||||
start,
|
||||
end,
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
}
|
||||
|
||||
if(status.pinned) {
|
||||
|
@ -582,11 +673,22 @@ internal class ItemViewHolder(
|
|||
sb.append("pinned")
|
||||
val end = sb.length
|
||||
val icon_id = Styler.getAttributeResourceId(activity, R.attr.ic_pin)
|
||||
sb.setSpan(EmojiImageSpan(activity, icon_id), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
sb.setSpan(
|
||||
EmojiImageSpan(activity, icon_id),
|
||||
start,
|
||||
end,
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
}
|
||||
|
||||
if(sb.isNotEmpty()) sb.append(' ')
|
||||
sb.append(TootStatus.formatTime(activity, status.time_created_at, column.column_type != Column.TYPE_CONVERSATION))
|
||||
sb.append(
|
||||
TootStatus.formatTime(
|
||||
activity,
|
||||
status.time_created_at,
|
||||
column.column_type != Column.TYPE_CONVERSATION
|
||||
)
|
||||
)
|
||||
tvTime.text = sb
|
||||
}
|
||||
|
||||
|
@ -617,18 +719,25 @@ internal class ItemViewHolder(
|
|||
tvContent.minLines = r?.originalLineCount ?: - 1
|
||||
if(r?.decoded_spoiler_text != null) {
|
||||
// 自動CWの場合はContentWarningのテキストを切り替える
|
||||
tvContentWarning.text = if(shown) activity.getString(R.string.auto_cw_prefix) else r.decoded_spoiler_text
|
||||
tvContentWarning.text =
|
||||
if(shown) activity.getString(R.string.auto_cw_prefix) else r.decoded_spoiler_text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setMedia(iv : MyNetworkImageView, status : TootStatus, media_attachments : ArrayList<TootAttachmentLike>, idx : Int) {
|
||||
private fun setMedia(
|
||||
iv : MyNetworkImageView,
|
||||
status : TootStatus,
|
||||
media_attachments : ArrayList<TootAttachmentLike>,
|
||||
idx : Int
|
||||
) {
|
||||
val ta = if(idx < media_attachments.size) media_attachments[idx] else null
|
||||
if(ta != null) {
|
||||
val url = ta.urlForThumbnail
|
||||
if(url != null && url.isNotEmpty()) {
|
||||
iv.visibility = View.VISIBLE
|
||||
iv.scaleType = if(activity.dont_crop_media_thumbnail) ImageView.ScaleType.FIT_CENTER else ImageView.ScaleType.CENTER_CROP
|
||||
iv.scaleType =
|
||||
if(activity.dont_crop_media_thumbnail) ImageView.ScaleType.FIT_CENTER else ImageView.ScaleType.CENTER_CROP
|
||||
|
||||
val mediaType = ta.type
|
||||
when(mediaType) {
|
||||
|
@ -638,11 +747,19 @@ internal class ItemViewHolder(
|
|||
else -> iv.setMediaType(0)
|
||||
}
|
||||
|
||||
iv.setImageUrl(activity.pref, 0f, access_info.supplyBaseUrl(url), access_info.supplyBaseUrl(url))
|
||||
iv.setImageUrl(
|
||||
activity.pref,
|
||||
0f,
|
||||
access_info.supplyBaseUrl(url),
|
||||
access_info.supplyBaseUrl(url)
|
||||
)
|
||||
|
||||
val description = ta.description
|
||||
if(description != null && description.isNotEmpty()) {
|
||||
val lp = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)
|
||||
val lp = LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
lp.topMargin = (0.5f + llExtra.resources.displayMetrics.density * 3f).toInt()
|
||||
val tv = MyTextView(activity)
|
||||
tv.layoutParams = lp
|
||||
|
@ -651,11 +768,13 @@ internal class ItemViewHolder(
|
|||
if(! activity.timeline_font_size_sp.isNaN()) {
|
||||
tv.textSize = activity.timeline_font_size_sp
|
||||
}
|
||||
val c = if(column.content_color != 0) column.content_color else content_color_default
|
||||
val c =
|
||||
if(column.content_color != 0) column.content_color else content_color_default
|
||||
tv.setTextColor(c)
|
||||
|
||||
//
|
||||
val desc = activity.getString(R.string.media_description, idx + 1, ta.description)
|
||||
val desc =
|
||||
activity.getString(R.string.media_description, idx + 1, ta.description)
|
||||
tv.text = DecodeOptions(
|
||||
emojiMapCustom = status.custom_emojis,
|
||||
emojiMapProfile = status.profile_emojis
|
||||
|
@ -733,13 +852,25 @@ internal class ItemViewHolder(
|
|||
AlertDialog.Builder(activity)
|
||||
.setMessage(activity.getString(R.string.confirm_unblock_domain, domain))
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.setPositiveButton(R.string.ok) { _, _ -> Action_Instance.blockDomain(activity, access_info, domain, false) }
|
||||
.setPositiveButton(R.string.ok) { _, _ ->
|
||||
Action_Instance.blockDomain(
|
||||
activity,
|
||||
access_info,
|
||||
domain,
|
||||
false
|
||||
)
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
is String -> {
|
||||
// search_tag は#を含まない
|
||||
Action_HashTag.timeline(activity, activity.nextPosition(column), access_info, item)
|
||||
Action_HashTag.timeline(
|
||||
activity,
|
||||
activity.nextPosition(column),
|
||||
access_info,
|
||||
item
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -782,22 +913,53 @@ internal class ItemViewHolder(
|
|||
when(v) {
|
||||
|
||||
ivThumbnail -> {
|
||||
status_account?.let { who -> DlgContextMenu(activity, column, who, null, notification).show() }
|
||||
status_account?.let { who ->
|
||||
DlgContextMenu(
|
||||
activity,
|
||||
column,
|
||||
who,
|
||||
null,
|
||||
notification
|
||||
).show()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
llBoosted -> {
|
||||
boost_account?.let { who -> DlgContextMenu(activity, column, who, null, notification).show() }
|
||||
boost_account?.let { who ->
|
||||
DlgContextMenu(
|
||||
activity,
|
||||
column,
|
||||
who,
|
||||
null,
|
||||
notification
|
||||
).show()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
llFollow -> {
|
||||
follow_account?.let { who -> DlgContextMenu(activity, column, who, null, notification).show() }
|
||||
follow_account?.let { who ->
|
||||
DlgContextMenu(
|
||||
activity,
|
||||
column,
|
||||
who,
|
||||
null,
|
||||
notification
|
||||
).show()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
btnFollow -> {
|
||||
follow_account?.let { who -> Action_Follow.followFromAnotherAccount(activity, activity.nextPosition(column), access_info, who) }
|
||||
follow_account?.let { who ->
|
||||
Action_Follow.followFromAnotherAccount(
|
||||
activity,
|
||||
activity.nextPosition(column),
|
||||
access_info,
|
||||
who
|
||||
)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -846,7 +1008,11 @@ internal class ItemViewHolder(
|
|||
is TootAttachmentMSP -> {
|
||||
// マストドン検索ポータルのデータではmedia_attachmentsが簡略化されている
|
||||
// 会話の流れを表示する
|
||||
Action_Toot.conversationOtherInstance(activity, activity.nextPosition(column), status__showing)
|
||||
Action_Toot.conversationOtherInstance(
|
||||
activity,
|
||||
activity.nextPosition(column),
|
||||
status__showing
|
||||
)
|
||||
}
|
||||
|
||||
is TootAttachment -> {
|
||||
|
@ -866,19 +1032,11 @@ internal class ItemViewHolder(
|
|||
}
|
||||
}
|
||||
|
||||
// 簡略ビューの時だけ呼ばれる
|
||||
// StatusButtonsPopupを表示する
|
||||
fun onItemClick(listView : MyListView, anchor : View) {
|
||||
activity.closeListItemPopup()
|
||||
status__showing?.let { status ->
|
||||
val popup = StatusButtonsPopup(activity, column, bSimpleList)
|
||||
activity.listItemPopup = popup
|
||||
popup.show(listView, anchor, status, item as? TootNotification)
|
||||
}
|
||||
}
|
||||
|
||||
private fun makeCardView(activity : ActMain, llExtra : LinearLayout, card : TootCard) {
|
||||
var lp = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)
|
||||
var lp = LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
lp.topMargin = (0.5f + llExtra.resources.displayMetrics.density * 3f).toInt()
|
||||
val tv = MyTextView(activity)
|
||||
tv.layoutParams = lp
|
||||
|
@ -892,8 +1050,18 @@ internal class ItemViewHolder(
|
|||
|
||||
val sb = StringBuilder()
|
||||
addLinkAndCaption(sb, activity.getString(R.string.card_header_card), card.url, card.title)
|
||||
addLinkAndCaption(sb, activity.getString(R.string.card_header_author), card.author_url, card.author_name)
|
||||
addLinkAndCaption(sb, activity.getString(R.string.card_header_provider), card.provider_url, card.provider_name)
|
||||
addLinkAndCaption(
|
||||
sb,
|
||||
activity.getString(R.string.card_header_author),
|
||||
card.author_url,
|
||||
card.author_name
|
||||
)
|
||||
addLinkAndCaption(
|
||||
sb,
|
||||
activity.getString(R.string.card_header_provider),
|
||||
card.provider_url,
|
||||
card.provider_name
|
||||
)
|
||||
|
||||
val description = card.description
|
||||
if(description != null && description.isNotEmpty()) {
|
||||
|
@ -907,21 +1075,35 @@ internal class ItemViewHolder(
|
|||
|
||||
val image = card.image
|
||||
if(image != null && image.isNotEmpty()) {
|
||||
lp = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, activity.app_state.media_thumb_height)
|
||||
lp = LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
activity.app_state.media_thumb_height
|
||||
)
|
||||
lp.topMargin = (0.5f + llExtra.resources.displayMetrics.density * 3f).toInt()
|
||||
val iv = MyNetworkImageView(activity)
|
||||
iv.layoutParams = lp
|
||||
//
|
||||
iv.id = R.id.ivCardThumbnail
|
||||
iv.setOnClickListener(this)
|
||||
iv.scaleType = if(activity.dont_crop_media_thumbnail) ImageView.ScaleType.FIT_CENTER else ImageView.ScaleType.CENTER_CROP
|
||||
iv.setImageUrl(activity.pref, 0f, access_info.supplyBaseUrl(image), access_info.supplyBaseUrl(image))
|
||||
iv.scaleType =
|
||||
if(activity.dont_crop_media_thumbnail) ImageView.ScaleType.FIT_CENTER else ImageView.ScaleType.CENTER_CROP
|
||||
iv.setImageUrl(
|
||||
activity.pref,
|
||||
0f,
|
||||
access_info.supplyBaseUrl(image),
|
||||
access_info.supplyBaseUrl(image)
|
||||
)
|
||||
|
||||
llExtra.addView(iv)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addLinkAndCaption(sb : StringBuilder, header : String, url : String?, caption : String?) {
|
||||
private fun addLinkAndCaption(
|
||||
sb : StringBuilder,
|
||||
header : String,
|
||||
url : String?,
|
||||
caption : String?
|
||||
) {
|
||||
|
||||
if(url.isNullOrEmpty() && caption.isNullOrEmpty()) return
|
||||
|
||||
|
@ -932,13 +1114,15 @@ internal class ItemViewHolder(
|
|||
if(url != null && url.isNotEmpty()) {
|
||||
sb.append("<a href=\"").append(HTMLDecoder.encodeEntity(url)).append("\">")
|
||||
}
|
||||
sb.append(HTMLDecoder.encodeEntity(
|
||||
when {
|
||||
caption != null && caption.isNotEmpty() -> caption
|
||||
url != null && url.isNotEmpty() -> url
|
||||
else -> "???"
|
||||
}
|
||||
))
|
||||
sb.append(
|
||||
HTMLDecoder.encodeEntity(
|
||||
when {
|
||||
caption != null && caption.isNotEmpty() -> caption
|
||||
url != null && url.isNotEmpty() -> url
|
||||
else -> "???"
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
if(url != null && url.isNotEmpty()) {
|
||||
sb.append("</a>")
|
||||
|
@ -955,7 +1139,10 @@ internal class ItemViewHolder(
|
|||
|
||||
val remain = enquete.time_start + NicoEnquete.ENQUETE_EXPIRE - now
|
||||
|
||||
val lp = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)
|
||||
val lp = LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
if(i == 0)
|
||||
lp.topMargin = (0.5f + llExtra.resources.displayMetrics.density * 3f).toInt()
|
||||
val b = Button(activity)
|
||||
|
@ -981,7 +1168,8 @@ internal class ItemViewHolder(
|
|||
val density = llExtra.resources.displayMetrics.density
|
||||
val height = (0.5f + 6 * density).toInt()
|
||||
val view = EnqueteTimerView(activity)
|
||||
view.layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, height)
|
||||
view.layoutParams =
|
||||
LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, height)
|
||||
view.setParams(enquete.time_start, NicoEnquete.ENQUETE_EXPIRE)
|
||||
llExtra.addView(view)
|
||||
}
|
||||
|
@ -1134,7 +1322,8 @@ internal class ItemViewHolder(
|
|||
}
|
||||
|
||||
btnFollow = imageButton {
|
||||
background = ContextCompat.getDrawable(context, R.drawable.btn_bg_transparent)
|
||||
background =
|
||||
ContextCompat.getDrawable(context, R.drawable.btn_bg_transparent)
|
||||
contentDescription = context.getString(R.string.follow)
|
||||
scaleType = ImageView.ScaleType.CENTER
|
||||
// tools:src="?attr/ic_follow_plus"
|
||||
|
@ -1181,7 +1370,8 @@ internal class ItemViewHolder(
|
|||
lparams(matchParent, wrapContent)
|
||||
|
||||
ivThumbnail = myNetworkImageView {
|
||||
background = ContextCompat.getDrawable(context, R.drawable.btn_bg_transparent)
|
||||
background =
|
||||
ContextCompat.getDrawable(context, R.drawable.btn_bg_transparent)
|
||||
contentDescription = context.getString(R.string.thumbnail)
|
||||
scaleType = ImageView.ScaleType.CENTER_CROP
|
||||
}.lparams(dip(48), dip(48)) {
|
||||
|
@ -1208,7 +1398,8 @@ internal class ItemViewHolder(
|
|||
|
||||
btnContentWarning = button {
|
||||
|
||||
background = ContextCompat.getDrawable(context, R.drawable.btn_bg_ddd)
|
||||
background =
|
||||
ContextCompat.getDrawable(context, R.drawable.btn_bg_ddd)
|
||||
minWidthCompat = dip(40)
|
||||
padding = dip(4)
|
||||
//tools:text="見る"
|
||||
|
@ -1253,7 +1444,10 @@ internal class ItemViewHolder(
|
|||
|
||||
ivMedia1 = myNetworkImageView {
|
||||
|
||||
background = ContextCompat.getDrawable(context, R.drawable.btn_bg_ddd)
|
||||
background = ContextCompat.getDrawable(
|
||||
context,
|
||||
R.drawable.btn_bg_ddd
|
||||
)
|
||||
contentDescription = context.getString(R.string.thumbnail)
|
||||
scaleType = ImageView.ScaleType.CENTER_CROP
|
||||
|
||||
|
@ -1263,7 +1457,10 @@ internal class ItemViewHolder(
|
|||
|
||||
ivMedia2 = myNetworkImageView {
|
||||
|
||||
background = ContextCompat.getDrawable(context, R.drawable.btn_bg_ddd)
|
||||
background = ContextCompat.getDrawable(
|
||||
context,
|
||||
R.drawable.btn_bg_ddd
|
||||
)
|
||||
contentDescription = context.getString(R.string.thumbnail)
|
||||
scaleType = ImageView.ScaleType.CENTER_CROP
|
||||
|
||||
|
@ -1274,7 +1471,10 @@ internal class ItemViewHolder(
|
|||
|
||||
ivMedia3 = myNetworkImageView {
|
||||
|
||||
background = ContextCompat.getDrawable(context, R.drawable.btn_bg_ddd)
|
||||
background = ContextCompat.getDrawable(
|
||||
context,
|
||||
R.drawable.btn_bg_ddd
|
||||
)
|
||||
contentDescription = context.getString(R.string.thumbnail)
|
||||
scaleType = ImageView.ScaleType.CENTER_CROP
|
||||
|
||||
|
@ -1285,7 +1485,10 @@ internal class ItemViewHolder(
|
|||
|
||||
ivMedia4 = myNetworkImageView {
|
||||
|
||||
background = ContextCompat.getDrawable(context, R.drawable.btn_bg_ddd)
|
||||
background = ContextCompat.getDrawable(
|
||||
context,
|
||||
R.drawable.btn_bg_ddd
|
||||
)
|
||||
contentDescription = context.getString(R.string.thumbnail)
|
||||
scaleType = ImageView.ScaleType.CENTER_CROP
|
||||
|
||||
|
@ -1296,9 +1499,13 @@ internal class ItemViewHolder(
|
|||
|
||||
btnHideMedia = imageButton {
|
||||
|
||||
background = ContextCompat.getDrawable(context, R.drawable.btn_bg_transparent)
|
||||
background = ContextCompat.getDrawable(
|
||||
context,
|
||||
R.drawable.btn_bg_transparent
|
||||
)
|
||||
contentDescription = "@string/hide"
|
||||
imageResource = Styler.getAttributeResourceId(context, R.attr.btn_close)
|
||||
imageResource =
|
||||
Styler.getAttributeResourceId(context, R.attr.btn_close)
|
||||
}.lparams(dip(32), matchParent) {
|
||||
startMargin = dip(8)
|
||||
}
|
||||
|
@ -1306,10 +1513,14 @@ internal class ItemViewHolder(
|
|||
|
||||
btnShowMedia = textView {
|
||||
|
||||
backgroundColor = Styler.getAttributeColor(context, R.attr.colorShowMediaBackground)
|
||||
backgroundColor = Styler.getAttributeColor(
|
||||
context,
|
||||
R.attr.colorShowMediaBackground
|
||||
)
|
||||
gravity = Gravity.CENTER
|
||||
text = context.getString(R.string.tap_to_show)
|
||||
textColor = Styler.getAttributeColor(context, R.attr.colorShowMediaText)
|
||||
textColor =
|
||||
Styler.getAttributeColor(context, R.attr.colorShowMediaText)
|
||||
|
||||
}.lparams(matchParent, matchParent)
|
||||
}
|
||||
|
@ -1330,18 +1541,26 @@ internal class ItemViewHolder(
|
|||
|
||||
btnConversation = imageButton {
|
||||
|
||||
background = ContextCompat.getDrawable(context, R.drawable.btn_bg_transparent)
|
||||
background = ContextCompat.getDrawable(
|
||||
context,
|
||||
R.drawable.btn_bg_transparent
|
||||
)
|
||||
contentDescription = context.getString(R.string.conversation_view)
|
||||
minimumWidth = dip(40)
|
||||
imageResource = Styler.getAttributeResourceId(context, R.attr.ic_conversation)
|
||||
imageResource =
|
||||
Styler.getAttributeResourceId(context, R.attr.ic_conversation)
|
||||
}.lparams(wrapContent, matchParent)
|
||||
|
||||
btnReply = imageButton {
|
||||
|
||||
background = ContextCompat.getDrawable(context, R.drawable.btn_bg_transparent)
|
||||
background = ContextCompat.getDrawable(
|
||||
context,
|
||||
R.drawable.btn_bg_transparent
|
||||
)
|
||||
contentDescription = context.getString(R.string.reply)
|
||||
minimumWidth = dip(40)
|
||||
imageResource = Styler.getAttributeResourceId(context, R.attr.btn_reply)
|
||||
imageResource =
|
||||
Styler.getAttributeResourceId(context, R.attr.btn_reply)
|
||||
|
||||
}.lparams(wrapContent, matchParent) {
|
||||
startMargin = dip(2)
|
||||
|
@ -1349,7 +1568,10 @@ internal class ItemViewHolder(
|
|||
|
||||
btnBoost = button {
|
||||
|
||||
background = ContextCompat.getDrawable(context, R.drawable.btn_bg_transparent)
|
||||
background = ContextCompat.getDrawable(
|
||||
context,
|
||||
R.drawable.btn_bg_transparent
|
||||
)
|
||||
compoundDrawablePadding = dip(4)
|
||||
|
||||
minWidthCompat = dip(48)
|
||||
|
@ -1359,7 +1581,10 @@ internal class ItemViewHolder(
|
|||
}
|
||||
|
||||
btnFavourite = button {
|
||||
background = ContextCompat.getDrawable(context, R.drawable.btn_bg_transparent)
|
||||
background = ContextCompat.getDrawable(
|
||||
context,
|
||||
R.drawable.btn_bg_transparent
|
||||
)
|
||||
compoundDrawablePadding = dip(4)
|
||||
minWidthCompat = dip(48)
|
||||
setPaddingStartEnd(dip(4), dip(4))
|
||||
|
@ -1375,7 +1600,10 @@ internal class ItemViewHolder(
|
|||
|
||||
btnFollow2 = imageButton {
|
||||
|
||||
background = ContextCompat.getDrawable(context, R.drawable.btn_bg_transparent)
|
||||
background = ContextCompat.getDrawable(
|
||||
context,
|
||||
R.drawable.btn_bg_transparent
|
||||
)
|
||||
contentDescription = context.getString(R.string.follow)
|
||||
scaleType = ImageView.ScaleType.CENTER
|
||||
// tools:src="?attr/ic_follow_plus"
|
||||
|
@ -1386,15 +1614,22 @@ internal class ItemViewHolder(
|
|||
ivFollowedBy2 = imageView {
|
||||
|
||||
scaleType = ImageView.ScaleType.CENTER
|
||||
imageResource = Styler.getAttributeResourceId(context, R.attr.ic_followed_by)
|
||||
imageResource = Styler.getAttributeResourceId(
|
||||
context,
|
||||
R.attr.ic_followed_by
|
||||
)
|
||||
importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
|
||||
}.lparams(matchParent, matchParent)
|
||||
}
|
||||
|
||||
btnMore = imageButton {
|
||||
background = ContextCompat.getDrawable(context, R.drawable.btn_bg_transparent)
|
||||
background = ContextCompat.getDrawable(
|
||||
context,
|
||||
R.drawable.btn_bg_transparent
|
||||
)
|
||||
contentDescription = context.getString(R.string.more)
|
||||
imageResource = Styler.getAttributeResourceId(context, R.attr.btn_more)
|
||||
imageResource =
|
||||
Styler.getAttributeResourceId(context, R.attr.btn_more)
|
||||
minimumWidth = dip(40)
|
||||
}.lparams(wrapContent, matchParent) {
|
||||
startMargin = dip(2)
|
||||
|
|
|
@ -39,7 +39,8 @@ object Pref {
|
|||
|
||||
}
|
||||
|
||||
class BooleanPref(key : String, private val defVal : Boolean, val id : Int) : BasePref<Boolean>(key) {
|
||||
class BooleanPref(key : String, private val defVal : Boolean, val id : Int) :
|
||||
BasePref<Boolean>(key) {
|
||||
|
||||
override operator fun invoke(pref : SharedPreferences) : Boolean {
|
||||
return pref.getBoolean(key, defVal)
|
||||
|
@ -83,7 +84,8 @@ object Pref {
|
|||
}
|
||||
}
|
||||
|
||||
class StringPref(key : String, private val defVal : String, val skipImport : Boolean = false) : BasePref<String>(key) {
|
||||
class StringPref(key : String, private val defVal : String, val skipImport : Boolean = false) :
|
||||
BasePref<String>(key) {
|
||||
|
||||
override operator fun invoke(pref : SharedPreferences) : String {
|
||||
return pref.getString(key, defVal)
|
||||
|
@ -96,34 +98,175 @@ object Pref {
|
|||
|
||||
// boolean
|
||||
|
||||
val bpAllowNonSpaceBeforeEmojiShortcode = BooleanPref("allow_non_space_before_emoji_shortcode", false, R.id.swAllowNonSpaceBeforeEmojiShortcode)
|
||||
val bpDisableEmojiAnimation = BooleanPref("disable_emoji_animation", false, R.id.swDisableEmojiAnimation)
|
||||
val bpDisableFastScroller = BooleanPref("disable_fast_scroller", true, R.id.swDisableFastScroller)
|
||||
val bpDisableTabletMode = BooleanPref("disable_tablet_mode", false, R.id.swDisableTabletMode)
|
||||
val bpDontConfirmBeforeCloseColumn = BooleanPref("DontConfirmBeforeCloseColumn", false, R.id.swDontConfirmBeforeCloseColumn)
|
||||
val bpDontCropMediaThumb = BooleanPref("DontCropMediaThumb", false, R.id.swDontCropMediaThumb)
|
||||
val bpDontDuplicationCheck = BooleanPref("dont_duplication_check", false, R.id.swDontDuplicationCheck)
|
||||
val bpDontRefreshOnResume = BooleanPref("dont_refresh_on_resume", false, R.id.swDontRefreshOnResume)
|
||||
val bpDontRound = BooleanPref("dont_round", false, R.id.swDontRound)
|
||||
val bpDontScreenOff = BooleanPref("dont_screen_off", false, R.id.swDontScreenOff)
|
||||
val bpDontUseActionButtonWithQuickTootBar = BooleanPref("dont_use_action_button", false, R.id.swDontUseActionButtonWithQuickTootBar)
|
||||
val bpDontUseStreaming = BooleanPref("dont_use_streaming", false, R.id.swDontUseStreaming)
|
||||
val bpEnableGifAnimation = BooleanPref("enable_gif_animation", false, R.id.swEnableGifAnimation)
|
||||
val bpExitAppWhenCloseProtectedColumn = BooleanPref("ExitAppWhenCloseProtectedColumn", false, R.id.swExitAppWhenCloseProtectedColumn)
|
||||
val bpMentionFullAcct = BooleanPref("mention_full_acct", false, R.id.swMentionFullAcct)
|
||||
val bpNotificationLED = BooleanPref("notification_led", true, R.id.cbNotificationLED)
|
||||
val bpNotificationSound = BooleanPref("notification_sound", true, R.id.cbNotificationSound)
|
||||
val bpNotificationVibration = BooleanPref("notification_vibration", true, R.id.cbNotificationVibration)
|
||||
val bpPostButtonBarTop = BooleanPref("post_button_bar_at_top", true, R.id.swPostButtonBarTop)
|
||||
val bpPriorChrome = BooleanPref("prior_chrome", true, R.id.swPriorChrome)
|
||||
val bpPriorLocalURL = BooleanPref("prior_local_url", false, R.id.swPriorLocalURL)
|
||||
val bpQuickTootBar = BooleanPref("quick_toot_bar", false, R.id.swQuickTootBar)
|
||||
val bpRelativeTimestamp = BooleanPref("relative_timestamp", true, R.id.swRelativeTimestamp)
|
||||
val bpShortAcctLocalUser = BooleanPref("short_acct_local_user", true, R.id.swShortAcctLocalUser)
|
||||
val bpShowFollowButtonInButtonBar = BooleanPref("ShowFollowButtonInButtonBar", false, R.id.swShowFollowButtonInButtonBar)
|
||||
val bpSimpleList = BooleanPref("simple_list", true, R.id.swSimpleList)
|
||||
val bpUseInternalMediaViewer = BooleanPref("use_internal_media_viewer", true, R.id.swUseInternalMediaViewer)
|
||||
val bpShowAppName = BooleanPref("show_app_name", false, R.id.swShowAppName)
|
||||
val bpAllowNonSpaceBeforeEmojiShortcode = BooleanPref(
|
||||
"allow_non_space_before_emoji_shortcode",
|
||||
false,
|
||||
R.id.swAllowNonSpaceBeforeEmojiShortcode
|
||||
)
|
||||
|
||||
val bpDisableEmojiAnimation = BooleanPref(
|
||||
"disable_emoji_animation",
|
||||
false,
|
||||
R.id.swDisableEmojiAnimation
|
||||
)
|
||||
|
||||
// val bpDisableFastScroller = BooleanPref("disable_fast_scroller", true, 0) // R.id.swDisableFastScroller)
|
||||
|
||||
val bpDisableTabletMode = BooleanPref(
|
||||
"disable_tablet_mode",
|
||||
false,
|
||||
R.id.swDisableTabletMode
|
||||
)
|
||||
|
||||
val bpDontConfirmBeforeCloseColumn = BooleanPref(
|
||||
"DontConfirmBeforeCloseColumn",
|
||||
false,
|
||||
R.id.swDontConfirmBeforeCloseColumn
|
||||
)
|
||||
|
||||
val bpDontCropMediaThumb = BooleanPref(
|
||||
"DontCropMediaThumb",
|
||||
false,
|
||||
R.id.swDontCropMediaThumb
|
||||
)
|
||||
|
||||
val bpDontDuplicationCheck = BooleanPref(
|
||||
"dont_duplication_check",
|
||||
false,
|
||||
R.id.swDontDuplicationCheck
|
||||
)
|
||||
|
||||
val bpDontRefreshOnResume = BooleanPref(
|
||||
"dont_refresh_on_resume",
|
||||
false,
|
||||
R.id.swDontRefreshOnResume
|
||||
)
|
||||
|
||||
val bpDontRound = BooleanPref(
|
||||
"dont_round",
|
||||
false,
|
||||
R.id.swDontRound
|
||||
)
|
||||
|
||||
val bpDontScreenOff = BooleanPref(
|
||||
"dont_screen_off",
|
||||
false,
|
||||
R.id.swDontScreenOff
|
||||
)
|
||||
|
||||
val bpDontUseActionButtonWithQuickTootBar = BooleanPref(
|
||||
"dont_use_action_button",
|
||||
false,
|
||||
R.id.swDontUseActionButtonWithQuickTootBar
|
||||
)
|
||||
|
||||
val bpDontUseStreaming = BooleanPref(
|
||||
"dont_use_streaming",
|
||||
false,
|
||||
R.id.swDontUseStreaming
|
||||
)
|
||||
|
||||
val bpEnableGifAnimation = BooleanPref(
|
||||
"enable_gif_animation",
|
||||
false,
|
||||
R.id.swEnableGifAnimation
|
||||
)
|
||||
|
||||
val bpExitAppWhenCloseProtectedColumn = BooleanPref(
|
||||
"ExitAppWhenCloseProtectedColumn",
|
||||
false,
|
||||
R.id.swExitAppWhenCloseProtectedColumn
|
||||
)
|
||||
|
||||
val bpMentionFullAcct = BooleanPref(
|
||||
"mention_full_acct",
|
||||
false,
|
||||
R.id.swMentionFullAcct
|
||||
)
|
||||
|
||||
val bpNotificationLED = BooleanPref(
|
||||
"notification_led",
|
||||
true,
|
||||
R.id.cbNotificationLED
|
||||
)
|
||||
|
||||
val bpNotificationSound = BooleanPref(
|
||||
"notification_sound",
|
||||
true,
|
||||
R.id.cbNotificationSound
|
||||
)
|
||||
|
||||
val bpNotificationVibration = BooleanPref(
|
||||
"notification_vibration",
|
||||
true,
|
||||
R.id.cbNotificationVibration
|
||||
)
|
||||
|
||||
val bpPostButtonBarTop = BooleanPref(
|
||||
"post_button_bar_at_top",
|
||||
true,
|
||||
R.id.swPostButtonBarTop
|
||||
)
|
||||
|
||||
val bpPriorChrome = BooleanPref(
|
||||
"prior_chrome",
|
||||
true,
|
||||
R.id.swPriorChrome
|
||||
)
|
||||
|
||||
val bpPriorLocalURL = BooleanPref(
|
||||
"prior_local_url",
|
||||
false,
|
||||
R.id.swPriorLocalURL
|
||||
)
|
||||
|
||||
val bpQuickTootBar = BooleanPref(
|
||||
"quick_toot_bar",
|
||||
false,
|
||||
R.id.swQuickTootBar
|
||||
)
|
||||
|
||||
val bpRelativeTimestamp = BooleanPref(
|
||||
"relative_timestamp",
|
||||
true,
|
||||
R.id.swRelativeTimestamp
|
||||
)
|
||||
|
||||
val bpShortAcctLocalUser = BooleanPref(
|
||||
"short_acct_local_user",
|
||||
true,
|
||||
R.id.swShortAcctLocalUser
|
||||
)
|
||||
|
||||
val bpShowFollowButtonInButtonBar = BooleanPref(
|
||||
"ShowFollowButtonInButtonBar",
|
||||
false,
|
||||
R.id.swShowFollowButtonInButtonBar
|
||||
)
|
||||
|
||||
val bpSimpleList = BooleanPref(
|
||||
"simple_list",
|
||||
true,
|
||||
R.id.swSimpleList
|
||||
)
|
||||
|
||||
val bpUseInternalMediaViewer = BooleanPref(
|
||||
"use_internal_media_viewer",
|
||||
true,
|
||||
R.id.swUseInternalMediaViewer
|
||||
)
|
||||
|
||||
val bpShowAppName = BooleanPref(
|
||||
"show_app_name",
|
||||
false,
|
||||
R.id.swShowAppName
|
||||
)
|
||||
|
||||
val bpForceGap = BooleanPref(
|
||||
"force_gap",
|
||||
false,
|
||||
R.id.swForceGap
|
||||
)
|
||||
|
||||
// int
|
||||
|
||||
|
|
|
@ -68,32 +68,69 @@ internal class StatusButtons(
|
|||
|
||||
val color_normal = Styler.getAttributeColor(activity, R.attr.colorImageButton)
|
||||
val color_accent = Styler.getAttributeColor(activity, R.attr.colorImageButtonAccent)
|
||||
val fav_icon_attr = if(access_info.isNicoru(status.account)) R.attr.ic_nicoru else R.attr.btn_favourite
|
||||
val fav_icon_attr =
|
||||
if(access_info.isNicoru(status.account)) R.attr.ic_nicoru else R.attr.btn_favourite
|
||||
|
||||
// ブーストボタン
|
||||
when {
|
||||
TootStatus.VISIBILITY_DIRECT == status.visibility -> setButton(btnBoost, false, color_accent, R.attr.ic_mail, "")
|
||||
TootStatus.VISIBILITY_PRIVATE == status.visibility -> setButton(btnBoost, false, color_accent, R.attr.ic_lock, "")
|
||||
activity.app_state.isBusyBoost(access_info, status) -> setButton(btnBoost, false, color_normal, R.attr.btn_refresh, "?")
|
||||
TootStatus.VISIBILITY_DIRECT == status.visibility -> setButton(
|
||||
btnBoost,
|
||||
false,
|
||||
color_accent,
|
||||
R.attr.ic_mail,
|
||||
""
|
||||
)
|
||||
TootStatus.VISIBILITY_PRIVATE == status.visibility -> setButton(
|
||||
btnBoost,
|
||||
false,
|
||||
color_accent,
|
||||
R.attr.ic_lock,
|
||||
""
|
||||
)
|
||||
activity.app_state.isBusyBoost(access_info, status) -> setButton(
|
||||
btnBoost,
|
||||
false,
|
||||
color_normal,
|
||||
R.attr.btn_refresh,
|
||||
"?"
|
||||
)
|
||||
|
||||
else -> {
|
||||
val color = if(status.reblogged) color_accent else color_normal
|
||||
setButton(btnBoost, true, color, R.attr.btn_boost, status.reblogs_count?.toString() ?: "")
|
||||
setButton(
|
||||
btnBoost,
|
||||
true,
|
||||
color,
|
||||
R.attr.btn_boost,
|
||||
status.reblogs_count?.toString() ?: ""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
when {
|
||||
activity.app_state.isBusyFav(access_info, status) -> setButton(btnFavourite, false, color_normal, R.attr.btn_refresh, "?")
|
||||
activity.app_state.isBusyFav(access_info, status) -> setButton(
|
||||
btnFavourite,
|
||||
false,
|
||||
color_normal,
|
||||
R.attr.btn_refresh,
|
||||
"?"
|
||||
)
|
||||
|
||||
else -> {
|
||||
val color = if(status.favourited) color_accent else color_normal
|
||||
setButton(btnFavourite, true, color, fav_icon_attr, status.favourites_count?.toString() ?: "")
|
||||
setButton(
|
||||
btnFavourite,
|
||||
true,
|
||||
color,
|
||||
fav_icon_attr,
|
||||
status.favourites_count?.toString() ?: ""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val account = status.account
|
||||
|
||||
this.relation = if( ! Pref.bpShowFollowButtonInButtonBar(activity.pref)) {
|
||||
this.relation = if(! Pref.bpShowFollowButtonInButtonBar(activity.pref)) {
|
||||
llFollow2.visibility = View.GONE
|
||||
null
|
||||
} else {
|
||||
|
@ -105,7 +142,13 @@ internal class StatusButtons(
|
|||
|
||||
}
|
||||
|
||||
private fun setButton(b : Button, enabled : Boolean, color : Int, icon_attr : Int, text : String) {
|
||||
private fun setButton(
|
||||
b : Button,
|
||||
enabled : Boolean,
|
||||
color : Int,
|
||||
icon_attr : Int,
|
||||
text : String
|
||||
) {
|
||||
val d = Styler.getAttributeDrawable(activity, icon_attr).mutate()
|
||||
d.setColorFilter(color, PorterDuff.Mode.SRC_ATOP)
|
||||
b.setCompoundDrawablesRelativeWithIntrinsicBounds(d, null, null, null)
|
||||
|
@ -123,9 +166,14 @@ internal class StatusButtons(
|
|||
|
||||
when(v) {
|
||||
|
||||
btnConversation -> Action_Toot.conversation(activity, activity.nextPosition(column), access_info, status)
|
||||
btnConversation -> Action_Toot.conversation(
|
||||
activity,
|
||||
activity.nextPosition(column),
|
||||
access_info,
|
||||
status
|
||||
)
|
||||
|
||||
btnReply -> if( ! access_info.isPseudo) {
|
||||
btnReply -> if(! access_info.isPseudo) {
|
||||
Action_Toot.reply(activity, access_info, status)
|
||||
} else {
|
||||
Action_Toot.replyFromAnotherAccount(activity, access_info, status)
|
||||
|
@ -222,8 +270,8 @@ internal class StatusButtons(
|
|||
activity.nextPosition(column),
|
||||
access_info,
|
||||
account,
|
||||
bFollow =true,
|
||||
callback =activity.follow_complete_callback
|
||||
bFollow = true,
|
||||
callback = activity.follow_complete_callback
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -242,19 +290,24 @@ internal class StatusButtons(
|
|||
|
||||
when(v) {
|
||||
btnConversation -> Action_Toot.conversationOtherInstance(
|
||||
activity, activity.nextPosition(column), status)
|
||||
|
||||
activity, activity.nextPosition(column), status
|
||||
)
|
||||
|
||||
btnBoost -> Action_Toot.boostFromAnotherAccount(
|
||||
activity, access_info, status)
|
||||
|
||||
activity, access_info, status
|
||||
)
|
||||
|
||||
btnFavourite -> Action_Toot.favouriteFromAnotherAccount(
|
||||
activity, access_info, status)
|
||||
|
||||
activity, access_info, status
|
||||
)
|
||||
|
||||
btnReply -> Action_Toot.replyFromAnotherAccount(
|
||||
activity, access_info, status)
|
||||
|
||||
activity, access_info, status
|
||||
)
|
||||
|
||||
btnFollow2 -> Action_Follow.followFromAnotherAccount(
|
||||
activity, activity.nextPosition(column), access_info, status.account)
|
||||
activity, activity.nextPosition(column), access_info, status.account
|
||||
)
|
||||
|
||||
}
|
||||
return true
|
||||
|
|
|
@ -12,7 +12,7 @@ import android.widget.PopupWindow
|
|||
import jp.juggler.subwaytooter.api.entity.TootNotification
|
||||
import jp.juggler.subwaytooter.api.entity.TootStatus
|
||||
import jp.juggler.subwaytooter.util.LogCategory
|
||||
import jp.juggler.subwaytooter.view.MyListView
|
||||
import android.support.v7.widget.ListRecyclerView
|
||||
|
||||
class StatusButtonsPopup(
|
||||
private val activity : ActMain,
|
||||
|
@ -29,6 +29,9 @@ class StatusButtonsPopup(
|
|||
v.measure(spec, spec)
|
||||
return v.measuredWidth
|
||||
}
|
||||
|
||||
var last_popup_close = 0L
|
||||
|
||||
}
|
||||
|
||||
private val viewRoot : View
|
||||
|
@ -42,7 +45,7 @@ class StatusButtonsPopup(
|
|||
activity,
|
||||
column,
|
||||
bSimpleList,
|
||||
btnConversation =viewRoot.findViewById(R.id.btnConversation),
|
||||
btnConversation = viewRoot.findViewById(R.id.btnConversation),
|
||||
btnReply = viewRoot.findViewById(R.id.btnReply),
|
||||
btnBoost = viewRoot.findViewById(R.id.btnBoost),
|
||||
btnFavourite = viewRoot.findViewById(R.id.btnFavourite),
|
||||
|
@ -62,7 +65,7 @@ class StatusButtonsPopup(
|
|||
|
||||
@SuppressLint("RtlHardcoded")
|
||||
fun show(
|
||||
listView : MyListView
|
||||
listView : ListRecyclerView
|
||||
, anchor : View
|
||||
, status : TootStatus
|
||||
, notification : TootNotification?
|
||||
|
@ -82,7 +85,7 @@ class StatusButtonsPopup(
|
|||
// ポップアップの外側をタッチしたらポップアップを閉じる
|
||||
// また、そのタッチイベントがlistViewに影響しないようにする
|
||||
window.dismiss()
|
||||
listView.last_popup_close = SystemClock.elapsedRealtime()
|
||||
last_popup_close = SystemClock.elapsedRealtime()
|
||||
return@OnTouchListener true
|
||||
}
|
||||
false
|
||||
|
|
|
@ -22,7 +22,6 @@ import jp.juggler.subwaytooter.table.SavedAccount
|
|||
import jp.juggler.subwaytooter.util.LogCategory
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.util.WordTrieTree
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import okhttp3.WebSocket
|
||||
import okhttp3.WebSocketListener
|
||||
|
@ -51,11 +50,11 @@ internal class StreamReader(
|
|||
internal val bDisposed = AtomicBoolean()
|
||||
internal val bListening = AtomicBoolean()
|
||||
internal val socket = AtomicReference<WebSocket>(null)
|
||||
internal val callback_list = LinkedList< (event_type : String, item : Any?)->Unit >()
|
||||
internal val callback_list = LinkedList<(event_type : String, item : Any?) -> Unit>()
|
||||
internal val parser : TootParser
|
||||
|
||||
init {
|
||||
this.parser = TootParser(context, access_info,highlightTrie = highlight_trie)
|
||||
this.parser = TootParser(context, access_info, highlightTrie = highlight_trie)
|
||||
}
|
||||
|
||||
internal fun dispose() {
|
||||
|
@ -68,18 +67,21 @@ internal class StreamReader(
|
|||
startRead()
|
||||
}
|
||||
|
||||
@Synchronized internal fun setHighlightTrie(highlight_trie : WordTrieTree) {
|
||||
@Synchronized
|
||||
internal fun setHighlightTrie(highlight_trie : WordTrieTree) {
|
||||
this.parser.highlightTrie = highlight_trie
|
||||
}
|
||||
|
||||
@Synchronized internal fun addCallback(stream_callback : (event_type : String, item : Any?)->Unit ) {
|
||||
@Synchronized
|
||||
internal fun addCallback(stream_callback : (event_type : String, item : Any?) -> Unit) {
|
||||
for(c in callback_list) {
|
||||
if(c === stream_callback) return
|
||||
}
|
||||
callback_list.add(stream_callback)
|
||||
}
|
||||
|
||||
@Synchronized internal fun removeCallback(stream_callback : (event_type : String, item : Any?)->Unit) {
|
||||
@Synchronized
|
||||
internal fun removeCallback(stream_callback : (event_type : String, item : Any?) -> Unit) {
|
||||
val it = callback_list.iterator()
|
||||
while(it.hasNext()) {
|
||||
val c = it.next()
|
||||
|
@ -136,7 +138,12 @@ internal class StreamReader(
|
|||
* Invoked when the peer has indicated that no more incoming messages will be transmitted.
|
||||
*/
|
||||
override fun onClosing(webSocket : WebSocket, code : Int, reason : String?) {
|
||||
log.d("WebSocket onClosing. code=%s,reason=%s,url=%s .", code, reason, webSocket .request().url())
|
||||
log.d(
|
||||
"WebSocket onClosing. code=%s,reason=%s,url=%s .",
|
||||
code,
|
||||
reason,
|
||||
webSocket.request().url()
|
||||
)
|
||||
webSocket.cancel()
|
||||
bListening.set(false)
|
||||
handler.removeCallbacks(proc_reconnect)
|
||||
|
@ -148,7 +155,12 @@ internal class StreamReader(
|
|||
* connection has been successfully released. No further calls to this listener will be made.
|
||||
*/
|
||||
override fun onClosed(webSocket : WebSocket, code : Int, reason : String?) {
|
||||
log.d("WebSocket onClosed. code=%s,reason=%s,url=%s .", code, reason, webSocket .request().url())
|
||||
log.d(
|
||||
"WebSocket onClosed. code=%s,reason=%s,url=%s .",
|
||||
code,
|
||||
reason,
|
||||
webSocket.request().url()
|
||||
)
|
||||
bListening.set(false)
|
||||
handler.removeCallbacks(proc_reconnect)
|
||||
handler.postDelayed(proc_reconnect, 10000L)
|
||||
|
@ -160,7 +172,7 @@ internal class StreamReader(
|
|||
* listener will be made.
|
||||
*/
|
||||
override fun onFailure(webSocket : WebSocket, ex : Throwable, response : Response?) {
|
||||
log.e(ex , "WebSocket onFailure. url=%s .", webSocket .request().url())
|
||||
log.e(ex, "WebSocket onFailure. url=%s .", webSocket.request().url())
|
||||
|
||||
bListening.set(false)
|
||||
handler.removeCallbacks(proc_reconnect)
|
||||
|
@ -187,11 +199,11 @@ internal class StreamReader(
|
|||
bListening.set(true)
|
||||
TootTaskRunner(context).run(access_info, object : TootTask {
|
||||
override fun background(client : TootApiClient) : TootApiResult? {
|
||||
val result = client.webSocket(end_point, this@Reader)
|
||||
val result = client.webSocket(end_point, this@Reader)
|
||||
if(result == null) {
|
||||
log.d("startRead: cancelled.")
|
||||
bListening.set(false)
|
||||
}else {
|
||||
} else {
|
||||
val ws = result.data as? WebSocket
|
||||
if(ws != null) {
|
||||
socket.set(ws)
|
||||
|
@ -202,6 +214,7 @@ internal class StreamReader(
|
|||
}
|
||||
return result
|
||||
}
|
||||
|
||||
override fun handleResult(result : TootApiResult?) {
|
||||
}
|
||||
})
|
||||
|
@ -234,7 +247,7 @@ internal class StreamReader(
|
|||
accessInfo : SavedAccount,
|
||||
endPoint : String,
|
||||
highlightTrie : WordTrieTree?,
|
||||
streamCallback : (event_type : String, item : Any?)->Unit
|
||||
streamCallback : (event_type : String, item : Any?) -> Unit
|
||||
) {
|
||||
|
||||
val reader = prepareReader(accessInfo, endPoint, highlightTrie)
|
||||
|
@ -245,12 +258,11 @@ internal class StreamReader(
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// カラム破棄やリロードのタイミングで呼ばれる
|
||||
fun unregister(
|
||||
accessInfo : SavedAccount,
|
||||
endPoint : String,
|
||||
streamCallback : (event_type : String, item : Any?)->Unit
|
||||
streamCallback : (event_type : String, item : Any?) -> Unit
|
||||
) {
|
||||
synchronized(reader_list) {
|
||||
val it = reader_list.iterator()
|
||||
|
|
|
@ -42,14 +42,26 @@ object Styler {
|
|||
val resourceId = a.getResourceId(0, 0)
|
||||
a.recycle()
|
||||
if(resourceId == 0)
|
||||
throw RuntimeException(String.format(Locale.JAPAN, "attr not defined.attr_id=0x%x", attrId))
|
||||
throw RuntimeException(
|
||||
String.format(
|
||||
Locale.JAPAN,
|
||||
"attr not defined.attr_id=0x%x",
|
||||
attrId
|
||||
)
|
||||
)
|
||||
return resourceId
|
||||
}
|
||||
|
||||
fun getAttributeDrawable(context : Context, attrId : Int) : Drawable {
|
||||
val drawableId = getAttributeResourceId(context, attrId)
|
||||
return ContextCompat.getDrawable(context, drawableId)
|
||||
?: throw RuntimeException(String.format(Locale.JAPAN, "getDrawable failed. drawableId=0x%x", drawableId))
|
||||
?: throw RuntimeException(
|
||||
String.format(
|
||||
Locale.JAPAN,
|
||||
"getDrawable failed. drawableId=0x%x",
|
||||
drawableId
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// ImageViewにアイコンを設定する
|
||||
|
@ -58,16 +70,21 @@ object Styler {
|
|||
}
|
||||
|
||||
// ImageViewにアイコンを設定する。色を変えてしまう
|
||||
fun setIconCustomColor(context : Context, imageView : ImageView, color : Int, iconAttrId : Int) {
|
||||
fun setIconCustomColor(
|
||||
context : Context,
|
||||
imageView : ImageView,
|
||||
color : Int,
|
||||
iconAttrId : Int
|
||||
) {
|
||||
val d = getAttributeDrawable(context, iconAttrId)
|
||||
d.mutate() // 色指定が他のアイコンに影響しないようにする
|
||||
d.setColorFilter(color, PorterDuff.Mode.SRC_ATOP)
|
||||
imageView.setImageDrawable(d)
|
||||
}
|
||||
|
||||
fun getVisibilityIconAttr( visibility : String?) : Int {
|
||||
fun getVisibilityIconAttr(visibility : String?) : Int {
|
||||
return when(visibility) {
|
||||
null->R.attr.ic_public
|
||||
null -> R.attr.ic_public
|
||||
TootStatus.VISIBILITY_PUBLIC -> R.attr.ic_public
|
||||
TootStatus.VISIBILITY_UNLISTED -> R.attr.ic_lock_open
|
||||
TootStatus.VISIBILITY_PRIVATE -> R.attr.ic_lock
|
||||
|
|
|
@ -5,8 +5,8 @@ import android.view.View
|
|||
|
||||
import jp.juggler.subwaytooter.util.LogCategory
|
||||
|
||||
internal class TabletColumnViewHolder(activity : ActMain, viewRoot : View)
|
||||
: RecyclerView.ViewHolder(viewRoot) {
|
||||
internal class TabletColumnViewHolder(activity : ActMain, viewRoot : View) :
|
||||
RecyclerView.ViewHolder(viewRoot) {
|
||||
|
||||
companion object {
|
||||
val log = LogCategory("TabletColumnViewHolder")
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
package jp.juggler.subwaytooter
|
||||
|
||||
import android.support.v7.widget.RecyclerView
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import android.widget.TextView
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.subwaytooter.util.LogCategory
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
|
||||
internal abstract class ViewHolderHeaderBase(val activity : ActMain, val viewRoot : View) :
|
||||
RecyclerView.ViewHolder(viewRoot) {
|
||||
|
||||
companion object {
|
||||
private val log = LogCategory("HeaderViewHolderBase")
|
||||
}
|
||||
|
||||
internal lateinit var column : Column
|
||||
internal lateinit var access_info : SavedAccount
|
||||
|
||||
init {
|
||||
Utils.scanView(viewRoot) { v ->
|
||||
try {
|
||||
if(v is Button) {
|
||||
// ボタンは太字なので触らない
|
||||
} else if(v is TextView) {
|
||||
if(activity.timeline_font != null) {
|
||||
v.typeface = activity.timeline_font
|
||||
}
|
||||
if(! activity.timeline_font_size_sp.isNaN()) {
|
||||
v.textSize = activity.timeline_font_size_sp
|
||||
}
|
||||
}
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal open fun bindData(column : Column) {
|
||||
this.column = column
|
||||
this.access_info = column.access_info
|
||||
}
|
||||
|
||||
internal abstract fun showColor()
|
||||
|
||||
internal abstract fun onViewRecycled()
|
||||
}
|
|
@ -3,7 +3,6 @@ package jp.juggler.subwaytooter
|
|||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import android.widget.TextView
|
||||
|
||||
import jp.juggler.subwaytooter.api.entity.TootInstance
|
||||
|
@ -11,23 +10,18 @@ import jp.juggler.subwaytooter.util.DecodeOptions
|
|||
import jp.juggler.subwaytooter.util.LogCategory
|
||||
import jp.juggler.subwaytooter.util.Utils
|
||||
import jp.juggler.subwaytooter.view.MyLinkMovementMethod
|
||||
import jp.juggler.subwaytooter.view.MyListView
|
||||
import jp.juggler.subwaytooter.view.MyNetworkImageView
|
||||
|
||||
internal class HeaderViewHolderInstance(
|
||||
internal class ViewHolderHeaderInstance(
|
||||
arg_activity : ActMain,
|
||||
arg_column : Column,
|
||||
parent : MyListView
|
||||
) : HeaderViewHolderBase(
|
||||
arg_activity,
|
||||
arg_column,
|
||||
arg_activity.layoutInflater.inflate(R.layout.lv_header_instance, parent, false)
|
||||
) , View.OnClickListener {
|
||||
viewRoot : View
|
||||
) : ViewHolderHeaderBase(arg_activity, viewRoot)
|
||||
, View.OnClickListener {
|
||||
|
||||
companion object {
|
||||
private val log = LogCategory("HeaderViewHolderInstance")
|
||||
private val log = LogCategory("ViewHolderHeaderInstance")
|
||||
}
|
||||
|
||||
|
||||
private val btnInstance : TextView
|
||||
private val tvVersion : TextView
|
||||
private val tvTitle : TextView
|
||||
|
@ -42,19 +36,6 @@ internal class HeaderViewHolderInstance(
|
|||
|
||||
init {
|
||||
|
||||
if(activity.timeline_font != null) {
|
||||
Utils.scanView(viewRoot) { v ->
|
||||
try {
|
||||
if(v is Button) {
|
||||
// ボタンは太字なので触らない
|
||||
} else if(v is TextView) {
|
||||
v.typeface = activity.timeline_font
|
||||
}
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
// CharSequence sv = HTMLDecoder.decodeHTML( activity, access_info, html, false, true, null );
|
||||
//
|
||||
|
@ -85,6 +66,7 @@ internal class HeaderViewHolderInstance(
|
|||
}
|
||||
|
||||
override fun bindData(column : Column) {
|
||||
super.bindData(column)
|
||||
val instance = column.instance_information
|
||||
this.instance = instance
|
||||
|
||||
|
@ -100,16 +82,16 @@ internal class HeaderViewHolderInstance(
|
|||
val uri = instance.uri ?: ""
|
||||
btnInstance.text = uri
|
||||
btnInstance.isEnabled = uri.isNotEmpty()
|
||||
|
||||
tvVersion.text = instance .version ?: ""
|
||||
tvTitle.text = instance .title ?: ""
|
||||
|
||||
val email = instance .email ?:""
|
||||
|
||||
tvVersion.text = instance.version ?: ""
|
||||
tvTitle.text = instance.title ?: ""
|
||||
|
||||
val email = instance.email ?: ""
|
||||
btnEmail.text = email
|
||||
btnEmail.isEnabled = email.isNotEmpty()
|
||||
|
||||
val sb = DecodeOptions(decodeEmoji = true)
|
||||
.decodeHTML(activity, access_info, "<p>" + (instance .description ?: "") + "</p>")
|
||||
.decodeHTML(activity, access_info, "<p>" + (instance.description ?: "") + "</p>")
|
||||
|
||||
var previous_br_count = 0
|
||||
var i = 0
|
||||
|
@ -130,7 +112,7 @@ internal class HeaderViewHolderInstance(
|
|||
tvDescription.text = sb
|
||||
|
||||
val stats = instance.stats
|
||||
if( stats == null) {
|
||||
if(stats == null) {
|
||||
tvUserCount.setText(R.string.not_provided_mastodon_under_1_6)
|
||||
tvTootCount.setText(R.string.not_provided_mastodon_under_1_6)
|
||||
tvDomainCount.setText(R.string.not_provided_mastodon_under_1_6)
|
||||
|
@ -142,10 +124,10 @@ internal class HeaderViewHolderInstance(
|
|||
}
|
||||
|
||||
val thumbnail = instance.thumbnail
|
||||
if(thumbnail == null || thumbnail.isEmpty() ) {
|
||||
if(thumbnail == null || thumbnail.isEmpty()) {
|
||||
ivThumbnail.setImageUrl(App1.pref, 0f, null)
|
||||
} else {
|
||||
ivThumbnail.setImageUrl(App1.pref, 0f,thumbnail, thumbnail)
|
||||
ivThumbnail.setImageUrl(App1.pref, 0f, thumbnail, thumbnail)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -153,11 +135,11 @@ internal class HeaderViewHolderInstance(
|
|||
override fun onClick(v : View) {
|
||||
when(v.id) {
|
||||
|
||||
R.id.btnInstance -> instance?.uri?.let{ uri ->
|
||||
R.id.btnInstance -> instance?.uri?.let { uri ->
|
||||
App1.openCustomTab(activity, "https://$uri/about")
|
||||
}
|
||||
|
||||
R.id.btnEmail -> instance?.email?.let{ email->
|
||||
R.id.btnEmail -> instance?.email?.let { email ->
|
||||
try {
|
||||
val intent = Intent(Intent.ACTION_SEND)
|
||||
intent.type = "text/plain"
|
||||
|
@ -166,22 +148,22 @@ internal class HeaderViewHolderInstance(
|
|||
activity.startActivity(intent)
|
||||
|
||||
} catch(ex : Throwable) {
|
||||
log.e(ex,"startActivity failed. mail=$email")
|
||||
log.e(ex, "startActivity failed. mail=$email")
|
||||
Utils.showToast(activity, true, R.string.missing_mail_app)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
R.id.ivThumbnail -> instance?.thumbnail?.let{ thumbnail ->
|
||||
R.id.ivThumbnail -> instance?.thumbnail?.let { thumbnail ->
|
||||
try {
|
||||
if( thumbnail.isNotEmpty() ){
|
||||
if(thumbnail.isNotEmpty()) {
|
||||
val intent = Intent(Intent.ACTION_VIEW)
|
||||
intent.data = Uri.parse(thumbnail)
|
||||
activity.startActivity(intent)
|
||||
}
|
||||
|
||||
} catch(ex : Throwable) {
|
||||
log.e(ex,"startActivity failed. thumbnail=$thumbnail")
|
||||
log.e(ex, "startActivity failed. thumbnail=$thumbnail")
|
||||
Utils.showToast(activity, true, "missing web browser")
|
||||
}
|
||||
|
||||
|
@ -189,10 +171,7 @@ internal class HeaderViewHolderInstance(
|
|||
}
|
||||
}
|
||||
|
||||
// private void setContent( @NonNull WebView wv, @NonNull String html, @NonNull String mime_type ){
|
||||
// html = "<html><meta charset=\"UTF-8\"><p>"+html+"</p></html>";
|
||||
// wv.clearHistory();
|
||||
// wv.loadData( Base64.encodeToString( Utils.encodeUTF8(html) ,Base64.NO_WRAP), mime_type+";charset=UTF-8", "base64");
|
||||
// }
|
||||
override fun onViewRecycled() {
|
||||
}
|
||||
|
||||
}
|
|
@ -7,7 +7,6 @@ import android.view.View
|
|||
import android.widget.Button
|
||||
import android.widget.ImageButton
|
||||
import android.widget.ImageView
|
||||
import android.widget.ListView
|
||||
import android.widget.TextView
|
||||
import jp.juggler.emoji.EmojiMap201709
|
||||
|
||||
|
@ -23,15 +22,10 @@ import jp.juggler.subwaytooter.util.Utils
|
|||
import jp.juggler.subwaytooter.view.MyLinkMovementMethod
|
||||
import jp.juggler.subwaytooter.view.MyNetworkImageView
|
||||
|
||||
internal class HeaderViewHolderProfile(
|
||||
arg_activity : ActMain,
|
||||
column : Column,
|
||||
parent : ListView
|
||||
) : HeaderViewHolderBase(
|
||||
arg_activity,
|
||||
column,
|
||||
arg_activity.layoutInflater.inflate(R.layout.lv_header_account, parent, false)
|
||||
), View.OnClickListener, View.OnLongClickListener {
|
||||
internal class ViewHolderHeaderProfile(
|
||||
activity : ActMain,
|
||||
viewRoot : View
|
||||
) : ViewHolderHeaderBase(activity, viewRoot), View.OnClickListener, View.OnLongClickListener {
|
||||
|
||||
private val ivBackground : MyNetworkImageView
|
||||
private val tvCreated : TextView
|
||||
|
@ -64,7 +58,6 @@ internal class HeaderViewHolderProfile(
|
|||
private val moved_name_invalidator : NetworkEmojiInvalidator
|
||||
|
||||
init {
|
||||
|
||||
ivBackground = viewRoot.findViewById(R.id.ivBackground)
|
||||
llProfile = viewRoot.findViewById(R.id.llProfile)
|
||||
tvCreated = viewRoot.findViewById(R.id.tvCreated)
|
||||
|
@ -110,15 +103,7 @@ internal class HeaderViewHolderProfile(
|
|||
moved_caption_invalidator = NetworkEmojiInvalidator(activity.handler, tvMoved)
|
||||
moved_name_invalidator = NetworkEmojiInvalidator(activity.handler, tvMovedName)
|
||||
|
||||
if(! activity.timeline_font_size_sp.isNaN()) {
|
||||
tvMovedName.textSize = activity.timeline_font_size_sp
|
||||
tvMoved.textSize = activity.timeline_font_size_sp
|
||||
}
|
||||
|
||||
if(! activity.acct_font_size_sp.isNaN()) {
|
||||
tvMovedAcct.textSize = activity.acct_font_size_sp
|
||||
tvCreated.textSize = activity.acct_font_size_sp
|
||||
}
|
||||
ivBackground.measureProfileBg = true
|
||||
}
|
||||
|
||||
override fun showColor() {
|
||||
|
@ -132,6 +117,18 @@ internal class HeaderViewHolderProfile(
|
|||
}
|
||||
|
||||
override fun bindData(column : Column) {
|
||||
super.bindData(column)
|
||||
|
||||
if(! activity.timeline_font_size_sp.isNaN()) {
|
||||
tvMovedName.textSize = activity.timeline_font_size_sp
|
||||
tvMoved.textSize = activity.timeline_font_size_sp
|
||||
}
|
||||
|
||||
if(! activity.acct_font_size_sp.isNaN()) {
|
||||
tvMovedAcct.textSize = activity.acct_font_size_sp
|
||||
tvCreated.textSize = activity.acct_font_size_sp
|
||||
}
|
||||
|
||||
val who = column.who_account
|
||||
this.who = who
|
||||
|
||||
|
@ -161,15 +158,25 @@ internal class HeaderViewHolderProfile(
|
|||
tvRemoteProfileWarning.visibility = View.GONE
|
||||
} else {
|
||||
tvCreated.text = TootStatus.formatTime(tvCreated.context, who.time_created_at, true)
|
||||
ivBackground.setImageUrl(activity.pref, 0f, access_info.supplyBaseUrl(who.header_static))
|
||||
ivBackground.setImageUrl(
|
||||
activity.pref,
|
||||
0f,
|
||||
access_info.supplyBaseUrl(who.header_static)
|
||||
)
|
||||
|
||||
ivAvatar.setImageUrl(activity.pref, 16f, access_info.supplyBaseUrl(who.avatar_static), access_info.supplyBaseUrl(who.avatar))
|
||||
ivAvatar.setImageUrl(
|
||||
activity.pref,
|
||||
16f,
|
||||
access_info.supplyBaseUrl(who.avatar_static),
|
||||
access_info.supplyBaseUrl(who.avatar)
|
||||
)
|
||||
|
||||
val name = who.decoded_display_name
|
||||
tvDisplayName.text = name
|
||||
name_invalidator.register(name)
|
||||
|
||||
tvRemoteProfileWarning.visibility = if(column.access_info.isRemoteUser(who)) View.VISIBLE else View.GONE
|
||||
tvRemoteProfileWarning.visibility =
|
||||
if(column.access_info.isRemoteUser(who)) View.VISIBLE else View.GONE
|
||||
|
||||
val sb = SpannableStringBuilder()
|
||||
sb.append("@").append(access_info.getFullAcct(who))
|
||||
|
@ -180,7 +187,12 @@ internal class HeaderViewHolderProfile(
|
|||
val end = sb.length
|
||||
val info = EmojiMap201709.sShortNameToImageId["lock"]
|
||||
if(info != null) {
|
||||
sb.setSpan(EmojiImageSpan(activity, info.image_id), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
sb.setSpan(
|
||||
EmojiImageSpan(activity, info.image_id),
|
||||
start,
|
||||
end,
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
}
|
||||
}
|
||||
tvAcct.text = sb
|
||||
|
@ -207,7 +219,11 @@ internal class HeaderViewHolderProfile(
|
|||
llMoved.visibility = View.VISIBLE
|
||||
tvMoved.visibility = View.VISIBLE
|
||||
|
||||
val caption = Utils.formatSpannable1(activity, R.string.account_moved_to, who.decodeDisplayName(activity))
|
||||
val caption = Utils.formatSpannable1(
|
||||
activity,
|
||||
R.string.account_moved_to,
|
||||
who.decodeDisplayName(activity)
|
||||
)
|
||||
tvMoved.text = caption
|
||||
moved_caption_invalidator.register(caption)
|
||||
|
||||
|
@ -225,9 +241,16 @@ internal class HeaderViewHolderProfile(
|
|||
|
||||
private fun setAcct(tv : TextView, acctLong : String, acctShort : String) {
|
||||
val ac = AcctColor.load(acctLong)
|
||||
tv.text = if(AcctColor.hasNickname(ac)) ac.nickname else if(activity.shortAcctLocalUser) "@" + acctShort else acctLong
|
||||
tv.text = when {
|
||||
AcctColor.hasNickname(ac) -> ac.nickname
|
||||
activity.shortAcctLocalUser -> "@" + acctShort
|
||||
else -> acctLong
|
||||
}
|
||||
|
||||
val acct_color = if(column.acct_color != 0) column.acct_color else Styler.getAttributeColor(activity, R.attr.colorTimeSmall)
|
||||
val acct_color = when {
|
||||
column.acct_color != 0 -> column.acct_color
|
||||
else -> Styler.getAttributeColor(activity, R.attr.colorTimeSmall)
|
||||
}
|
||||
tv.setTextColor(if(AcctColor.hasColorForeground(ac)) ac.color_fg else acct_color)
|
||||
|
||||
if(AcctColor.hasColorBackground(ac)) {
|
||||
|
@ -265,23 +288,28 @@ internal class HeaderViewHolderProfile(
|
|||
column.startLoading()
|
||||
}
|
||||
|
||||
R.id.btnMore -> who ?.let{ who->
|
||||
R.id.btnMore -> who?.let { who ->
|
||||
DlgContextMenu(activity, column, who, null, null).show()
|
||||
}
|
||||
|
||||
R.id.btnFollow -> who ?.let{ who->
|
||||
R.id.btnFollow -> who?.let { who ->
|
||||
DlgContextMenu(activity, column, who, null, null).show()
|
||||
}
|
||||
|
||||
R.id.btnMoved -> who_moved ?.let{ who_moved->
|
||||
R.id.btnMoved -> who_moved?.let { who_moved ->
|
||||
DlgContextMenu(activity, column, who_moved, null, null).show()
|
||||
}
|
||||
|
||||
R.id.llMoved -> who_moved ?.let { who_moved ->
|
||||
R.id.llMoved -> who_moved?.let { who_moved ->
|
||||
if(access_info.isPseudo) {
|
||||
DlgContextMenu(activity, column, who_moved, null, null).show()
|
||||
} else {
|
||||
Action_User.profileLocal(activity, activity.nextPosition(column), access_info, who_moved)
|
||||
Action_User.profileLocal(
|
||||
activity,
|
||||
activity.nextPosition(column),
|
||||
access_info,
|
||||
who_moved
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -291,12 +319,22 @@ internal class HeaderViewHolderProfile(
|
|||
when(v.id) {
|
||||
|
||||
R.id.btnFollow -> {
|
||||
Action_Follow.followFromAnotherAccount(activity, activity.nextPosition(column), access_info, who)
|
||||
Action_Follow.followFromAnotherAccount(
|
||||
activity,
|
||||
activity.nextPosition(column),
|
||||
access_info,
|
||||
who
|
||||
)
|
||||
return true
|
||||
}
|
||||
|
||||
R.id.btnMoved -> {
|
||||
Action_Follow.followFromAnotherAccount(activity, activity.nextPosition(column), access_info, who_moved)
|
||||
Action_Follow.followFromAnotherAccount(
|
||||
activity,
|
||||
activity.nextPosition(column),
|
||||
access_info,
|
||||
who_moved
|
||||
)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -304,4 +342,7 @@ internal class HeaderViewHolderProfile(
|
|||
return false
|
||||
}
|
||||
|
||||
override fun onViewRecycled() {
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package jp.juggler.subwaytooter
|
||||
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
|
||||
import jp.juggler.subwaytooter.util.DecodeOptions
|
||||
import jp.juggler.subwaytooter.view.MyLinkMovementMethod
|
||||
|
||||
internal class ViewHolderHeaderSearch(
|
||||
arg_activity : ActMain,
|
||||
viewRoot : View
|
||||
) : ViewHolderHeaderBase(arg_activity, viewRoot) {
|
||||
|
||||
private val tvSearchDesc : TextView
|
||||
|
||||
init {
|
||||
this.tvSearchDesc = viewRoot.findViewById(R.id.tvSearchDesc)
|
||||
tvSearchDesc.visibility = View.VISIBLE
|
||||
tvSearchDesc.movementMethod = MyLinkMovementMethod
|
||||
}
|
||||
|
||||
override fun showColor() {
|
||||
}
|
||||
|
||||
override fun bindData(column : Column) {
|
||||
super.bindData(column)
|
||||
|
||||
val html = column.getHeaderDesc() ?: ""
|
||||
val sv = DecodeOptions(decodeEmoji = true).decodeHTML(activity, access_info, html)
|
||||
|
||||
tvSearchDesc.text = sv
|
||||
}
|
||||
|
||||
override fun onViewRecycled() {
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package jp.juggler.subwaytooter
|
||||
|
||||
import android.support.v7.widget.RecyclerView
|
||||
|
||||
internal class ViewHolderItem(val ivh : ItemViewHolder) : RecyclerView.ViewHolder(ivh.viewRoot)
|
|
@ -64,11 +64,13 @@ class TootApiClient(
|
|||
|
||||
private val reStartJsonArray = Pattern.compile("\\A\\s*\\[")
|
||||
private val reStartJsonObject = Pattern.compile("\\A\\s*\\{")
|
||||
private val reWhiteSpace = Pattern.compile("\\s+")
|
||||
|
||||
private val mspTokenUrl = "http://mastodonsearch.jp/api/v1.0.1/utoken"
|
||||
private val mspSearchUrl = "http://mastodonsearch.jp/api/v1.0.1/cross"
|
||||
private val mspApiKey = "e53de7f66130208f62d1808672bf6320523dcd0873dc69bc"
|
||||
|
||||
|
||||
fun getMspMaxId(array : JSONArray, max_id : String) : String {
|
||||
// max_id の更新
|
||||
val size = array.length()
|
||||
|
@ -123,12 +125,14 @@ class TootApiClient(
|
|||
// HTMLならタグの除去を試みる
|
||||
val ct = response.body()?.contentType()
|
||||
if(ct?.subtype() == "html") {
|
||||
return DecodeOptions().decodeHTML(null, null, sv).toString()
|
||||
val decoded = DecodeOptions().decodeHTML(null, null, sv).toString()
|
||||
|
||||
return reWhiteSpace.matcher(decoded).replaceAll(" ").trim()
|
||||
}
|
||||
|
||||
// XXX: Amazon S3 が403を返した場合にcontent-typeが?/xmlでserverがAmazonならXMLをパースしてエラーを整形することもできるが、多分必要ない
|
||||
|
||||
return sv
|
||||
return reWhiteSpace.matcher(sv).replaceAll(" ").trim()
|
||||
}
|
||||
|
||||
fun formatResponse(
|
||||
|
|
|
@ -33,7 +33,7 @@ class TootInstance(src : JSONObject) {
|
|||
// ユーザ数等の数字。マストドン1.6以降。
|
||||
val stats : Stats?
|
||||
|
||||
// FIXME: urls をパースしてない。使ってないから…
|
||||
// XXX: urls をパースしてない。使ってないから…
|
||||
|
||||
|
||||
init {
|
||||
|
|
|
@ -148,7 +148,12 @@ object HTMLDecoder {
|
|||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private class TokenParser internal constructor(internal val src : String) {
|
||||
private val reComment = Pattern.compile("<!--.*?-->", Pattern.DOTALL)
|
||||
private val reDoctype = Pattern.compile("\\A\\s*<!doctype[^>]*>",Pattern.CASE_INSENSITIVE)
|
||||
|
||||
private class TokenParser(srcArg : String) {
|
||||
|
||||
internal val src : String
|
||||
internal var next : Int = 0
|
||||
|
||||
internal var tag : String = ""
|
||||
|
@ -156,17 +161,21 @@ object HTMLDecoder {
|
|||
internal var text : String = ""
|
||||
|
||||
init {
|
||||
this.next = 0
|
||||
var sv = reComment.matcher(srcArg).replaceAll(" ")
|
||||
sv = reDoctype.matcher(sv).replaceFirst("")
|
||||
this.src = sv
|
||||
eat()
|
||||
}
|
||||
|
||||
internal fun eat() {
|
||||
|
||||
// end?
|
||||
if(next >= src.length) {
|
||||
tag = TAG_END
|
||||
open_type = OPEN_TYPE_OPEN_CLOSE
|
||||
return
|
||||
}
|
||||
|
||||
// text ?
|
||||
var end = src.indexOf('<', next)
|
||||
if(end == - 1) end = src.length
|
||||
|
@ -177,6 +186,7 @@ object HTMLDecoder {
|
|||
next = end
|
||||
return
|
||||
}
|
||||
|
||||
// tag ?
|
||||
end = src.indexOf('>', next)
|
||||
if(end == - 1) {
|
||||
|
@ -187,6 +197,10 @@ object HTMLDecoder {
|
|||
text = src.substring(next, end)
|
||||
|
||||
next = end
|
||||
|
||||
|
||||
|
||||
|
||||
val m = reTag.matcher(text)
|
||||
if(m.find()) {
|
||||
val is_close = m.group(1).isNotEmpty()
|
||||
|
@ -203,6 +217,8 @@ object HTMLDecoder {
|
|||
this.open_type = OPEN_TYPE_OPEN_CLOSE
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private class Node {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package jp.juggler.subwaytooter.util
|
||||
|
||||
import jp.juggler.subwaytooter.view.MyListView
|
||||
import android.support.v7.widget.RecyclerView
|
||||
import jp.juggler.subwaytooter.ColumnViewHolder
|
||||
|
||||
class ScrollPosition{
|
||||
|
||||
|
@ -12,19 +13,37 @@ class ScrollPosition{
|
|||
this.top = top
|
||||
}
|
||||
|
||||
constructor(listView : MyListView) {
|
||||
if(listView.childCount == 0) {
|
||||
// constructor(listView : MyListView) {
|
||||
// if(listView.childCount == 0) {
|
||||
// top = 0
|
||||
// pos = top
|
||||
// } else {
|
||||
// pos = listView.firstVisiblePosition
|
||||
// top = listView.getChildAt(0).top
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// fun restore(listView : MyListView) {
|
||||
// if(0 <= pos && pos < listView.adapter.count) {
|
||||
// listView.setSelectionFromTop(pos, top)
|
||||
// }
|
||||
// }
|
||||
|
||||
constructor(holder:ColumnViewHolder) {
|
||||
val findPosition = holder.listLayoutManager.findFirstVisibleItemPosition()
|
||||
if( findPosition == RecyclerView.NO_POSITION){
|
||||
top = 0
|
||||
pos = top
|
||||
} else {
|
||||
pos = listView.firstVisiblePosition
|
||||
top = listView.getChildAt(0).top
|
||||
}else{
|
||||
pos = findPosition
|
||||
val firstItemView = holder.listLayoutManager.findViewByPosition(findPosition)
|
||||
top = firstItemView?.top ?: 0
|
||||
}
|
||||
}
|
||||
|
||||
fun restore(listView : MyListView) {
|
||||
if(0 <= pos && pos < listView.adapter.count) {
|
||||
listView.setSelectionFromTop(pos, top)
|
||||
fun restore(holder:ColumnViewHolder) {
|
||||
if(0 <= pos && pos < holder.listView.adapter.itemCount) {
|
||||
holder.listLayoutManager.scrollToPositionWithOffset(pos,top)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
package jp.juggler.subwaytooter.view
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Rect
|
||||
import android.support.v7.widget.RecyclerView
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.view.View
|
||||
import jp.juggler.subwaytooter.R
|
||||
import jp.juggler.subwaytooter.Styler
|
||||
|
||||
class ListDivider(context : Context) : RecyclerView.ItemDecoration() {
|
||||
|
||||
companion object {
|
||||
var height : Int =0
|
||||
}
|
||||
|
||||
private val drawable : Drawable
|
||||
|
||||
init {
|
||||
drawable = Styler.getAttributeDrawable(context, R.attr.colorSettingDivider)
|
||||
height = (context.resources.displayMetrics.density * 1f +0.5f).toInt()
|
||||
}
|
||||
|
||||
override fun getItemOffsets(outRect : Rect, view : View, parent : RecyclerView, state : RecyclerView.State) {
|
||||
outRect.set(0, 0, 0, height)
|
||||
}
|
||||
|
||||
override fun onDraw(canvas : Canvas, parent : RecyclerView, state : RecyclerView.State) {
|
||||
val left = parent.paddingLeft
|
||||
val right = parent.width - parent.paddingRight
|
||||
|
||||
for(i in 0 until parent.childCount) {
|
||||
val child = parent.getChildAt(i)
|
||||
val params = child.layoutParams as RecyclerView.LayoutParams
|
||||
val top = child.bottom + params.bottomMargin
|
||||
val bottom = top + height
|
||||
drawable.setBounds(left, top, right, bottom)
|
||||
drawable.draw(canvas)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -6,6 +6,7 @@ import android.os.SystemClock
|
|||
import android.util.AttributeSet
|
||||
import android.view.MotionEvent
|
||||
import android.widget.ListView
|
||||
import jp.juggler.subwaytooter.StatusButtonsPopup
|
||||
|
||||
import jp.juggler.subwaytooter.util.LogCategory
|
||||
|
||||
|
@ -15,19 +16,16 @@ class MyListView : ListView {
|
|||
private val log = LogCategory("MyListView")
|
||||
}
|
||||
|
||||
var last_popup_close = 0L
|
||||
|
||||
constructor(context : Context) : super(context)
|
||||
constructor(context : Context, attrs : AttributeSet) : super(context, attrs)
|
||||
constructor(context : Context, attrs : AttributeSet, defStyleAttr : Int) : super(context, attrs, defStyleAttr)
|
||||
constructor(context : Context, attrs : AttributeSet, defStyleAttr : Int, defStyleRes : Int) : super(context, attrs, defStyleAttr, defStyleRes)
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onTouchEvent(ev : MotionEvent) : Boolean {
|
||||
|
||||
// ポップアップを閉じた時にクリックでリストを触ったことになってしまう不具合の回避
|
||||
val now = SystemClock.elapsedRealtime()
|
||||
if(now - last_popup_close < 30L) {
|
||||
if(now - StatusButtonsPopup.last_popup_close < 30L) {
|
||||
val action = ev.action
|
||||
if(action == MotionEvent.ACTION_DOWN) {
|
||||
// ポップアップを閉じた直後はタッチダウンを無視する
|
||||
|
|
|
@ -126,7 +126,7 @@ class MyNetworkImageView @JvmOverloads constructor(
|
|||
val d = drawable
|
||||
if(d is Animatable) {
|
||||
if(d.isRunning) {
|
||||
log.d("cancelLoading: Animatable.stop()")
|
||||
//log.d("cancelLoading: Animatable.stop()")
|
||||
d.stop()
|
||||
}
|
||||
}
|
||||
|
@ -446,4 +446,32 @@ class MyNetworkImageView @JvmOverloads constructor(
|
|||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// プロフ表示の背景画像のレイアウト崩れの対策
|
||||
var measureProfileBg = false
|
||||
|
||||
override fun onMeasure(widthMeasureSpec : Int, heightMeasureSpec : Int) {
|
||||
if(measureProfileBg) {
|
||||
val w_mode = MeasureSpec.getMode(widthMeasureSpec)
|
||||
val w_size = MeasureSpec.getSize(widthMeasureSpec)
|
||||
val h_mode = MeasureSpec.getMode(heightMeasureSpec)
|
||||
val h_size = MeasureSpec.getSize(heightMeasureSpec)
|
||||
|
||||
val w = when(w_mode) {
|
||||
MeasureSpec.EXACTLY -> w_size
|
||||
MeasureSpec.AT_MOST -> w_size
|
||||
MeasureSpec.UNSPECIFIED ->0
|
||||
else -> 0
|
||||
}
|
||||
val h = when(h_mode) {
|
||||
MeasureSpec.EXACTLY -> h_size
|
||||
MeasureSpec.AT_MOST -> h_size
|
||||
MeasureSpec.UNSPECIFIED ->0
|
||||
else -> 0
|
||||
}
|
||||
setMeasuredDimension(w, h)
|
||||
}else{
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import android.util.AttributeSet
|
|||
import android.view.MotionEvent
|
||||
import android.view.ViewConfiguration
|
||||
|
||||
class MyRecyclerView : RecyclerView {
|
||||
class TabletModeRecyclerView : RecyclerView {
|
||||
|
||||
|
||||
private var mForbidStartDragging : Boolean = false
|
|
@ -207,6 +207,22 @@
|
|||
|
||||
<View style="@style/setting_divider"/>
|
||||
|
||||
<TextView
|
||||
style="@style/setting_row_label"
|
||||
android:text="@string/force_gap_when_refresh"
|
||||
/>
|
||||
|
||||
<LinearLayout style="@style/setting_row_form">
|
||||
|
||||
<Switch
|
||||
android:id="@+id/swForceGap"
|
||||
style="@style/setting_horizontal_stretch"
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<View style="@style/setting_divider"/>
|
||||
|
||||
<TextView
|
||||
style="@style/setting_row_label"
|
||||
android:labelFor="@+id/etClientName"
|
||||
|
@ -574,21 +590,21 @@
|
|||
android:text="@string/appearance"
|
||||
/>
|
||||
|
||||
<View style="@style/setting_divider"/>
|
||||
<!--<View style="@style/setting_divider"/>-->
|
||||
|
||||
<TextView
|
||||
style="@style/setting_row_label"
|
||||
android:text="@string/disable_fast_scroller"
|
||||
/>
|
||||
<!--<TextView-->
|
||||
<!--style="@style/setting_row_label"-->
|
||||
<!--android:text="@string/disable_fast_scroller"-->
|
||||
<!--/>-->
|
||||
|
||||
<LinearLayout style="@style/setting_row_form">
|
||||
<!--<LinearLayout style="@style/setting_row_form">-->
|
||||
|
||||
<Switch
|
||||
android:id="@+id/swDisableFastScroller"
|
||||
style="@style/setting_horizontal_stretch"
|
||||
/>
|
||||
<!--<Switch-->
|
||||
<!--android:id="@+id/swDisableFastScroller"-->
|
||||
<!--style="@style/setting_horizontal_stretch"-->
|
||||
<!--/>-->
|
||||
|
||||
</LinearLayout>
|
||||
<!--</LinearLayout>-->
|
||||
|
||||
<View style="@style/setting_divider"/>
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@
|
|||
android:layout_height="match_parent"
|
||||
/>
|
||||
|
||||
<jp.juggler.subwaytooter.view.MyRecyclerView
|
||||
<jp.juggler.subwaytooter.view.TabletModeRecyclerView
|
||||
android:id="@+id/rvPager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
|
|
|
@ -115,6 +115,7 @@
|
|||
android:id="@+id/ivBackground"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:scaleType="centerCrop"
|
||||
/>
|
||||
|
|
@ -373,19 +373,20 @@
|
|||
app:srl_direction="both"
|
||||
>
|
||||
|
||||
<jp.juggler.subwaytooter.view.MyListView
|
||||
<android.support.v7.widget.ListRecyclerView
|
||||
android:id="@+id/listView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
|
||||
android:clipToPadding="false"
|
||||
android:divider="?attr/colorSettingDivider"
|
||||
android:dividerHeight="1dp"
|
||||
android:fadeScrollbars="false"
|
||||
android:fastScrollEnabled="true"
|
||||
android:paddingEnd="12dp"
|
||||
android:paddingStart="12dp"
|
||||
android:clipToPadding="false"
|
||||
|
||||
android:scrollbars="vertical"
|
||||
android:fadeScrollbars="false"
|
||||
android:scrollbarStyle="outsideOverlay"
|
||||
|
||||
|
||||
/>
|
||||
|
||||
</com.omadahealth.github.swipyrefreshlayout.library.SwipyRefreshLayout>
|
||||
|
|
|
@ -600,6 +600,7 @@
|
|||
<string name="domain_block_from_pseudo">Can\'t use domain block from pseudo account.</string>
|
||||
<string name="domain_block_from_local">Can\'t use domain block for local instance.</string>
|
||||
<string name="always_show_application">Show (via) application name if possible</string>
|
||||
<string name="force_gap_when_refresh">Force put gap when refreshing top</string>
|
||||
|
||||
<!--<string name="abc_action_bar_home_description">Revenir à l\'accueil</string>-->
|
||||
<!--<string name="abc_action_bar_home_description_format">%1$s, %2$s</string>-->
|
||||
|
|
|
@ -884,5 +884,6 @@
|
|||
<string name="domain_block_from_pseudo">疑似アカウントではドメインブロックできません</string>
|
||||
<string name="domain_block_from_local">自分のタンスにはドメインブロックできません</string>
|
||||
<string name="always_show_application">アプリ名(via)を可能なら表示する</string>
|
||||
<string name="force_gap_when_refresh">リフレッシュ時に常にギャップを挟む</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -589,5 +589,6 @@
|
|||
<string name="domain_block_from_pseudo">Can\'t use domain block from pseudo account.</string>
|
||||
<string name="domain_block_from_local">Can\'t use domain block for local instance.</string>
|
||||
<string name="always_show_application">Show (via) application name if possible</string>
|
||||
<string name="force_gap_when_refresh">Force put gap when refreshing top</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
buildscript {
|
||||
|
||||
ext.kotlin_version = '1.2.10'
|
||||
ext.kotlin_version = '1.2.20'
|
||||
ext.anko_version='0.10.4'
|
||||
|
||||
repositories {
|
||||
|
|
|
@ -15,3 +15,10 @@ org.gradle.jvmargs=-Xmx1536m
|
|||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
|
||||
|
||||
####################################
|
||||
# enable build cache
|
||||
# https://blog.jetbrains.com/kotlin/2018/01/kotlin-1-2-20-is-out/
|
||||
org.gradle.caching=true
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#Sat Nov 04 23:32:32 JST 2017
|
||||
#Thu Jan 18 01:59:02 JST 2018
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.3-all.zip
|
||||
|
|
Loading…
Reference in New Issue