- ハッシュタグカラムの設定に「インスタンス内のみ」を追加

- アカウント追加時のタンス名の入力補完リストにPleromaタンスをいくつか追加した
- Pleromaタンスに投稿する時に文字数制限の表記をインスタンス情報から読む
This commit is contained in:
tateisu 2018-03-25 22:27:58 +09:00
parent 7d5a39994d
commit f5fb4da171
15 changed files with 217 additions and 34 deletions

1
.gitignore vendored
View File

@ -8,3 +8,4 @@
/captures
.externalNativeBuild
/rc
_Emoji/emoji-data/

@ -1 +0,0 @@
Subproject commit eb2246bb9263cba4e04e1497d635925ef59bd143

View File

@ -12,8 +12,8 @@ android {
minSdkVersion 21
targetSdkVersion 27
versionCode 231
versionName "2.3.1"
versionCode 232
versionName "2.3.2"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

View File

@ -52,6 +52,7 @@ import java.util.HashSet
import java.util.Locale
import jp.juggler.subwaytooter.api.entity.TootAttachment
import jp.juggler.subwaytooter.api.entity.TootInstance
import jp.juggler.subwaytooter.api.entity.TootStatus
import jp.juggler.subwaytooter.api.entity.parseItem
import jp.juggler.subwaytooter.dialog.*
@ -791,21 +792,64 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
etContent.commitContentListener = commitContentListener
}
private var lastInstanceTask : TootTaskRunner? = null
private fun getMaxCharCount() : Int {
val account = account
if(account != null && ! account.isPseudo) {
val info = account.instance
var lastTask = lastInstanceTask
// 情報がないか古いなら再取得
if(info == null || System.currentTimeMillis() - info.time_parse >= 300000L) {
// 同時に実行するタスクは1つまで
if(lastTask?.isActive != true) {
lastTask = TootTaskRunner(this, TootTaskRunner.PROGRESS_NONE)
lastInstanceTask = lastTask
lastTask.run(account, object : TootTask {
var newInfo : TootInstance? = null
override fun background(client : TootApiClient) : TootApiResult? {
val result = client.request("/api/v1/instance")
newInfo = TootParser(this@ActPost, account).instance(result?.jsonObject)
return result
}
override fun handleResult(result : TootApiResult?) {
if(isFinishing || isDestroyed) return
if(newInfo != null) {
account.instance = newInfo
updateTextCount()
}
}
})
}
}
if(info != null) {
val max = info.max_toot_chars
if(max != null && max > 0) return max
}
}
return 500
}
private fun updateTextCount() {
var length = 0
var s = EmojiDecoder.decodeShortCode(etContent.text.toString())
length += s.codePointCount(0, s.length)
s =
if(cbContentWarning.isChecked) EmojiDecoder.decodeShortCode(etContentWarning.text.toString()) else ""
s = if(cbContentWarning.isChecked)
EmojiDecoder.decodeShortCode(etContentWarning.text.toString())
else
""
length += s.codePointCount(0, s.length)
val max : Int
if(! cbEnquete.isChecked) {
max = 500
} else {
max = 350
var max = getMaxCharCount()
if(cbEnquete.isChecked) {
max -= 150 // フレニコ固有。500-150で350になる
for(et in list_etChoice) {
s = EmojiDecoder.decodeShortCode(et.text.toString())
length += s.codePointCount(0, s.length)

View File

@ -2,6 +2,7 @@ package jp.juggler.subwaytooter
import android.annotation.SuppressLint
import android.content.Context
import android.net.Uri
import android.os.AsyncTask
import android.os.SystemClock
import jp.juggler.subwaytooter.api.*
@ -40,12 +41,9 @@ class Column(
// ステータスのリストを返すAPI
private const val PATH_HOME = "/api/v1/timelines/home?limit=$READ_LIMIT"
private const val PATH_LOCAL = "/api/v1/timelines/public?limit=$READ_LIMIT&local=true"
private const val PATH_FEDERATE = "/api/v1/timelines/public?limit=$READ_LIMIT"
private const val PATH_FAVOURITES = "/api/v1/favourites?limit=$READ_LIMIT"
private const val PATH_ACCOUNT_STATUSES =
"/api/v1/accounts/%d/statuses?limit=$READ_LIMIT" // 1:account_id
private const val PATH_HASHTAG =
"/api/v1/timelines/tag/%s?limit=$READ_LIMIT" // 1: hashtag(url encoded)
private const val PATH_LIST_TL = "/api/v1/timelines/list/%s?limit=$READ_LIMIT"
// アカウントのリストを返すAPI
@ -92,6 +90,7 @@ class Column(
private const val KEY_DONT_AUTO_REFRESH = "dont_auto_refresh"
private const val KEY_HIDE_MEDIA_DEFAULT = "hide_media_default"
private const val KEY_SYSTEM_NOTIFICATION_NOT_RELATED = "system_notification_not_related"
private const val KEY_INSTANCE_LOCAL = "instance_local"
private const val KEY_ENABLE_SPEECH = "enable_speech"
@ -240,8 +239,13 @@ class Column(
TYPE_HOME, TYPE_NOTIFICATIONS -> "/api/v1/streaming/?stream=user"
TYPE_LOCAL -> "/api/v1/streaming/?stream=public:local"
TYPE_FEDERATE -> "/api/v1/streaming/?stream=public"
TYPE_HASHTAG -> "/api/v1/streaming/?stream=hashtag&tag=" + hashtag.encodePercent() // タグ先頭の#を含まない
TYPE_LIST_TL -> "/api/v1/streaming/?stream=list&list=" + profile_id.toString()
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()
// タグ先頭の#を含まない
}
else -> null
}
}
@ -267,6 +271,8 @@ class Column(
internal var dont_auto_refresh : Boolean = false
internal var hide_media_default : Boolean = false
internal var system_notification_not_related : Boolean = false
internal var instance_local : Boolean = false
var enable_speech : Boolean = false
internal var regex_text : String = ""
@ -445,6 +451,7 @@ class Column(
dont_auto_refresh = src.optBoolean(KEY_DONT_AUTO_REFRESH)
hide_media_default = src.optBoolean(KEY_HIDE_MEDIA_DEFAULT)
system_notification_not_related = src.optBoolean(KEY_SYSTEM_NOTIFICATION_NOT_RELATED)
instance_local = src.optBoolean(KEY_INSTANCE_LOCAL)
enable_speech = src.optBoolean(KEY_ENABLE_SPEECH)
@ -499,6 +506,7 @@ class Column(
dst.put(KEY_DONT_AUTO_REFRESH, dont_auto_refresh)
dst.put(KEY_HIDE_MEDIA_DEFAULT, hide_media_default)
dst.put(KEY_SYSTEM_NOTIFICATION_NOT_RELATED, system_notification_not_related)
dst.put(KEY_INSTANCE_LOCAL, instance_local)
dst.put(KEY_ENABLE_SPEECH, enable_speech)
@ -787,15 +795,15 @@ class Column(
// ステータスが削除された時に呼ばれる
fun onStatusRemoved(tl_host : String, status_id : Long) {
if(is_dispose.get() || bInitialLoading || bRefreshLoading ) return
if(is_dispose.get() || bInitialLoading || bRefreshLoading) return
if(tl_host.equals(access_info.host, ignoreCase = true)) {
val tmp_list = ArrayList<TimelineItem>(list_data.size)
for(o in list_data) {
if(o is TootStatus) {
if(status_id == o.id) continue
if(status_id == (o.reblog?.id ?: - 1L)) continue
}else if(o is TootNotification) {
} else if(o is TootNotification) {
val s = o.status
if(s != null) {
if(status_id == s.id) continue
@ -3623,22 +3631,22 @@ class Column(
}
private fun makePublicFederateUrl() : String {
return if(with_attachment) {
"$PATH_FEDERATE&only_media=true"
} else {
PATH_FEDERATE
}
val sb = StringBuilder("/api/v1/timelines/public?limit=")
.append(READ_LIMIT)
if(with_attachment) sb.append("&only_media=true")
return sb.toString()
}
private fun makeHashtagUrl(
hashtag : String // 先頭の#を含まない
) : String {
val path = String.format(Locale.JAPAN, PATH_HASHTAG, hashtag.encodePercent())
return if(with_attachment) {
"$path&only_media=true"
} else {
path
}
val sb = StringBuilder("/api/v1/timelines/tag/")
.append(hashtag.encodePercent())
.append("?limit=")
.append(READ_LIMIT)
if(with_attachment) sb.append("&only_media=true")
if(instance_local) sb.append("&local=true")
return sb.toString()
}
}

View File

@ -101,6 +101,7 @@ class ColumnViewHolder(
private val cbDontShowFavourite : CheckBox
private val cbDontShowReply : CheckBox
private val cbDontShowNormalToot : CheckBox
private val cbInstanceLocal : CheckBox
private val cbDontStreaming : CheckBox
private val cbDontAutoRefresh : CheckBox
private val cbHideMediaDefault : CheckBox
@ -232,6 +233,7 @@ class ColumnViewHolder(
cbDontShowFavourite = viewRoot.findViewById(R.id.cbDontShowFavourite)
cbDontShowReply = viewRoot.findViewById(R.id.cbDontShowReply)
cbDontShowNormalToot = viewRoot.findViewById(R.id.cbDontShowNormalToot)
cbInstanceLocal =viewRoot.findViewById(R.id.cbInstanceLocal)
cbDontStreaming = viewRoot.findViewById(R.id.cbDontStreaming)
cbDontAutoRefresh = viewRoot.findViewById(R.id.cbDontAutoRefresh)
cbHideMediaDefault = viewRoot.findViewById(R.id.cbHideMediaDefault)
@ -263,6 +265,7 @@ class ColumnViewHolder(
cbDontShowFavourite.setOnCheckedChangeListener(this)
cbDontShowReply.setOnCheckedChangeListener(this)
cbDontShowNormalToot.setOnCheckedChangeListener(this)
cbInstanceLocal.setOnCheckedChangeListener(this)
cbDontStreaming.setOnCheckedChangeListener(this)
cbDontAutoRefresh.setOnCheckedChangeListener(this)
cbHideMediaDefault.setOnCheckedChangeListener(this)
@ -432,6 +435,7 @@ class ColumnViewHolder(
cbDontShowFavourite.isChecked = column.dont_show_favourite
cbDontShowReply.isChecked = column.dont_show_reply
cbDontShowNormalToot.isChecked = column.dont_show_normal_toot
cbInstanceLocal.isChecked = column.instance_local
cbDontStreaming.isChecked = column.dont_streaming
cbDontAutoRefresh.isChecked = column.dont_auto_refresh
cbHideMediaDefault.isChecked = column.hide_media_default
@ -453,6 +457,9 @@ class ColumnViewHolder(
vg(cbDontShowFavourite, isNotificationColumn)
vg(cbDontShowFollow, isNotificationColumn)
vg(cbInstanceLocal, column.column_type == Column.TYPE_HASHTAG)
vg(cbDontStreaming, column.canStreaming())
vg(cbDontAutoRefresh, column.canAutoRefresh())
vg(cbHideMediaDefault, column.canNSFWDefault())
@ -740,6 +747,12 @@ class ColumnViewHolder(
column.startLoading()
}
R.id.cbInstanceLocal -> {
column.instance_local = isChecked
activity.app_state.saveColumnList()
column.startLoading()
}
R.id.cbDontStreaming -> {
column.dont_streaming = isChecked
activity.app_state.saveColumnList()

View File

@ -27,5 +27,6 @@ class TootParser(
fun notificationList(src : JSONArray?) = parseList(::TootNotification, this, src)
fun results(src : JSONObject?) = parseItem(::TootResults, this, src)
fun instance(src : JSONObject?) = parseItem(::TootInstance, this, src)
}

View File

@ -53,6 +53,7 @@ class TootTaskRunner(
private class MyTask(
private val runner : TootTaskRunner
) : AsyncTask<Void, Void, TootApiResult?>() {
var isActive :Boolean = true
override fun doInBackground(vararg voids : Void) : TootApiResult? {
val callback = runner.callback
@ -64,6 +65,7 @@ class TootTaskRunner(
}
override fun onPostExecute(result : TootApiResult?) {
isActive = false
runner.dismissProgress()
runner.callback?.handleResult(result)
}
@ -99,6 +101,9 @@ class TootTaskRunner(
this.task = MyTask(this)
}
val isActive : Boolean
get()= task.isActive
fun run(callback : TootTask) {
openProgress()
@ -214,6 +219,4 @@ class TootTaskRunner(
}
}
}

View File

@ -3,9 +3,13 @@ package jp.juggler.subwaytooter.api.entity
import jp.juggler.subwaytooter.api.TootParser
import jp.juggler.subwaytooter.util.*
import org.json.JSONObject
import java.util.regex.Pattern
class TootInstance(parser:TootParser,src : JSONObject) {
companion object {
val rePleroma = Pattern.compile("\\bpleroma")
}
// いつ取得したか(内部利用)
var time_parse : Long = System.currentTimeMillis()
@ -37,6 +41,16 @@ class TootInstance(parser:TootParser,src : JSONObject) {
val contact_account : TootAccount?
// (Pleroma only) トゥートの最大文字数
val max_toot_chars : Int?
// インスタンスの種別
enum class InstanceType{
Mastodon,
Pleroma
}
val instanceType : InstanceType
// XXX: urls をパースしてない。使ってないから…
init {
@ -49,6 +63,13 @@ class TootInstance(parser:TootParser,src : JSONObject) {
this.stats = parseItem(::Stats, src.optJSONObject("stats"))
this.thumbnail = src.parseString("thumbnail")
this.max_toot_chars = src.parseInt("max_toot_chars")
this.instanceType = when{
rePleroma.matcher(version ?:"").find() -> InstanceType.Pleroma
else->InstanceType.Mastodon
}
languages = src.optJSONArray("languages")?.toStringArrayList()
val parser2 = TootParser(
@ -74,7 +95,6 @@ class TootInstance(parser:TootParser,src : JSONObject) {
}
fun isEnoughVersion(check : VersionString) : Boolean {
if(decoded_version.isEmpty || check.isEmpty) return false
val i = VersionString.compare(decoded_version, check)
return i >= 0

View File

@ -420,9 +420,9 @@ fun StringBuilder.appendHex2(value : Int) : StringBuilder {
return this
}
fun String.optInt() : Int? {
fun String?.optInt() : Int? {
return try {
this.toInt(10)
this?.toInt(10)
} catch(ignored : Throwable) {
null
}
@ -623,6 +623,36 @@ fun JSONObject.parseLong(key : String) : Long? {
}
}
fun JSONObject.parseInt(key : String) : Int? {
val o = this.opt(key)
return when(o) {
is Int -> return o
is Number -> return try{
o.toInt()
}catch(ignored:NumberFormatException){
null
}
is String -> {
if(o.indexOf('.') == - 1 && o.indexOf(',') == - 1) {
try {
return o.toInt(10)
} catch(ignored : NumberFormatException) {
}
}
try {
o.toDouble().toInt()
} catch(ignored : NumberFormatException) {
null
}
}
else -> null // may null or JSONObject.NULL or object,array,boolean
}
}
////////////////////////////////////////////////////////////////////
// Bundle

View File

@ -190,6 +190,14 @@
android:layout_height="wrap_content"
android:text="@string/dont_show_normal_toot"
/>
<CheckBox
android:id="@+id/cbInstanceLocal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/instance_local"
/>
<CheckBox
android:id="@+id/cbDontStreaming"
android:layout_width="match_parent"

View File

@ -1,10 +1,12 @@
15m.icolectiva.org
1oku.net
3.distsn.org
3.nu
39sounds.net
3dcgdon.net
4com.jp
575don.club
68kami.ga
7144.party
765ml.com
7nw.eu
@ -57,6 +59,7 @@ banthamilk.blue
bapp.me
baraag.net
bark.zer0xff.com
bash-street-boys-and-girls.com
basstdn.jp
bcn-users.degica.com
beerbeer.tokyo
@ -64,6 +67,7 @@ betterletter.io
bibeogaem.zone
big.tuxme.net
bigdoinks.online
bikeshed.party
birdsite.link
bitcoinadon.social
biwakodon.com
@ -71,6 +75,7 @@ blackice.online
bldon.net
blogs.jp
bloodandthunderleviathan.herokuapp.com
blovice.bahnhof.cz
bms.stoicsounds.jp
bne.social
boitam.eu
@ -123,7 +128,9 @@ cmu.party
cmx.im
cn.tootist.net
coales.co
cocoronavi.com
cocoronavi.net
cofe.social
comm.cx
compass-community.f5.si
computerfairi.es
@ -134,6 +141,7 @@ cosi.town
cosp.la
counter.social
creativity.cafe
creatodon.online
currydon.com
cuttlefi.sh
cybr.es
@ -188,6 +196,7 @@ ecurie.social
edge.mstdn.jp
edgefire.sytes.net
ediot.social
edolas.world
eigadon.net
eizodon.jp
ekimemo.info
@ -227,6 +236,7 @@ fo0bar.org
foodon.jp
foresdon.jp
forexdon.org
forum.ama.ne.jp
forum.manga.tokyo
fosstodon.org
fr.osm.social
@ -236,6 +246,7 @@ freeradical.zone
friends.nico
friends.tennis
friloux.me
frogtown.club
from.komic.eu
frontend.social
frootmig.net
@ -372,6 +383,7 @@ kalebporter.club
kancolle-yokosuka.xyz
kanmoku.net
kashiwadon.net
kawaiistu.moe
kawasaki-city.social
keiba.social
kemono-friends.masto.host
@ -414,6 +426,7 @@ lethar.gy
lfsr.net
lgbt.io
libertarian.chat
libertarianism.club
lilydon.com
linuxrocks.online
livers.jp
@ -1067,6 +1080,7 @@ nishinomiya.in.net
nittc.tokyo
niu.moe
niwatoriman.me
nixeneko.info
nkmr.paoru.jp
noagendasocial.com
nobody.social
@ -1106,17 +1120,20 @@ ostatus.axross.io
ostatus.blessedgeeks.jp
ostatus.blessedgeeks.org
ostatus.ikeji.ma
ostatus.ikeji.ma
ostatus.isidai.com
ostatus.noviiro.me
ostatus.renken.is
ostatus.shade3d.jp
ostatus.taiyolab.com
ostatus.yajamon.xyz
otajo.tk
otajodon.com
otogamer.me
otoya.space
oulipo.social
p-worldon.com
p.kokolor.es
pachi.house
pachyder.me
pao.moe
@ -1135,7 +1152,27 @@ pkkm.me
planet.moe
plasticmodels.tokyo
playvicious.social
ple.ggtea.org
pleasehug.me
plero.ma
pleroma.7nw.eu
pleroma.arcseconds.net
pleroma.dereferenced.org
pleroma.firechicken.net
pleroma.gdgd.jp.net
pleroma.gza.jp
pleroma.iro-iro.xyz
pleroma.knzk.me
pleroma.miniwa.moe
pleroma.nakanod.net
pleroma.nakayoshi.tk
pleroma.net.ru
pleroma.playground.ws
pleroma.raramagi.ga
pleroma.rhinoworks.info
pleroma.soykaf.com
pleroma.thog.eu
plrm.moe.hm
pnw.social
pochi46.com
podcast.style
@ -1211,6 +1248,7 @@ share.elouworld.org
shelter.moe
shigadon.com
shigezen.com
shigusegubu.club
shigyoudon.jp
shimaidon.net
shimokita.social
@ -1226,6 +1264,10 @@ slime.global
sn.angry.im
snabeltann.no
sns.gdgd.jp.net
soc.canned-death.us
soc.flyingcube.tech
soc.freedombone.net
soc.h4x.group
soc.ialis.me
social.0day.agency
social.48bin.net
@ -1238,6 +1280,7 @@ social.apreslanu.it
social.arbleizez.bzh
social.arnip.org
social.atypique.net
social.aura.ovo.run
social.backtick.town
social.ballpointcarrot.net
social.bluecore.net
@ -1246,6 +1289,7 @@ social.bzh
social.celabs.com
social.chilliet.eu
social.chinwag.im
social.cofe.space
social.conglomer.net
social.coop
social.csswg.org
@ -1260,9 +1304,11 @@ social.gemuplay.com
social.gestaltzerfall.net
social.glados.ch
social.guimik.fr
social.hacktivis.me
social.homunyan.com
social.hoyle.me.uk
social.hyuki.net
social.i2p.rocks
social.imchip.be
social.imirhil.fr
social.infranix.eu
@ -1291,6 +1337,7 @@ social.nah.re
social.nasqueron.org
social.nassi.me
social.nervestaple.com
social.netzkombinat.su
social.noff.co
social.nofftopia.com
social.noisyspot.jp
@ -1308,6 +1355,7 @@ social.pueseso.club
social.qunagi.net
social.rbs.io
social.reekynet.com
social.sakamoto.gq
social.sitedethib.com
social.snargol.com
social.spiwit.net
@ -1333,6 +1381,7 @@ socialtuna.net
society.oftrolls.com
solo-outdon.club
soscet.network
sozial.derguhl.de
spacerock.jp
spanner.works
spod.ca
@ -1374,6 +1423,7 @@ tekkadon.manimani.cc
tenforward.social
teo.taiha.net
tescher.me
testing.pleroma.lol
tetsugaku.place
tgp.jp
the.wired.sehol.se
@ -1448,6 +1498,7 @@ ukrainian.social
uldhaar.dk
umastodon.jp
un.lobi.to
unconfigu.red
under-bank.blue
unique-inet.tokyo
unityjp-mastodon.tokyo
@ -1480,6 +1531,7 @@ webdevnerds.tech
weebs.moe
wellness.so
whiplashprinciples.club
whispr.hoenn.me
witches.town
wizfox.jp
wogan.im
@ -1493,6 +1545,7 @@ www.mikuappend.com
www.mofgao.space
www.mstddntfdn.online
www.nekotodon.com
www.pptdn.jp
www.seiyu-mstdn.club
wxw.moe
x0r.be

View File

@ -629,8 +629,9 @@
<string name="changed">changed.</string>
<string name="thumbnails_arrange_vertically">Vertical arrange thumbnails (app restart required)</string>
<string name="instance_local">Instance local</string>
<!--<string name="abc_action_bar_home_description">Revenir à l\'accueil</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>-->
<!--<string name="abc_action_bar_home_subtitle_description_format">%1$s, %2$s, %3$s</string>-->
<!--<string name="abc_action_bar_up_description">Revenir en haut de la page</string>-->

View File

@ -911,5 +911,6 @@
<string name="changed">変更しました</string>
<string name="thumbnails_arrange_vertically">サムネイルを縦に並べる (アプリ再起動が必要)</string>
<string name="instance_local">インスタンス内のみ</string>
</resources>

View File

@ -616,4 +616,5 @@
<string name="fav_muted_user_long">Fav/Boost notification disabled users</string>
<string name="changed">changed.</string>
<string name="thumbnails_arrange_vertically">Vertical arrange thumbnails (app restart required)</string>
<string name="instance_local">Instance local</string>
</resources>