From 4c43e807b123c5d8c33ff9b1a37d1b64c7d8f154 Mon Sep 17 00:00:00 2001 From: tateisu Date: Tue, 16 Nov 2021 22:05:08 +0900 Subject: [PATCH] =?UTF-8?q?=E3=82=B5=E3=82=A4=E3=83=89=E3=83=A1=E3=83=8B?= =?UTF-8?q?=E3=83=A5=E3=83=BC=E3=81=AB=E3=80=8CURL=E3=81=8B=E3=82=89?= =?UTF-8?q?=E3=82=AB=E3=83=A9=E3=83=A0=E3=82=92=E9=96=8B=E3=81=8F=E3=80=8D?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../juggler/subwaytooter/action/Action_App.kt | 16 +++ .../subwaytooter/actmain/ActMainIntent.kt | 25 +++-- .../subwaytooter/actmain/SideMenuAdapter.kt | 6 +- .../juggler/subwaytooter/dialog/DlgOpenUrl.kt | 101 ++++++++++++++++++ app/src/main/res/drawable/ic_paste.xml | 10 ++ app/src/main/res/layout/dlg_open_url.xml | 71 ++++++++++++ app/src/main/res/values-ja/strings.xml | 4 + app/src/main/res/values/strings.xml | 4 + 8 files changed, 227 insertions(+), 10 deletions(-) create mode 100644 app/src/main/java/jp/juggler/subwaytooter/dialog/DlgOpenUrl.kt create mode 100644 app/src/main/res/drawable/ic_paste.xml create mode 100644 app/src/main/res/layout/dlg_open_url.xml diff --git a/app/src/main/java/jp/juggler/subwaytooter/action/Action_App.kt b/app/src/main/java/jp/juggler/subwaytooter/action/Action_App.kt index 664dfca1..50337bda 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/action/Action_App.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/action/Action_App.kt @@ -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) + } + } +} diff --git a/app/src/main/java/jp/juggler/subwaytooter/actmain/ActMainIntent.kt b/app/src/main/java/jp/juggler/subwaytooter/actmain/ActMainIntent.kt index 7b7fed06..c2c6d9ed 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/actmain/ActMainIntent.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/actmain/ActMainIntent.kt @@ -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) { @@ -176,8 +177,8 @@ private fun ActMain.handleNotificationClick(uri: Uri, dataIdString: String) { val columnList = appState.columnList val column = columnList.firstOrNull { it.type == ColumnType.NOTIFICATIONS && - it.accessInfo == account && - !it.systemNotificationNotRelated + it.accessInfo == account && + !it.systemNotificationNotRelated }?.also { scrollToColumn(columnList.indexOf(it)) } ?: addColumn( @@ -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 diff --git a/app/src/main/java/jp/juggler/subwaytooter/actmain/SideMenuAdapter.kt b/app/src/main/java/jp/juggler/subwaytooter/actmain/SideMenuAdapter.kt index 2af0ad3c..31350453 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/actmain/SideMenuAdapter.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/actmain/SideMenuAdapter.kt @@ -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() diff --git a/app/src/main/java/jp/juggler/subwaytooter/dialog/DlgOpenUrl.kt b/app/src/main/java/jp/juggler/subwaytooter/dialog/DlgOpenUrl.kt new file mode 100644 index 00000000..a10d457f --- /dev/null +++ b/app/src/main/java/jp/juggler/subwaytooter/dialog/DlgOpenUrl.kt @@ -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 + } +} diff --git a/app/src/main/res/drawable/ic_paste.xml b/app/src/main/res/drawable/ic_paste.xml new file mode 100644 index 00000000..2d8f09a1 --- /dev/null +++ b/app/src/main/res/drawable/ic_paste.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/layout/dlg_open_url.xml b/app/src/main/res/layout/dlg_open_url.xml new file mode 100644 index 00000000..9da8e347 --- /dev/null +++ b/app/src/main/res/layout/dlg_open_url.xml @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + +