ハッシュタグカラムに追加のタグの指定を追加

This commit is contained in:
tateisu 2019-02-03 13:29:50 +09:00
parent 778798d5c0
commit d9bbc1f038
6 changed files with 260 additions and 64 deletions

View File

@ -178,6 +178,9 @@ class Column(
private const val KEY_PROFILE_TAB = "tab"
private const val KEY_STATUS_ID = "status_id"
private const val KEY_HASHTAG = "hashtag"
private const val KEY_HASHTAG_ANY = "hashtag_any"
private const val KEY_HASHTAG_ALL = "hashtag_all"
private const val KEY_HASHTAG_NONE = "hashtag_none"
private const val KEY_SEARCH_QUERY = "search_query"
private const val KEY_SEARCH_RESOLVE = "search_resolve"
private const val KEY_INSTANCE_URI = "instance_uri"
@ -240,6 +243,12 @@ class Column(
return params[idx] as T
}
@Suppress("UNCHECKED_CAST")
private inline fun <reified T> getParamAtNullable(params : Array<out Any>, idx : Int) : T? {
if(idx >= params.size) return null
return params[idx] as T
}
fun loadAccount(context : Context, src : JSONObject) : SavedAccount {
val account_db_id = src.parseLong(KEY_ACCOUNT_ROW_ID) ?: - 1L
return if(account_db_id >= 0) {
@ -561,8 +570,13 @@ class Column(
TYPE_DIRECT_MESSAGES -> "/api/v1/streaming/?stream=direct"
TYPE_HASHTAG -> when(instance_local) {
true -> "/api/v1/streaming/?stream=" + Uri.encode("hashtag:local") + "&tag=" + hashtag.encodePercent()
else -> "/api/v1/streaming/?stream=hashtag&tag=" + hashtag.encodePercent()
true -> {
"/api/v1/streaming/?stream=" + Uri.encode("hashtag:local") +
"&tag=" + hashtag.encodePercent() + makeHashtagExtraQuery()
}
else -> "/api/v1/streaming/?stream=hashtag&tag=" + hashtag.encodePercent() +
makeHashtagExtraQuery()
// タグ先頭の#を含まない
}
else -> null
@ -622,8 +636,11 @@ class Column(
internal var search_query : String = ""
internal var search_resolve : Boolean = false
private var hashtag : String = ""
internal var instance_uri : String = ""
private var hashtag : String = ""
internal var hashtag_any : String = ""
internal var hashtag_all : String = ""
internal var hashtag_none : String = ""
// プロフカラムでのアカウント情報
@Volatile
@ -834,7 +851,12 @@ class Column(
profile_id = EntityId.mayNull(src.parseString(KEY_PROFILE_ID))
}
TYPE_HASHTAG -> hashtag = src.optString(KEY_HASHTAG)
TYPE_HASHTAG -> {
hashtag = src.optString(KEY_HASHTAG)
hashtag_any = src.optString(KEY_HASHTAG_ANY)
hashtag_all = src.optString(KEY_HASHTAG_ALL)
hashtag_none = src.optString(KEY_HASHTAG_NONE)
}
TYPE_SEARCH -> {
search_query = src.optString(KEY_SEARCH_QUERY)
@ -899,7 +921,12 @@ class Column(
TYPE_LIST_MEMBER, TYPE_LIST_TL ->
dst.put(KEY_PROFILE_ID, profile_id.toString())
TYPE_HASHTAG -> dst.put(KEY_HASHTAG, hashtag)
TYPE_HASHTAG -> {
dst.put(KEY_HASHTAG, hashtag)
dst.put(KEY_HASHTAG_ANY, hashtag_any)
dst.put(KEY_HASHTAG_ALL, hashtag_all)
dst.put(KEY_HASHTAG_NONE, hashtag_none)
}
TYPE_SEARCH -> dst.put(KEY_SEARCH_QUERY, search_query).put(
KEY_SEARCH_RESOLVE,
@ -932,7 +959,12 @@ class Column(
TYPE_CONVERSATION, TYPE_BOOSTED_BY, TYPE_FAVOURITED_BY, TYPE_LOCAL_AROUND, TYPE_FEDERATED_AROUND, TYPE_ACCOUNT_AROUND ->
status_id == EntityId(getParamAt(params, 0))
TYPE_HASHTAG -> getParamAt<String>(params, 0) == hashtag
TYPE_HASHTAG -> {
(getParamAt<String>(params, 0) == hashtag)
&& ((getParamAtNullable<String>(params, 1) ?: "") == hashtag_any)
&& ((getParamAtNullable<String>(params, 2) ?: "") == hashtag_all)
&& ((getParamAtNullable<String>(params, 3) ?: "") == hashtag_none)
}
TYPE_SEARCH -> getParamAt<String>(params, 0) == search_query && getParamAt<Boolean>(
params,
@ -995,7 +1027,29 @@ class Column(
(status_id?.toString() ?: "null")
)
TYPE_HASHTAG -> context.getString(R.string.hashtag_of, hashtag)
TYPE_HASHTAG -> {
val sb = StringBuilder(context.getString(R.string.hashtag_of, hashtag))
if(hashtag_any.isNotBlank()) sb.append(' ').append(
context.getString(
R.string.hashtag_title_any,
hashtag_any
)
)
if(hashtag_all.isNotBlank()) sb.append(' ').append(
context.getString(
R.string.hashtag_title_all,
hashtag_all
)
)
if(hashtag_none.isNotBlank()) sb.append(' ').append(
context.getString(
R.string.hashtag_title_none,
hashtag_none
)
)
sb.toString()
}
TYPE_SEARCH ->
if(bLong) context.getString(R.string.search_of, search_query)
@ -2998,14 +3052,12 @@ class Column(
TYPE_HASHTAG -> return if(isMisskey) {
getStatuses(
client
, makeHashtagUrl(hashtag)
, misskeyParams = makeMisskeyTimelineParameter(parser)
.put("tag", hashtag)
.put("limit", MISSKEY_HASHTAG_LIMIT)
, makeHashtagUrl()
, misskeyParams = makeHashtagParams(parser)
)
} else {
getStatuses(client, makeHashtagUrl(hashtag))
getStatuses(client, makeHashtagUrl())
}
TYPE_REPORTS -> return parseReports(client, PATH_REPORTS)
@ -3538,10 +3590,10 @@ class Column(
}
private fun JSONObject.putMisskeyUntil(id : EntityId?) : JSONObject {
if(id != null){
if(useDate){
put("untilDate" ,id.toString().toLong() )
}else{
if(id != null) {
if(useDate) {
put("untilDate", id.toString().toLong())
} else {
put("untilId", id.toString())
}
}
@ -3549,10 +3601,10 @@ class Column(
}
private fun JSONObject.putMisskeySince(id : EntityId?) : JSONObject {
if(id != null){
if(useDate){
put("sinceDate" ,id.toString().toLong() )
}else{
if(id != null) {
if(useDate) {
put("sinceDate", id.toString().toLong())
} else {
put("sinceId", id.toString())
}
}
@ -5014,13 +5066,11 @@ class Column(
TYPE_HASHTAG -> if(isMisskey) {
getStatusList(
client
, makeHashtagUrl(hashtag)
, misskeyParams = makeMisskeyTimelineParameter(parser)
.put("tag", hashtag)
.put("limit", MISSKEY_HASHTAG_LIMIT)
, makeHashtagUrl()
, misskeyParams = makeHashtagParams(parser)
)
} else {
getStatusList(client, makeHashtagUrl(hashtag))
getStatusList(client, makeHashtagUrl())
}
TYPE_SEARCH_MSP ->
@ -5938,13 +5988,11 @@ class Column(
TYPE_HASHTAG -> if(isMisskey) {
getStatusList(
client
, makeHashtagUrl(hashtag)
, misskeyParams = makeMisskeyTimelineParameter(parser)
.put("tag", hashtag)
.put("limit", MISSKEY_HASHTAG_LIMIT)
, makeHashtagUrl()
, misskeyParams = makeHashtagParams(parser)
)
} else {
getStatusList(client, makeHashtagUrl(hashtag))
getStatusList(client, makeHashtagUrl())
}
TYPE_BOOSTED_BY -> getAccountList(
@ -7001,22 +7049,46 @@ class Column(
}
}
private fun makeHashtagUrl(
hashtag : String // 先頭の#を含まない
) : String {
private fun makeHashtagExtraQuery() : String {
val sb = StringBuilder()
hashtag_any.split(" ").filter { it.isNotEmpty() }.forEach {
sb.append("&any[]=").append(it.encodePercent())
}
hashtag_all.split(" ").filter { it.isNotEmpty() }.forEach {
sb.append("&all[]=").append(it.encodePercent())
}
hashtag_none.split(" ").filter { it.isNotEmpty() }.forEach {
sb.append("&none[]=").append(it.encodePercent())
}
return sb.toString()
}
private fun makeHashtagUrl() : String {
return if(isMisskey) {
"/api/notes/search_by_tag"
} else {
// hashtag : String // 先頭の#を含まない
val sb = StringBuilder("/api/v1/timelines/tag/")
.append(hashtag.encodePercent())
.append("?limit=")
.append(READ_LIMIT)
.append("?limit=").append(READ_LIMIT)
if(with_attachment) sb.append("&only_media=true")
if(instance_local) sb.append("&local=true")
sb.toString()
sb
.append(makeHashtagExtraQuery())
.toString()
}
}
private fun makeHashtagParams(parser : TootParser) : JSONObject {
return makeMisskeyTimelineParameter(parser)
.put("tag", hashtag)
.put("limit", MISSKEY_HASHTAG_LIMIT)
}
private fun loadFilter2(client : TootApiClient) : ArrayList<TootFilter>? {
if(access_info.isPseudo || access_info.isMisskey) return null
val column_context = getFilterContext()

View File

@ -134,6 +134,12 @@ class ColumnViewHolder(
private val etListName : EditText
private val btnListAdd : View
private val llHashtagExtra :LinearLayout
private val etHashtagExtraAny:EditText
private val etHashtagExtraAll:EditText
private val etHashtagExtraNone :EditText
private val isPageDestroyed : Boolean
get() = column == null || activity.isFinishing
@ -347,6 +353,13 @@ class ColumnViewHolder(
btnQuickFilterVote = viewRoot.findViewById(R.id.btnQuickFilterVote)
val llColumnSettingInside : LinearLayout = viewRoot.findViewById(R.id.llColumnSettingInside)
llHashtagExtra= viewRoot.findViewById(R.id.llHashtagExtra)
etHashtagExtraAny= viewRoot.findViewById(R.id.etHashtagExtraAny)
etHashtagExtraAll= viewRoot.findViewById(R.id.etHashtagExtraAll)
etHashtagExtraNone= viewRoot.findViewById(R.id.etHashtagExtraNone)
btnQuickFilterAll.setOnClickListener(this)
btnQuickFilterMention.setOnClickListener(this)
btnQuickFilterFavourite.setOnClickListener(this)
@ -438,27 +451,6 @@ class ColumnViewHolder(
btnColumnClose.layoutParams.height = wh
btnColumnClose.setPaddingRelative(pad, pad, pad, pad)
// 入力の追跡
etRegexFilter.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(
s : CharSequence,
start : Int,
count : Int,
after : Int
) {
}
override fun onTextChanged(s : CharSequence, start : Int, before : Int, count : Int) {}
override fun afterTextChanged(s : Editable) {
if(loading_busy) return
activity.handler.removeCallbacks(proc_start_filter)
if(isRegexValid()) {
activity.handler.postDelayed(proc_start_filter, 666L)
}
}
})
btnSearch.setOnClickListener(this)
etSearch.setOnEditorActionListener(TextView.OnEditorActionListener { _, actionId, _ ->
if(! loading_busy) {
@ -470,14 +462,48 @@ class ColumnViewHolder(
false
})
}
private val proc_start_filter = Runnable {
if(! isPageDestroyed && isRegexValid()) {
val column = this.column ?: return@Runnable
column.regex_text = etRegexFilter.text.toString()
// 入力の追跡
etRegexFilter.addTextChangedListener(CustomTextWatcher{
if(!isRegexValid()) return@CustomTextWatcher
column?.regex_text = etRegexFilter.text.toString()
activity.app_state.saveColumnList()
column.startLoading()
activity.handler.removeCallbacks(proc_start_filter)
activity.handler.postDelayed(proc_start_filter, 666L)
})
etHashtagExtraAny.addTextChangedListener(CustomTextWatcher{
column?.hashtag_any = etHashtagExtraAny.text.toString()
activity.app_state.saveColumnList()
activity.handler.removeCallbacks(proc_start_filter)
activity.handler.postDelayed(proc_start_filter, 666L)
})
etHashtagExtraAll.addTextChangedListener(CustomTextWatcher{
column?.hashtag_all = etHashtagExtraAll.text.toString()
activity.app_state.saveColumnList()
activity.handler.removeCallbacks(proc_start_filter)
activity.handler.postDelayed(proc_start_filter, 666L)
})
etHashtagExtraNone.addTextChangedListener(CustomTextWatcher{
column?.hashtag_none = etHashtagExtraNone.text.toString()
activity.app_state.saveColumnList()
activity.handler.removeCallbacks(proc_start_filter)
activity.handler.postDelayed(proc_start_filter, 666L)
})
}
private val proc_start_filter :Runnable = object: Runnable {
override fun run() {
if( isPageDestroyed ) return
val column = this@ColumnViewHolder.column ?: return
if( loading_busy){
activity.handler.removeCallbacks(this)
activity.handler.postDelayed(this, 666L)
}else {
column.startLoading()
}
}
}
@ -659,6 +685,12 @@ class ColumnViewHolder(
vg(llListList, column.column_type == Column.TYPE_LIST_LIST)
vg(cbResolve, column.column_type == Column.TYPE_SEARCH)
vg(llHashtagExtra,column.column_type == Column.TYPE_HASHTAG && !column.isMisskey)
etHashtagExtraAny.setText(column.hashtag_any)
etHashtagExtraAll.setText(column.hashtag_all)
etHashtagExtraNone.setText(column.hashtag_none)
// tvRegexFilterErrorの表示を更新
if(bAllowFilter) {
isRegexValid()

View File

@ -15,6 +15,8 @@ import android.graphics.drawable.shapes.RectShape
import android.os.Build
import android.os.SystemClock
import android.support.v4.content.ContextCompat
import android.text.Editable
import android.text.TextWatcher
import android.util.SparseArray
import android.view.View
import android.widget.ImageView
@ -285,3 +287,21 @@ fun DialogInterface.dismissSafe(){
}
}
class CustomTextWatcher(
val callback: ()->Unit
) : TextWatcher {
override fun beforeTextChanged(
s : CharSequence,
start : Int,
count : Int,
after : Int
) {
}
override fun onTextChanged(s : CharSequence, start : Int, before : Int, count : Int) {}
override fun afterTextChanged(s : Editable) {
callback()
}
}

View File

@ -154,6 +154,66 @@
android:id="@+id/llColumnSettingInside"
>
<LinearLayout
android:id="@+id/llHashtagExtra"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:labelFor="@+id/etHashtagExtraAny"
android:text="@string/hashtag_extra_any"
android:textColor="?attr/colorColumnHeaderPageNumber"
/>
<EditText
android:id="@+id/etHashtagExtraAny"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text"
android:maxLines="1"
android:scrollHorizontally="true"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:labelFor="@+id/etHashtagExtraAll"
android:text="@string/hashtag_extra_all"
android:textColor="?attr/colorColumnHeaderPageNumber"
/>
<EditText
android:id="@+id/etHashtagExtraAll"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text"
android:maxLines="1"
android:scrollHorizontally="true"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:labelFor="@+id/etHashtagExtraNone"
android:text="@string/hashtag_extra_none"
android:textColor="?attr/colorColumnHeaderPageNumber"
/>
<EditText
android:id="@+id/etHashtagExtraNone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text"
android:maxLines="1"
android:scrollHorizontally="true"
/>
</LinearLayout>
<CheckBox
android:id="@+id/cbDontCloseColumn"
android:layout_width="match_parent"

View File

@ -857,5 +857,11 @@
<string name="releases">更新履歴</string>
<string name="timezone">タイムゾーン (アプリ再起動が必要)</string>
<string name="device_timezone">端末のタイムゾーン</string>
<string name="hashtag_extra_any">追加のタグ:いずれかを含む (タグ指定に#をつけない)</string>
<string name="hashtag_extra_all">追加のタグ:全てを含む (タグ指定に#をつけない)</string>
<string name="hashtag_extra_none">追加のタグ:これらを除く (タグ指定に#をつけない)</string>
<string name="hashtag_title_any">か %1$s</string>
<string name="hashtag_title_all">と %1$s</string>
<string name="hashtag_title_none">(%1$s を除く)</string>
</resources>

View File

@ -879,5 +879,11 @@
<string name="releases">Releases</string>
<string name="timezone">Time zone (app restart required)</string>
<string name="device_timezone">Device time zone</string>
<string name="hashtag_extra_any">Additional tags: any (tags without #)</string>
<string name="hashtag_extra_all">Additional tags: all (tags without #)</string>
<string name="hashtag_extra_none">Additional tags: none (tags without #)</string>
<string name="hashtag_title_any">or %1$s</string>
<string name="hashtag_title_all">and %1$s</string>
<string name="hashtag_title_none">without %1$s</string>
</resources>