move bottom sheet from fragments to activities (#628)
* move bottom sheet from fragments to activities * move BottomSheetLogic to dedicated abstract Activity * change tests * improve code
This commit is contained in:
parent
1c711eca22
commit
a2bfef3101
23 changed files with 421 additions and 296 deletions
196
app/src/main/java/com/keylesspalace/tusky/BottomSheetActivity.kt
Normal file
196
app/src/main/java/com/keylesspalace/tusky/BottomSheetActivity.kt
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
/* 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
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.support.annotation.VisibleForTesting
|
||||
import android.support.design.widget.BottomSheetBehavior
|
||||
import android.view.View
|
||||
import android.widget.LinearLayout
|
||||
import com.keylesspalace.tusky.entity.SearchResults
|
||||
import com.keylesspalace.tusky.entity.Status
|
||||
import com.keylesspalace.tusky.network.MastodonApi
|
||||
import com.keylesspalace.tusky.util.LinkHelper
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
import java.net.URI
|
||||
import java.net.URISyntaxException
|
||||
|
||||
/** this is the base class for all activities that open links
|
||||
* links are checked against the api if they are mastodon links so they can be openend in Tusky
|
||||
* Subclasses must have a bottom sheet with Id item_status_bottom_sheet in their layout hierachy
|
||||
*/
|
||||
|
||||
abstract class BottomSheetActivity : BaseActivity() {
|
||||
|
||||
lateinit var bottomSheet: BottomSheetBehavior<LinearLayout>
|
||||
var searchUrl: String? = null
|
||||
|
||||
abstract fun getMastodonApi(): MastodonApi
|
||||
|
||||
override fun onPostCreate(savedInstanceState: Bundle?) {
|
||||
super.onPostCreate(savedInstanceState)
|
||||
|
||||
val bottomSheetLayout: LinearLayout = findViewById(R.id.item_status_bottom_sheet)
|
||||
bottomSheet = BottomSheetBehavior.from(bottomSheetLayout)
|
||||
bottomSheet.state = BottomSheetBehavior.STATE_HIDDEN
|
||||
bottomSheet.setBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
|
||||
override fun onStateChanged(bottomSheet: View, newState: Int) {
|
||||
if (newState == BottomSheetBehavior.STATE_HIDDEN) {
|
||||
cancelActiveSearch()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSlide(bottomSheet: View, slideOffset: Float) {}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
open fun viewUrl(url: String) {
|
||||
if (!looksLikeMastodonUrl(url)) {
|
||||
openLink(url)
|
||||
return
|
||||
}
|
||||
|
||||
val call = getMastodonApi().search(url, true)
|
||||
call.enqueue(object : Callback<SearchResults> {
|
||||
override fun onResponse(call: Call<SearchResults>, response: Response<SearchResults>) {
|
||||
if (getCancelSearchRequested(url)) {
|
||||
return
|
||||
}
|
||||
|
||||
onEndSearch(url)
|
||||
if (response.isSuccessful) {
|
||||
// According to the mastodon API doc, if the search query is a url,
|
||||
// only exact matches for statuses or accounts are returned
|
||||
// which is good, because pleroma returns a different url
|
||||
// than the public post link
|
||||
val searchResult = response.body()
|
||||
if(searchResult != null) {
|
||||
if (searchResult.statuses.isNotEmpty()) {
|
||||
viewThread(searchResult.statuses[0])
|
||||
return
|
||||
} else if (searchResult.accounts.isNotEmpty()) {
|
||||
viewAccount(searchResult.accounts[0].id)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
openLink(url)
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<SearchResults>, t: Throwable) {
|
||||
if (!getCancelSearchRequested(url)) {
|
||||
onEndSearch(url)
|
||||
openLink(url)
|
||||
}
|
||||
}
|
||||
})
|
||||
callList.add(call)
|
||||
onBeginSearch(url)
|
||||
}
|
||||
|
||||
open fun viewThread(status: Status) {
|
||||
if (!isSearching()) {
|
||||
val intent = Intent(this, ViewThreadActivity::class.java)
|
||||
intent.putExtra("id", status.actionableId)
|
||||
intent.putExtra("url", status.actionableStatus.url)
|
||||
startActivity(intent)
|
||||
}
|
||||
}
|
||||
|
||||
open fun viewAccount(id: String) {
|
||||
val intent = Intent(this, AccountActivity::class.java)
|
||||
intent.putExtra("id", id)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
@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)
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
open fun openLink(url: String) {
|
||||
LinkHelper.openLink(url, this)
|
||||
}
|
||||
|
||||
private fun showQuerySheet() {
|
||||
bottomSheet.state = BottomSheetBehavior.STATE_COLLAPSED
|
||||
}
|
||||
|
||||
private fun hideQuerySheet() {
|
||||
bottomSheet.state = BottomSheetBehavior.STATE_HIDDEN
|
||||
}
|
||||
}
|
||||
|
||||
// https://mastodon.foo.bar/@User
|
||||
// https://mastodon.foo.bar/@User/43456787654678
|
||||
// https://pleroma.foo.bar/users/User
|
||||
// https://pleroma.foo.bar/users/43456787654678
|
||||
// https://pleroma.foo.bar/notice/43456787654678
|
||||
// https://pleroma.foo.bar/objects/d4643c42-3ae0-4b73-b8b0-c725f5819207
|
||||
fun looksLikeMastodonUrl(urlString: String): Boolean {
|
||||
val uri: URI
|
||||
try {
|
||||
uri = URI(urlString)
|
||||
} catch (e: URISyntaxException) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (uri.query != null ||
|
||||
uri.fragment != null ||
|
||||
uri.path == null) {
|
||||
return false
|
||||
}
|
||||
|
||||
val path = uri.path
|
||||
return path.matches("^/@[^/]+$".toRegex()) ||
|
||||
path.matches("^/users/[^/]+$".toRegex()) ||
|
||||
path.matches("^/@[^/]+/\\d+$".toRegex()) ||
|
||||
path.matches("^/notice/\\d+$".toRegex()) ||
|
||||
path.matches("^/objects/[-a-f0-9]+$".toRegex())
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue