2018-05-06 22:05:54 +02:00
|
|
|
/* Copyright 2018 Conny Duck
|
|
|
|
*
|
|
|
|
* This file is a part of Tusky.
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
|
|
|
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
|
|
|
|
* License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
|
|
|
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
|
|
|
* Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along with Tusky; if not,
|
|
|
|
* see <http://www.gnu.org/licenses>. */
|
|
|
|
|
|
|
|
package com.keylesspalace.tusky
|
|
|
|
|
2022-02-25 18:56:21 +01:00
|
|
|
import android.content.Context
|
2018-05-06 22:05:54 +02:00
|
|
|
import android.content.Intent
|
|
|
|
import android.os.Bundle
|
|
|
|
import android.view.View
|
|
|
|
import android.widget.LinearLayout
|
2019-10-11 17:51:47 +02:00
|
|
|
import android.widget.Toast
|
2019-09-22 08:18:44 +02:00
|
|
|
import androidx.annotation.VisibleForTesting
|
|
|
|
import androidx.lifecycle.Lifecycle
|
2021-05-16 19:53:27 +02:00
|
|
|
import autodispose2.androidx.lifecycle.AndroidLifecycleScopeProvider
|
|
|
|
import autodispose2.autoDispose
|
2019-09-22 08:18:44 +02:00
|
|
|
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
2022-01-11 19:55:17 +01:00
|
|
|
import com.keylesspalace.tusky.components.account.AccountActivity
|
2022-08-15 11:00:18 +02:00
|
|
|
import com.keylesspalace.tusky.components.viewthread.ViewThreadActivity
|
2018-05-06 22:05:54 +02:00
|
|
|
import com.keylesspalace.tusky.network.MastodonApi
|
2022-12-03 12:16:17 +01:00
|
|
|
import com.keylesspalace.tusky.util.looksLikeMastodonUrl
|
2022-02-25 18:56:21 +01:00
|
|
|
import com.keylesspalace.tusky.util.openLink
|
2021-05-16 19:53:27 +02:00
|
|
|
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
2018-05-08 19:15:10 +02:00
|
|
|
import javax.inject.Inject
|
2018-05-06 22:05:54 +02:00
|
|
|
|
|
|
|
/** this is the base class for all activities that open links
|
2022-11-09 19:32:39 +01:00
|
|
|
* links are checked against the api if they are mastodon links so they can be opened in Tusky
|
|
|
|
* Subclasses must have a bottom sheet with Id item_status_bottom_sheet in their layout hierarchy
|
2018-05-06 22:05:54 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
abstract class BottomSheetActivity : BaseActivity() {
|
|
|
|
|
|
|
|
lateinit var bottomSheet: BottomSheetBehavior<LinearLayout>
|
|
|
|
var searchUrl: String? = null
|
|
|
|
|
2018-05-08 19:15:10 +02:00
|
|
|
@Inject
|
|
|
|
lateinit var mastodonApi: MastodonApi
|
2018-05-06 22:05:54 +02:00
|
|
|
|
|
|
|
override fun onPostCreate(savedInstanceState: Bundle?) {
|
|
|
|
super.onPostCreate(savedInstanceState)
|
|
|
|
|
|
|
|
val bottomSheetLayout: LinearLayout = findViewById(R.id.item_status_bottom_sheet)
|
2019-09-22 08:18:44 +02:00
|
|
|
bottomSheet = BottomSheetBehavior.from(bottomSheetLayout)
|
|
|
|
bottomSheet.state = BottomSheetBehavior.STATE_HIDDEN
|
2019-10-22 21:18:20 +02:00
|
|
|
bottomSheet.addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
|
2019-09-22 08:18:44 +02:00
|
|
|
override fun onStateChanged(bottomSheet: View, newState: Int) {
|
|
|
|
if (newState == BottomSheetBehavior.STATE_HIDDEN) {
|
|
|
|
cancelActiveSearch()
|
2018-05-06 22:05:54 +02:00
|
|
|
}
|
2019-09-22 08:18:44 +02:00
|
|
|
}
|
2018-05-06 22:05:54 +02:00
|
|
|
|
2019-09-22 08:18:44 +02:00
|
|
|
override fun onSlide(bottomSheet: View, slideOffset: Float) {}
|
|
|
|
})
|
2018-05-06 22:05:54 +02:00
|
|
|
}
|
|
|
|
|
2019-10-11 17:51:47 +02:00
|
|
|
open fun viewUrl(url: String, lookupFallbackBehavior: PostLookupFallbackBehavior = PostLookupFallbackBehavior.OPEN_IN_BROWSER) {
|
2018-05-06 22:05:54 +02:00
|
|
|
if (!looksLikeMastodonUrl(url)) {
|
|
|
|
openLink(url)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-09-22 08:18:44 +02:00
|
|
|
mastodonApi.searchObservable(
|
2021-06-28 21:13:24 +02:00
|
|
|
query = url,
|
|
|
|
resolve = true
|
2019-09-22 08:18:44 +02:00
|
|
|
).observeOn(AndroidSchedulers.mainThread())
|
2021-06-28 21:13:24 +02:00
|
|
|
.autoDispose(AndroidLifecycleScopeProvider.from(this, Lifecycle.Event.ON_DESTROY))
|
|
|
|
.subscribe(
|
|
|
|
{ (accounts, statuses) ->
|
2019-09-22 08:18:44 +02:00
|
|
|
if (getCancelSearchRequested(url)) {
|
|
|
|
return@subscribe
|
2018-05-06 22:05:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
onEndSearch(url)
|
2019-09-22 08:18:44 +02:00
|
|
|
|
|
|
|
if (statuses.isNotEmpty()) {
|
|
|
|
viewThread(statuses[0].id, statuses[0].url)
|
|
|
|
return@subscribe
|
|
|
|
} else if (accounts.isNotEmpty()) {
|
|
|
|
viewAccount(accounts[0].id)
|
|
|
|
return@subscribe
|
|
|
|
}
|
|
|
|
|
2019-10-11 17:51:47 +02:00
|
|
|
performUrlFallbackAction(url, lookupFallbackBehavior)
|
2021-06-28 21:13:24 +02:00
|
|
|
},
|
|
|
|
{
|
2019-09-22 08:18:44 +02:00
|
|
|
if (!getCancelSearchRequested(url)) {
|
|
|
|
onEndSearch(url)
|
2019-10-11 17:51:47 +02:00
|
|
|
performUrlFallbackAction(url, lookupFallbackBehavior)
|
2019-09-22 08:18:44 +02:00
|
|
|
}
|
2021-06-28 21:13:24 +02:00
|
|
|
}
|
|
|
|
)
|
2019-09-22 08:18:44 +02:00
|
|
|
|
2018-05-06 22:05:54 +02:00
|
|
|
onBeginSearch(url)
|
|
|
|
}
|
|
|
|
|
2019-02-12 19:22:37 +01:00
|
|
|
open fun viewThread(statusId: String, url: String?) {
|
2018-05-06 22:05:54 +02:00
|
|
|
if (!isSearching()) {
|
|
|
|
val intent = Intent(this, ViewThreadActivity::class.java)
|
2019-02-12 19:22:37 +01:00
|
|
|
intent.putExtra("id", statusId)
|
|
|
|
intent.putExtra("url", url)
|
2018-07-31 21:25:25 +02:00
|
|
|
startActivityWithSlideInAnimation(intent)
|
2018-05-06 22:05:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
open fun viewAccount(id: String) {
|
2018-06-18 13:26:18 +02:00
|
|
|
val intent = AccountActivity.getIntent(this, id)
|
2018-07-31 21:25:25 +02:00
|
|
|
startActivityWithSlideInAnimation(intent)
|
2018-05-06 22:05:54 +02:00
|
|
|
}
|
|
|
|
|
2019-10-11 17:51:47 +02:00
|
|
|
protected open fun performUrlFallbackAction(url: String, fallbackBehavior: PostLookupFallbackBehavior) {
|
|
|
|
when (fallbackBehavior) {
|
|
|
|
PostLookupFallbackBehavior.OPEN_IN_BROWSER -> openLink(url)
|
|
|
|
PostLookupFallbackBehavior.DISPLAY_ERROR -> Toast.makeText(this, getString(R.string.post_lookup_error_format, url), Toast.LENGTH_SHORT).show()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-06 22:05:54 +02:00
|
|
|
@VisibleForTesting
|
|
|
|
fun onBeginSearch(url: String) {
|
|
|
|
searchUrl = url
|
|
|
|
showQuerySheet()
|
|
|
|
}
|
|
|
|
|
|
|
|
@VisibleForTesting
|
|
|
|
fun getCancelSearchRequested(url: String): Boolean {
|
|
|
|
return url != searchUrl
|
|
|
|
}
|
|
|
|
|
|
|
|
@VisibleForTesting
|
|
|
|
fun isSearching(): Boolean {
|
|
|
|
return searchUrl != null
|
|
|
|
}
|
|
|
|
|
|
|
|
@VisibleForTesting
|
|
|
|
fun onEndSearch(url: String?) {
|
|
|
|
if (url == searchUrl) {
|
|
|
|
// Don't clear query if there's no match,
|
|
|
|
// since we might just now be getting the response for a canceled search
|
|
|
|
searchUrl = null
|
|
|
|
hideQuerySheet()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@VisibleForTesting
|
|
|
|
fun cancelActiveSearch() {
|
|
|
|
if (isSearching()) {
|
|
|
|
onEndSearch(searchUrl)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-25 18:56:21 +01:00
|
|
|
@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
|
2018-05-06 22:05:54 +02:00
|
|
|
open fun openLink(url: String) {
|
2022-02-25 18:56:21 +01:00
|
|
|
(this as Context).openLink(url)
|
2018-05-06 22:05:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private fun showQuerySheet() {
|
2019-09-22 08:18:44 +02:00
|
|
|
bottomSheet.state = BottomSheetBehavior.STATE_COLLAPSED
|
2018-05-06 22:05:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private fun hideQuerySheet() {
|
2019-09-22 08:18:44 +02:00
|
|
|
bottomSheet.state = BottomSheetBehavior.STATE_HIDDEN
|
2018-05-06 22:05:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-11 17:51:47 +02:00
|
|
|
enum class PostLookupFallbackBehavior {
|
|
|
|
OPEN_IN_BROWSER,
|
|
|
|
DISPLAY_ERROR,
|
|
|
|
}
|