サイドメニューに「URLからカラムを開く」を追加
This commit is contained in:
parent
629cb1df9b
commit
4c43e807b1
|
@ -1,12 +1,16 @@
|
|||
package jp.juggler.subwaytooter.action
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.net.Uri
|
||||
import jp.juggler.subwaytooter.ActColumnList
|
||||
import jp.juggler.subwaytooter.ActMain
|
||||
import jp.juggler.subwaytooter.R
|
||||
import jp.juggler.subwaytooter.actmain.currentColumn
|
||||
import jp.juggler.subwaytooter.actmain.handleOtherUri
|
||||
import jp.juggler.subwaytooter.api.entity.TootApplication
|
||||
import jp.juggler.subwaytooter.dialog.DlgOpenUrl
|
||||
import jp.juggler.subwaytooter.table.MutedApp
|
||||
import jp.juggler.util.dismissSafe
|
||||
import jp.juggler.util.showToast
|
||||
|
||||
// カラム一覧を開く
|
||||
|
@ -33,3 +37,15 @@ fun ActMain.appMute(
|
|||
appState.onMuteUpdated()
|
||||
showToast(false, R.string.app_was_muted)
|
||||
}
|
||||
|
||||
fun ActMain.openColumnFromUrl() {
|
||||
DlgOpenUrl.show(this) { dialog, url ->
|
||||
try {
|
||||
if (handleOtherUri(Uri.parse(url))) {
|
||||
dialog.dismissSafe()
|
||||
}
|
||||
} catch (ex: Throwable) {
|
||||
showToast(ex, R.string.url_parse_failed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ fun ActMain.handleIntentUri(uri: Uri) {
|
|||
}
|
||||
}
|
||||
|
||||
private fun ActMain.handleOtherUri(uri: Uri) {
|
||||
fun ActMain.handleOtherUri(uri: Uri): Boolean {
|
||||
val url = uri.toString()
|
||||
|
||||
url.findStatusIdFromUrl()?.let { statusInfo ->
|
||||
|
@ -55,7 +55,7 @@ private fun ActMain.handleOtherUri(uri: Uri) {
|
|||
statusInfo.host,
|
||||
statusInfo.statusId
|
||||
)
|
||||
return
|
||||
return true
|
||||
}
|
||||
|
||||
TootAccount.reAccountUrl.matcher(url).takeIf { it.find() }?.let { m ->
|
||||
|
@ -80,7 +80,7 @@ private fun ActMain.handleOtherUri(uri: Uri) {
|
|||
userUrl = url,
|
||||
)
|
||||
}
|
||||
return
|
||||
return true
|
||||
}
|
||||
|
||||
TootAccount.reAccountUrl2.matcher(url).takeIf { it.find() }?.let { m ->
|
||||
|
@ -93,7 +93,7 @@ private fun ActMain.handleOtherUri(uri: Uri) {
|
|||
acct = Acct.parse(user, host),
|
||||
userUrl = url,
|
||||
)
|
||||
return
|
||||
return true
|
||||
}
|
||||
|
||||
// このアプリでは処理できないURLだった
|
||||
|
@ -139,7 +139,7 @@ private fun ActMain.handleOtherUri(uri: Uri) {
|
|||
|
||||
// 指定した選択肢でチューザーを作成して開く
|
||||
startActivity(chooser)
|
||||
return
|
||||
return true
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
|
@ -149,6 +149,7 @@ private fun ActMain.handleOtherUri(uri: Uri) {
|
|||
.setMessage(errorMessage)
|
||||
.setPositiveButton(R.string.close, null)
|
||||
.show()
|
||||
return false
|
||||
}
|
||||
|
||||
private fun ActMain.handleCustomSchemaUri(uri: Uri) {
|
||||
|
@ -257,7 +258,9 @@ private fun ActMain.handleOAuth2Callback(uri: Uri) {
|
|||
val error = uri.getQueryParameter("error")
|
||||
val errorDescription = uri.getQueryParameter("error_description")
|
||||
if (error != null || errorDescription != null) {
|
||||
return@runApiTask TootApiResult(errorDescription.notBlank() ?: error.notBlank() ?: "?")
|
||||
return@runApiTask TootApiResult(
|
||||
errorDescription.notBlank() ?: error.notBlank() ?: "?"
|
||||
)
|
||||
}
|
||||
|
||||
// subwaytooter://oauth(\d*)/
|
||||
|
@ -364,7 +367,11 @@ fun ActMain.afterAccountVerify(
|
|||
return false
|
||||
}
|
||||
|
||||
private fun ActMain.afterAccessTokenUpdate(ta: TootAccount, sa: SavedAccount, tokenInfo: JsonObject?): Boolean {
|
||||
private fun ActMain.afterAccessTokenUpdate(
|
||||
ta: TootAccount,
|
||||
sa: SavedAccount,
|
||||
tokenInfo: JsonObject?
|
||||
): Boolean {
|
||||
if (sa.username != ta.username) {
|
||||
showToast(true, R.string.user_name_not_match)
|
||||
return false
|
||||
|
|
|
@ -223,6 +223,10 @@ class SideMenuAdapter(
|
|||
closeColumnAll()
|
||||
},
|
||||
|
||||
Item(icon = R.drawable.ic_paste, title = R.string.open_column_from_url) {
|
||||
openColumnFromUrl()
|
||||
},
|
||||
|
||||
Item(icon = R.drawable.ic_home, title = R.string.home) {
|
||||
timeline(defaultInsertPosition, ColumnType.HOME)
|
||||
},
|
||||
|
@ -459,7 +463,7 @@ class SideMenuAdapter(
|
|||
var tz = TimeZone.getDefault()
|
||||
val tzId = PrefS.spTimeZone()
|
||||
if (tzId.isBlank()) {
|
||||
return tz.displayName +"("+context.getString(R.string.device_timezone)+")"
|
||||
return tz.displayName + "(" + context.getString(R.string.device_timezone) + ")"
|
||||
}
|
||||
tz = TimeZone.getTimeZone(tzId)
|
||||
var offset = tz.rawOffset.toLong()
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
package jp.juggler.subwaytooter.dialog
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Dialog
|
||||
import android.content.ClipboardManager
|
||||
import android.view.WindowManager
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.widget.EditText
|
||||
import androidx.core.view.postDelayed
|
||||
import jp.juggler.subwaytooter.R
|
||||
import jp.juggler.subwaytooter.databinding.DlgOpenUrlBinding
|
||||
import jp.juggler.util.LogCategory
|
||||
import jp.juggler.util.isEnabledAlpha
|
||||
import jp.juggler.util.showToast
|
||||
import jp.juggler.util.systemService
|
||||
|
||||
object DlgOpenUrl {
|
||||
private val log = LogCategory("DlgOpenUrl")
|
||||
|
||||
fun show(
|
||||
activity: Activity,
|
||||
onEmptyError: () -> Unit = { activity.showToast(false, R.string.url_empty) },
|
||||
onOK: (Dialog, String) -> Unit
|
||||
) {
|
||||
|
||||
val allowEmpty = false
|
||||
|
||||
val clipboard: ClipboardManager? = systemService(activity)
|
||||
val viewBinding = DlgOpenUrlBinding.inflate(activity.layoutInflater)
|
||||
|
||||
viewBinding.etInput.setOnEditorActionListener { _, actionId, _ ->
|
||||
if (actionId == EditorInfo.IME_ACTION_DONE) {
|
||||
viewBinding.btnOk.performClick()
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
val dialog = Dialog(activity)
|
||||
dialog.setContentView(viewBinding.root)
|
||||
viewBinding.btnCancel.setOnClickListener { dialog.cancel() }
|
||||
viewBinding.btnPaste.setOnClickListener { pasteTo(clipboard, viewBinding.etInput) }
|
||||
viewBinding.btnOk.setOnClickListener {
|
||||
val token = viewBinding.etInput.text.toString().trim { it <= ' ' }
|
||||
if (token.isEmpty() && !allowEmpty) {
|
||||
onEmptyError()
|
||||
} else {
|
||||
onOK(dialog, token)
|
||||
}
|
||||
}
|
||||
|
||||
dialog.window?.setLayout(
|
||||
WindowManager.LayoutParams.MATCH_PARENT,
|
||||
WindowManager.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
|
||||
val clipboardListener = ClipboardManager.OnPrimaryClipChangedListener {
|
||||
showPasteButton(clipboard, viewBinding)
|
||||
}
|
||||
clipboard?.addPrimaryClipChangedListener(clipboardListener)
|
||||
dialog.setOnDismissListener {
|
||||
clipboard?.removePrimaryClipChangedListener(clipboardListener)
|
||||
}
|
||||
viewBinding.root.postDelayed(100L) {
|
||||
showPasteButton(clipboard, viewBinding)
|
||||
pasteTo(clipboard, viewBinding.etInput)
|
||||
}
|
||||
dialog.show()
|
||||
}
|
||||
|
||||
private fun showPasteButton(clipboard: ClipboardManager?, viewBinding: DlgOpenUrlBinding) {
|
||||
viewBinding.btnPaste.isEnabledAlpha = when {
|
||||
clipboard == null -> false
|
||||
!clipboard.hasPrimaryClip() -> false
|
||||
clipboard.primaryClipDescription?.hasMimeType("text/plain") != true -> false
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
|
||||
private fun pasteTo(clipboard: ClipboardManager?, et: EditText) {
|
||||
val text = clipboard?.getUrlFromClipboard()
|
||||
?: return
|
||||
val ss = et.selectionStart
|
||||
val se = et.selectionEnd
|
||||
et.text.replace(ss, se, text)
|
||||
et.setSelection(ss, ss + text.length)
|
||||
}
|
||||
|
||||
private fun ClipboardManager.getUrlFromClipboard(): String? {
|
||||
try {
|
||||
val item = primaryClip?.getItemAt(0)
|
||||
item?.uri?.toString()?.let { return it }
|
||||
item?.text?.toString()?.let { return it }
|
||||
log.w("clip has nor uri or text.")
|
||||
} catch (ex: Throwable) {
|
||||
log.w(ex, "getUrlFromClipboard failed.")
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M19,2h-4.18C14.4,0.84 13.3,0 12,0c-1.3,0 -2.4,0.84 -2.82,2L5,2c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,4c0,-1.1 -0.9,-2 -2,-2zM12,2c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM19,20L5,20L5,4h2v3h10L17,4h2v16z"/>
|
||||
</vector>
|
|
@ -0,0 +1,71 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="12dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_weight="1"
|
||||
android:labelFor="@+id/etInput"
|
||||
android:text="@string/url_of_user_or_status"
|
||||
tools:ignore="LabelFor" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/btnPaste"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="4dp"
|
||||
android:contentDescription="@android:string/paste"
|
||||
android:src="@drawable/ic_paste"
|
||||
app:tint="?attr/colorImageButton" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etInput"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:imeOptions="actionDone"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="text" />
|
||||
|
||||
|
||||
<LinearLayout
|
||||
style="?android:attr/buttonBarStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnCancel"
|
||||
style="?android:attr/buttonBarButtonStyle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/cancel" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnOk"
|
||||
style="?android:attr/buttonBarButtonStyle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/ok" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
|
@ -1102,4 +1102,8 @@
|
|||
<string name="show_media_description">添付メディアの説明文を表示する</string>
|
||||
<string name="background_pattern">背景パターン</string>
|
||||
<string name="use_twemoji_emoji">Twemoji絵文字を使う</string>
|
||||
<string name="open_column_from_url">URLからカラムを開く</string>
|
||||
<string name="url_empty">ユーザや投稿のURLを指定してください。</string>
|
||||
<string name="url_parse_failed">URLの指定が変です</string>
|
||||
<string name="url_of_user_or_status">ユーザや投稿のURL</string>
|
||||
</resources>
|
||||
|
|
|
@ -1113,4 +1113,8 @@
|
|||
<string name="show_media_description">Show media description</string>
|
||||
<string name="background_pattern">Background pattern</string>
|
||||
<string name="use_twemoji_emoji">Use Twemoji emoji</string>
|
||||
<string name="open_column_from_url">Open column from URL…</string>
|
||||
<string name="url_empty">please input URL of user or status.</string>
|
||||
<string name="url_of_user_or_status">URL of user or status</string>
|
||||
<string name="url_parse_failed">parse error.</string>
|
||||
</resources>
|
||||
|
|
Loading…
Reference in New Issue