From d9bbc1f038476a84bf3b7da449585ceccb91baad Mon Sep 17 00:00:00 2001 From: tateisu Date: Sun, 3 Feb 2019 13:29:50 +0900 Subject: [PATCH] =?UTF-8?q?=E3=83=8F=E3=83=83=E3=82=B7=E3=83=A5=E3=82=BF?= =?UTF-8?q?=E3=82=B0=E3=82=AB=E3=83=A9=E3=83=A0=E3=81=AB=E8=BF=BD=E5=8A=A0?= =?UTF-8?q?=E3=81=AE=E3=82=BF=E3=82=B0=E3=81=AE=E6=8C=87=E5=AE=9A=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/jp/juggler/subwaytooter/Column.kt | 144 +++++++++++++----- .../juggler/subwaytooter/ColumnViewHolder.kt | 88 +++++++---- app/src/main/java/jp/juggler/util/UiUtils.kt | 20 +++ app/src/main/res/layout/page_column.xml | 60 ++++++++ app/src/main/res/values-ja/strings.xml | 6 + app/src/main/res/values/strings.xml | 6 + 6 files changed, 260 insertions(+), 64 deletions(-) diff --git a/app/src/main/java/jp/juggler/subwaytooter/Column.kt b/app/src/main/java/jp/juggler/subwaytooter/Column.kt index 4bdbad33..81f26b19 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/Column.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/Column.kt @@ -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 getParamAtNullable(params : Array, 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(params, 0) == hashtag + TYPE_HASHTAG -> { + (getParamAt(params, 0) == hashtag) + && ((getParamAtNullable(params, 1) ?: "") == hashtag_any) + && ((getParamAtNullable(params, 2) ?: "") == hashtag_all) + && ((getParamAtNullable(params, 3) ?: "") == hashtag_none) + } TYPE_SEARCH -> getParamAt(params, 0) == search_query && getParamAt( 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? { if(access_info.isPseudo || access_info.isMisskey) return null val column_context = getFilterContext() diff --git a/app/src/main/java/jp/juggler/subwaytooter/ColumnViewHolder.kt b/app/src/main/java/jp/juggler/subwaytooter/ColumnViewHolder.kt index c194e38a..9d24920c 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/ColumnViewHolder.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/ColumnViewHolder.kt @@ -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() diff --git a/app/src/main/java/jp/juggler/util/UiUtils.kt b/app/src/main/java/jp/juggler/util/UiUtils.kt index f7e9b8d7..bbe6dcef 100644 --- a/app/src/main/java/jp/juggler/util/UiUtils.kt +++ b/app/src/main/java/jp/juggler/util/UiUtils.kt @@ -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() + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/page_column.xml b/app/src/main/res/layout/page_column.xml index 115ec315..f44079ba 100644 --- a/app/src/main/res/layout/page_column.xml +++ b/app/src/main/res/layout/page_column.xml @@ -154,6 +154,66 @@ android:id="@+id/llColumnSettingInside" > + + + + + + + + + + + + + + + + 更新履歴 タイムゾーン (アプリ再起動が必要) 端末のタイムゾーン + 追加のタグ:いずれかを含む (タグ指定に#をつけない) + 追加のタグ:全てを含む (タグ指定に#をつけない) + 追加のタグ:これらを除く (タグ指定に#をつけない) + か %1$s + と %1$s + (%1$s を除く) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 14fe7b25..27448b51 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -879,5 +879,11 @@ Releases Time zone (app restart required) Device time zone + Additional tags: any (tags without #) + Additional tags: all (tags without #) + Additional tags: none (tags without #) + or %1$s + and %1$s + without %1$s \ No newline at end of file