diff --git a/app/src/main/java/org/b3log/siyuan/S.kt b/app/src/main/java/org/b3log/siyuan/S.kt index db4b2ef4..55ba6209 100644 --- a/app/src/main/java/org/b3log/siyuan/S.kt +++ b/app/src/main/java/org/b3log/siyuan/S.kt @@ -1,6 +1,7 @@ @file:Suppress("CompositionLocalNaming", "CompositionLocalNaming") package org.b3log.siyuan +import android.net.Uri import androidx.compose.runtime.compositionLocalOf import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp @@ -81,4 +82,13 @@ object S { val Card_bgColor_green1 = compositionLocalOf { Color(0x9F237A58) } val Card_bgColor_gold1 = compositionLocalOf { Color(0x88997758) } } + + data class UriMatchPattern(val scheme: String, val host: String) + + fun isUriMatched(uri: Uri?, pattern: UriMatchPattern): Boolean { + return uri?.scheme == pattern.scheme && uri.host == pattern.host + } + val case_ld246_1 = UriMatchPattern("https", "ssl.ptlogin2.qq.com") // 目前必须将默认打开方式浏览器设置为汐洛,否则QQ回调进不来 + val case_ld246_2 = UriMatchPattern("https", "ld246.com") + val case_github_1 = UriMatchPattern("https", "github.com") } \ No newline at end of file diff --git a/app/src/main/java/org/b3log/siyuan/Us.kt b/app/src/main/java/org/b3log/siyuan/Us.kt index bd8aee15..6f0e8c9b 100644 --- a/app/src/main/java/org/b3log/siyuan/Us.kt +++ b/app/src/main/java/org/b3log/siyuan/Us.kt @@ -54,6 +54,7 @@ import java.io.File import java.io.FileInputStream import java.io.FileOutputStream import java.net.URLDecoder +import java.net.URLEncoder import java.security.MessageDigest import java.text.DecimalFormat import javax.crypto.Cipher @@ -65,13 +66,22 @@ import kotlin.math.sqrt object Us { - fun dropAndDecodeUrl(url: String, drop: String): String { - // 正则表达式匹配 "https://ld246.com/forward?goto=" 并去除 - val regex = drop.toRegex() - val withoutPrefix = regex.replace(url, "") - - // URL 解码 - return URLDecoder.decode(withoutPrefix, "UTF-8") + fun replaceScheme_deepDecode(url: String, old: String, new: String): String { + // 解码URL + var decodedUrl = URLDecoder.decode(url, "UTF-8") + // 替换scheme + decodedUrl = decodedUrl.replace(old, new) + var previousUrl: String + do { + previousUrl = decodedUrl + // 再次解码URL + decodedUrl = URLDecoder.decode(decodedUrl, "UTF-8") + } while (decodedUrl != previousUrl) + + return decodedUrl + } + fun replaceEncodeScheme(url: String, old: String, new: String): String { + return url.replace(URLEncoder.encode(old), URLEncoder.encode(new)) } fun parseAndDecodeUrl(url: String, regex: Regex): String { val decodedUrls = regex.findAll(url).map { matchResult -> diff --git a/app/src/main/java/org/b3log/siyuan/compose/HTML.kt b/app/src/main/java/org/b3log/siyuan/compose/HTML.kt index 103bb566..8d76941b 100644 --- a/app/src/main/java/org/b3log/siyuan/compose/HTML.kt +++ b/app/src/main/java/org/b3log/siyuan/compose/HTML.kt @@ -16,6 +16,7 @@ import android.util.Log import android.view.MotionEvent import android.view.View import android.widget.TextView +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.viewinterop.AndroidView @@ -42,7 +43,7 @@ fun HtmlText(html: String, modifier: Modifier = Modifier) { @Composable fun SelectableHtmlText(html: String, modifier: Modifier = Modifier) { AndroidView( - modifier = modifier, + modifier = modifier.fillMaxWidth(), factory = { context -> TextView(context).apply { // 允许长按复制文本,需放在前面 @@ -56,7 +57,7 @@ fun SelectableHtmlText(html: String, modifier: Modifier = Modifier) { }, update = { textView -> val _Html = Us.parseAndDecodeUrl(html, """['"]https://ld246.com/forward\?goto=([^'"]*)['"]""".toRegex()) - Log.i("HTML", _Html) +// Log.i("HTML", _Html) textView.text = HtmlCompat.fromHtml(_Html, HtmlCompat.FROM_HTML_MODE_COMPACT, null, MyTagHandler()) } ) diff --git a/app/src/main/java/org/b3log/siyuan/compose/components/TopBarWithGobackAndMenu.kt b/app/src/main/java/org/b3log/siyuan/compose/components/TopBarWithGobackAndMenu.kt index 2ee7f60b..e765607b 100644 --- a/app/src/main/java/org/b3log/siyuan/compose/components/TopBarWithGobackAndMenu.kt +++ b/app/src/main/java/org/b3log/siyuan/compose/components/TopBarWithGobackAndMenu.kt @@ -120,7 +120,7 @@ fun TopRightMenu( expanded = expanded, onDismissRequest = onDismiss ) { - if (uri != null) { + if (uri != null && !listOf("http","https","siyuan").contains(uri.scheme)) { DropdownMenuItem( text = { Text("复制") }, leadingIcon = { Icon(Icons.TwoTone.ContentCopy, contentDescription = null) }, diff --git a/app/src/main/java/org/b3log/siyuan/ld246/DC.kt b/app/src/main/java/org/b3log/siyuan/ld246/DC.kt index f75e936f..b42a4d60 100644 --- a/app/src/main/java/org/b3log/siyuan/ld246/DC.kt +++ b/app/src/main/java/org/b3log/siyuan/ld246/DC.kt @@ -1,11 +1,15 @@ package org.b3log.siyuan.ld246 +data class ld246_Response_NoData( + val msg: String, + val random: String, + val code: Int, +) data class ld246_Response( val msg: String, val random: String, val code: Int, val data: ld246_Response_Data, - // 其他可能存在的字段可以省略 ) data class ld246_Response_Data( val notifications: List, // 理想的字段,其实不存在 @@ -16,7 +20,6 @@ data class ld246_Response_Data( val followingNotifications: List, // 关注 val pagination: Pagination, val unreadNotificationCount: UnreadNotificationCount, - // 其他可能存在的字段可以省略 ) data class ld246_Response_Data_Notification( val dataId: String, @@ -26,17 +29,14 @@ data class ld246_Response_Data_Notification( val hasRead: Boolean, val title: String, val content: String, - // 其他可能存在的字段可以省略 ) data class Pagination( val paginationPageCount: Int, val paginationPageNums: List - // 其他可能存在的字段可以省略 ) data class UnreadNotificationCount( val unreadReviewNotificationCnt: Int, val unreadNotificationCnt: Int, - // 其他可能存在的字段可以省略 ) diff --git a/app/src/main/java/org/b3log/siyuan/ld246/Home.kt b/app/src/main/java/org/b3log/siyuan/ld246/Home.kt index 0991952a..794acf0a 100644 --- a/app/src/main/java/org/b3log/siyuan/ld246/Home.kt +++ b/app/src/main/java/org/b3log/siyuan/ld246/Home.kt @@ -2,13 +2,16 @@ package org.b3log.siyuan.ld246 import android.annotation.SuppressLint import android.app.Activity -import android.content.ActivityNotFoundException import android.content.Intent -import android.content.res.Configuration +import android.net.Uri +import android.os.Build import android.os.Bundle +import android.provider.MediaStore import android.util.Base64 import android.util.Log import android.view.View +import android.webkit.CookieManager +import android.webkit.CookieSyncManager import android.webkit.WebResourceRequest import android.webkit.WebView import android.webkit.WebViewClient @@ -32,6 +35,7 @@ import androidx.compose.material.icons.automirrored.twotone.Article import androidx.compose.material.icons.automirrored.twotone.Reply import androidx.compose.material.icons.twotone.Attribution import androidx.compose.material.icons.twotone.CenterFocusWeak +import androidx.compose.material.icons.twotone.Cookie import androidx.compose.material.icons.twotone.OpenInBrowser import androidx.compose.material.icons.twotone.Quickreply import androidx.compose.material.icons.twotone.Swipe @@ -60,7 +64,6 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshotFlow @@ -68,9 +71,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.nestedscroll.nestedScroll -import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.font.FontWeight @@ -78,6 +79,7 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import androidx.core.app.ActivityCompat import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope @@ -142,6 +144,12 @@ class HomeActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + val intent = intent + val uri = intent.data + Log.i(TAG, "onCreate() invoked") + val scheme = uri?.scheme + val host = uri?.host + Log.d(TAG, "scheme: $scheme, host:$host") // 设置沉浸式通知栏 window.setDecorFitsSystemWindows(false) window.decorView.setOnApplyWindowInsetsListener { _, insets -> @@ -206,6 +214,14 @@ class HomeActivity : ComponentActivity() { ) } }) + + if (S.isUriMatched(uri, S.case_ld246_1) || S.isUriMatched( + uri, + S.case_ld246_2 + ) || S.isUriMatched(uri, S.case_github_1) + ) { + showFullScreenDialog(uri.toString()) + } } @SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") @@ -320,6 +336,14 @@ class HomeActivity : ComponentActivity() { } } ) + DropdownMenuItem( + text = { Text("清除 Cookie") }, + leadingIcon = { Icon(Icons.TwoTone.Cookie, contentDescription = null) }, + onClick = { + onDismiss() + showFullScreenDialog("action?=Logout") + } + ) DropdownMenuItem( text = { Text("链滴 API TOKEN") }, leadingIcon = { Icon(Icons.TwoTone.Token, contentDescription = null) }, @@ -398,17 +422,17 @@ class HomeActivity : ComponentActivity() { unselectedContentColor = unselectedContentColor, text = { Column(horizontalAlignment = Alignment.CenterHorizontally) { + Icon( + imageVector = titles_icons[index], + contentDescription = title + ) Text( text = title, maxLines = 2, overflow = TextOverflow.Ellipsis, - fontSize = 18.sp, + fontSize = 16.sp, fontWeight = FontWeight.Bold ) - Icon( - imageVector = titles_icons[index], - contentDescription = title - ) } } ) @@ -488,6 +512,7 @@ class HomeActivity : ComponentActivity() { }) { Row( modifier = Modifier + .fillMaxWidth() .clickable { val url = "https://${S.HOST_ld246}/member/${notification.authorName}" if (openUrlExternal) { @@ -516,7 +541,8 @@ class HomeActivity : ComponentActivity() { text = notification.title, fontSize = 15.sp, fontStyle = FontStyle.Italic, - fontWeight = FontWeight.Bold + fontWeight = FontWeight.Bold, + modifier = Modifier.fillMaxWidth() ) SelectableHtmlText(notification.content) } @@ -534,7 +560,7 @@ class HomeActivity : ComponentActivity() { @OptIn(ExperimentalMaterial3Api::class) private fun updateNotificationsMap(state: PullToRefreshState?) { - Log.e(TAG, "updateNotificationsMap() -> ${map[LockNoteType]} ") +// Log.e(TAG, "updateNotificationsMap() -> ${map[LockNoteType]} ") _notificationsMap.postValue(map[LockNoteType]) if (state != null) { if (state.isRefreshing) { @@ -588,18 +614,33 @@ class HomeActivity : ComponentActivity() { else -> listOf() } } - PopTip.show("<( ̄︶ ̄)↗[GO!]") - apiService.apiV2NotificationsMakeRead( - LockNoteType_EN, - token, - ua - ) + PopTip.show("<( ̄︶ ̄)↗[${response.code()}]") + val _mr: Call = + apiService.apiV2NotificationsMakeRead( + LockNoteType_EN, + token, + ua + ) + _mr.enqueue(object : Callback { + override fun onResponse( + p0: Call, + p1: Response + ) { + Log.i(TAG, "Make Read: ${p1}") + } + + override fun onFailure( + p0: Call, + p1: Throwable + ) { + Log.w(TAG, "Make Read: ${p1}") + } + }) } else { - Log.e(TAG, "onResponse: $response") +// Log.e(TAG, "onResponse: $response") handleErrorResponse(response) } updateNotificationsMap(state) - Log.e("MAPMAP 1", map.toString()) } override fun onFailure(call: Call, t: Throwable) { @@ -611,7 +652,6 @@ class HomeActivity : ComponentActivity() { }) } else { updateNotificationsMap(state) - Log.e("MAPMAP 2", map.toString()) } } catch (e: Exception) { // 处理错误 @@ -633,38 +673,84 @@ class HomeActivity : ComponentActivity() { UI(null) } + private fun handleUrlLoading(view: WebView, url: String): Boolean { + val _url = Us.replaceScheme_deepDecode(url, "googlechrome://", "slld246://") + val real_url = Us.replaceEncodeScheme(url, "googlechrome://", "slld246://") + Log.d(TAG, _url) + + if (_url.startsWith("mqq://") || _url.startsWith("wtloginmqq://") || _url.startsWith("sinaweibo://")) { + return try { + val intent = Intent(Intent.ACTION_VIEW, Uri.parse(real_url)) + ActivityCompat.startActivityForResult(view.context as Activity, intent, 1, null) + true + } catch (e: Exception) { + Log.e(TAG, e.toString()) + false + } + } else { + return false + } + } + + private fun webViewClient(): WebViewClient { + return object : WebViewClient() { + override fun shouldOverrideUrlLoading( + view: WebView, + request: WebResourceRequest + ): Boolean { + return handleUrlLoading(view, request.url.toString()) + } + } + } + + private fun clearWebViewCookies(webView: WebView?) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + CookieManager.getInstance().apply { + removeAllCookies { success -> + if (success) { + webView?.clearCache(true) + fullScreenDialog?.dismiss() + PopTip.show("<( ̄︶ ̄)↗[success]") + } else { + fullScreenDialog?.dismiss() + PopTip.show("  ̄へ ̄ [failed]") + } + } + } + } else { + CookieSyncManager.createInstance(this) + CookieManager.getInstance().apply { + removeAllCookie() + webView?.clearCache(true) + fullScreenDialog?.dismiss() + } + CookieSyncManager.getInstance().sync() + } + } + fun showFullScreenDialog(url: String) { + Log.w("showFullScreenDialog", url) if (fullScreenDialog == null) { - val _dialog = FullScreenDialog.build() - fullScreenDialog = _dialog - _dialog.apply { + fullScreenDialog = FullScreenDialog.build().apply { setDialogLifecycleCallback(object : DialogLifecycleCallback() { override fun onShow(dialog: FullScreenDialog?) { + when (url) { + "action?=Logout" -> { + dialog?.hide() // 隐藏处理过程 + val webView = + dialog?.getCustomView()?.findViewById(R.id.webView) + clearWebViewCookies(webView) + return + } + + else -> {} + } dialog?.setCustomView(object : OnBindView(R.layout.layout_full_screen) { override fun onBind(dialog: FullScreenDialog?, v: View) { val webView = v.findViewById(R.id.webView) - webView.webViewClient = object : WebViewClient() { - override fun shouldOverrideUrlLoading( - view: WebView, - request: WebResourceRequest - ): Boolean { - val _url = request.url.toString() - if (_url.startsWith("mqq://") || _url.startsWith("wtloginmqq://") || _url.startsWith( - "sinaweibo://" - ) - ) { - return try { - val intent = Intent(Intent.ACTION_VIEW, request.url) - view.context.startActivity(intent) - true - } catch (e: ActivityNotFoundException) { - false - } - } - return false - } - } + webView.webViewClient = webViewClient() + webView.loadUrl(url) val btnRefresh = v.findViewById(R.id.btnRefresh) btnRefresh.setOnClickListener { @@ -685,12 +771,13 @@ class HomeActivity : ComponentActivity() { } }) }.show() + } else { + fullScreenDialog?.let { dialog -> + val webView = dialog.getCustomView()?.findViewById(R.id.webView) + Log.w(fullScreenDialog.toString(), webView.toString()) + webView?.loadUrl(url) + dialog.refreshUI() + } } - - val webView = fullScreenDialog!!.getCustomView()?.findViewById(R.id.webView) - webView!!.loadUrl(url) - fullScreenDialog!!.show().refreshUI() } - - } diff --git a/app/src/main/java/org/b3log/siyuan/ld246/api/Notification.kt b/app/src/main/java/org/b3log/siyuan/ld246/api/Notification.kt index 9691cbb8..d1d7f2dd 100644 --- a/app/src/main/java/org/b3log/siyuan/ld246/api/Notification.kt +++ b/app/src/main/java/org/b3log/siyuan/ld246/api/Notification.kt @@ -1,6 +1,7 @@ package org.b3log.siyuan.ld246.api import org.b3log.siyuan.ld246.ld246_Response +import org.b3log.siyuan.ld246.ld246_Response_NoData import retrofit2.Call import retrofit2.http.GET import retrofit2.http.Header @@ -50,5 +51,5 @@ interface ApiServiceNotification { @Path("type") type: String, @Header("Authorization") Authorization: String?, @Header("User-Agent") UA: String?, - ): Call + ): Call } diff --git a/app/src/main/java/org/b3log/siyuan/producer/MainPro.kt b/app/src/main/java/org/b3log/siyuan/producer/MainPro.kt index 3c00322b..966747b8 100644 --- a/app/src/main/java/org/b3log/siyuan/producer/MainPro.kt +++ b/app/src/main/java/org/b3log/siyuan/producer/MainPro.kt @@ -79,6 +79,7 @@ import org.b3log.siyuan.compose.LockScreenOrientation import org.b3log.siyuan.compose.SelectableText import org.b3log.siyuan.compose.VideoButtons import org.b3log.siyuan.compose.components.CommonTopAppBar +import org.b3log.siyuan.ld246.HomeActivity import java.io.IOException @@ -94,6 +95,20 @@ class MainPro : ComponentActivity() { val intent = intent val uri = intent.data Log.i(TAG, "onCreate() invoked") + val scheme = uri?.scheme + val host = uri?.host + Log.d(TAG, "scheme: $scheme, host:$host") + if (S.isUriMatched(uri, S.case_ld246_1) || S.isUriMatched(uri, S.case_ld246_2) || S.isUriMatched(uri, S.case_github_1)) { + // 转发处理 + val homeIntent = Intent(this, HomeActivity::class.java) + homeIntent.data = uri // 将URI数据传递给HomeActivity + startActivity(homeIntent) + finish() // 如果不需要返回MainPro,可以在这里结束它 + } else { + // 如果不是特定的scheme和host,处理其他逻辑或直接结束 + // ... + } + // 设置沉浸式通知栏 window.setDecorFitsSystemWindows(false) window.decorView.setOnApplyWindowInsetsListener { _, insets -> diff --git a/app/src/main/res/layout/layout_full_screen.xml b/app/src/main/res/layout/layout_full_screen.xml index 5df19698..0811184a 100644 --- a/app/src/main/res/layout/layout_full_screen.xml +++ b/app/src/main/res/layout/layout_full_screen.xml @@ -1,18 +1,21 @@ - + android:layout_height="match_parent" + android:padding="1dp" + android:layout_marginTop="41dp"/> + android:padding="12dp">