mirror of
https://github.com/pachli/pachli-android.git
synced 2025-02-09 00:18:56 +01:00
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
@ -75,7 +75,7 @@ import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
||||
public final class AccountActivity extends BaseActivity implements ActionButtonActivity,
|
||||
public final class AccountActivity extends BottomSheetActivity implements ActionButtonActivity,
|
||||
HasSupportFragmentInjector {
|
||||
private static final String TAG = "AccountActivity"; // logging tag
|
||||
|
||||
@ -329,8 +329,8 @@ public final class AccountActivity extends BaseActivity implements ActionButtonA
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewURL(String url) {
|
||||
LinkHelper.openLink(url, note.getContext());
|
||||
public void onViewUrl(String url) {
|
||||
viewUrl(url);
|
||||
}
|
||||
});
|
||||
|
||||
@ -711,4 +711,10 @@ public final class AccountActivity extends BaseActivity implements ActionButtonA
|
||||
public AndroidInjector<Fragment> supportFragmentInjector() {
|
||||
return dispatchingAndroidInjector;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public MastodonApi getMastodonApi() {
|
||||
return mastodonApi;
|
||||
}
|
||||
}
|
||||
|
@ -34,10 +34,17 @@ import com.keylesspalace.tusky.db.AccountEntity;
|
||||
import com.keylesspalace.tusky.db.AccountManager;
|
||||
import com.keylesspalace.tusky.util.ThemeUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import retrofit2.Call;
|
||||
|
||||
public abstract class BaseActivity extends AppCompatActivity {
|
||||
|
||||
protected List<Call> callList;
|
||||
|
||||
@Inject
|
||||
public AccountManager accountManager;
|
||||
|
||||
@ -75,6 +82,9 @@ public abstract class BaseActivity extends AppCompatActivity {
|
||||
getTheme().applyStyle(style, false);
|
||||
|
||||
redirectIfNotLoggedIn();
|
||||
|
||||
callList = new ArrayList<>();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -162,4 +172,12 @@ public abstract class BaseActivity extends AppCompatActivity {
|
||||
.build()
|
||||
.scheduleAsync();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
for (Call call : callList) {
|
||||
call.cancel();
|
||||
}
|
||||
super.onDestroy();
|
||||
}
|
||||
}
|
||||
|
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())
|
||||
}
|
@ -24,6 +24,9 @@ import android.support.v7.widget.Toolbar;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import com.keylesspalace.tusky.fragment.TimelineFragment;
|
||||
import com.keylesspalace.tusky.network.MastodonApi;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@ -31,8 +34,10 @@ import dagger.android.AndroidInjector;
|
||||
import dagger.android.DispatchingAndroidInjector;
|
||||
import dagger.android.support.HasSupportFragmentInjector;
|
||||
|
||||
public class FavouritesActivity extends BaseActivity implements HasSupportFragmentInjector {
|
||||
public class FavouritesActivity extends BottomSheetActivity implements HasSupportFragmentInjector {
|
||||
|
||||
@Inject
|
||||
public MastodonApi mastodonApi;
|
||||
@Inject
|
||||
public DispatchingAndroidInjector<Fragment> dispatchingAndroidInjector;
|
||||
|
||||
@ -71,4 +76,10 @@ public class FavouritesActivity extends BaseActivity implements HasSupportFragme
|
||||
public AndroidInjector<Fragment> supportFragmentInjector() {
|
||||
return dispatchingAndroidInjector;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public MastodonApi getMastodonApi() {
|
||||
return mastodonApi;
|
||||
}
|
||||
}
|
||||
|
@ -60,6 +60,8 @@ import com.mikepenz.materialdrawer.util.AbstractDrawerImageLoader;
|
||||
import com.mikepenz.materialdrawer.util.DrawerImageLoader;
|
||||
import com.squareup.picasso.Picasso;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@ -72,7 +74,7 @@ import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
||||
public class MainActivity extends BaseActivity implements ActionButtonActivity,
|
||||
public class MainActivity extends BottomSheetActivity implements ActionButtonActivity,
|
||||
HasSupportFragmentInjector {
|
||||
private static final String TAG = "MainActivity"; // logging tag
|
||||
private static final long DRAWER_ITEM_ADD_ACCOUNT = -13;
|
||||
@ -550,4 +552,10 @@ public class MainActivity extends BaseActivity implements ActionButtonActivity,
|
||||
public AndroidInjector<Fragment> supportFragmentInjector() {
|
||||
return fragmentInjector;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public MastodonApi getMastodonApi() {
|
||||
return mastodonApi;
|
||||
}
|
||||
}
|
@ -10,13 +10,16 @@ import android.view.MenuItem
|
||||
import android.widget.FrameLayout
|
||||
import com.keylesspalace.tusky.fragment.TimelineFragment
|
||||
import com.keylesspalace.tusky.interfaces.ActionButtonActivity
|
||||
import com.keylesspalace.tusky.network.MastodonApi
|
||||
import dagger.android.AndroidInjector
|
||||
import dagger.android.DispatchingAndroidInjector
|
||||
import dagger.android.support.HasSupportFragmentInjector
|
||||
import javax.inject.Inject
|
||||
|
||||
class ModalTimelineActivity : BaseActivity(), ActionButtonActivity, HasSupportFragmentInjector {
|
||||
class ModalTimelineActivity : BottomSheetActivity(), ActionButtonActivity, HasSupportFragmentInjector {
|
||||
|
||||
@Inject
|
||||
lateinit var api: MastodonApi
|
||||
@Inject
|
||||
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Fragment>
|
||||
|
||||
@ -75,4 +78,9 @@ class ModalTimelineActivity : BaseActivity(), ActionButtonActivity, HasSupportFr
|
||||
override fun supportFragmentInjector(): AndroidInjector<Fragment> {
|
||||
return dispatchingAndroidInjector
|
||||
}
|
||||
|
||||
override fun getMastodonApi(): MastodonApi {
|
||||
return api
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -30,6 +30,9 @@ import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import com.keylesspalace.tusky.fragment.SearchFragment;
|
||||
import com.keylesspalace.tusky.network.MastodonApi;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@ -37,9 +40,10 @@ import dagger.android.AndroidInjector;
|
||||
import dagger.android.DispatchingAndroidInjector;
|
||||
import dagger.android.support.HasSupportFragmentInjector;
|
||||
|
||||
public class SearchActivity extends BaseActivity implements SearchView.OnQueryTextListener,
|
||||
public class SearchActivity extends BottomSheetActivity implements SearchView.OnQueryTextListener,
|
||||
HasSupportFragmentInjector {
|
||||
|
||||
@Inject
|
||||
public MastodonApi mastodonApi;
|
||||
@Inject
|
||||
public DispatchingAndroidInjector<Fragment> fragmentInjector;
|
||||
|
||||
@ -139,4 +143,10 @@ public class SearchActivity extends BaseActivity implements SearchView.OnQueryTe
|
||||
public AndroidInjector<Fragment> supportFragmentInjector() {
|
||||
return fragmentInjector;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public MastodonApi getMastodonApi() {
|
||||
return mastodonApi;
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,9 @@ import android.support.v7.widget.Toolbar;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import com.keylesspalace.tusky.fragment.TimelineFragment;
|
||||
import com.keylesspalace.tusky.network.MastodonApi;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@ -31,8 +34,10 @@ import dagger.android.AndroidInjector;
|
||||
import dagger.android.DispatchingAndroidInjector;
|
||||
import dagger.android.support.HasSupportFragmentInjector;
|
||||
|
||||
public class ViewTagActivity extends BaseActivity implements HasSupportFragmentInjector {
|
||||
public class ViewTagActivity extends BottomSheetActivity implements HasSupportFragmentInjector {
|
||||
|
||||
@Inject
|
||||
public MastodonApi mastodonApi;
|
||||
@Inject
|
||||
public DispatchingAndroidInjector<Fragment> dispatchingAndroidInjector;
|
||||
|
||||
@ -74,4 +79,10 @@ public class ViewTagActivity extends BaseActivity implements HasSupportFragmentI
|
||||
public AndroidInjector<Fragment> supportFragmentInjector() {
|
||||
return dispatchingAndroidInjector;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public MastodonApi getMastodonApi() {
|
||||
return mastodonApi;
|
||||
}
|
||||
}
|
||||
|
@ -25,15 +25,18 @@ import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import com.keylesspalace.tusky.fragment.ViewThreadFragment;
|
||||
import com.keylesspalace.tusky.network.MastodonApi;
|
||||
import com.keylesspalace.tusky.util.LinkHelper;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import dagger.android.AndroidInjector;
|
||||
import dagger.android.DispatchingAndroidInjector;
|
||||
import dagger.android.support.HasSupportFragmentInjector;
|
||||
|
||||
public class ViewThreadActivity extends BaseActivity implements HasSupportFragmentInjector {
|
||||
public class ViewThreadActivity extends BottomSheetActivity implements HasSupportFragmentInjector {
|
||||
|
||||
public static final int REVEAL_BUTTON_HIDDEN = 1;
|
||||
public static final int REVEAL_BUTTON_REVEAL = 2;
|
||||
@ -41,6 +44,8 @@ public class ViewThreadActivity extends BaseActivity implements HasSupportFragme
|
||||
|
||||
private int revealButtonState = REVEAL_BUTTON_HIDDEN;
|
||||
|
||||
@Inject
|
||||
public MastodonApi mastodonApi;
|
||||
@Inject
|
||||
public DispatchingAndroidInjector<Fragment> dispatchingAndroidInjector;
|
||||
|
||||
@ -113,4 +118,10 @@ public class ViewThreadActivity extends BaseActivity implements HasSupportFragme
|
||||
public AndroidInjector<Fragment> supportFragmentInjector() {
|
||||
return dispatchingAndroidInjector;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public MastodonApi getMastodonApi() {
|
||||
return mastodonApi;
|
||||
}
|
||||
}
|
||||
|
@ -20,49 +20,35 @@ import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.design.widget.BottomSheetBehavior;
|
||||
import android.support.v4.app.ActivityOptionsCompat;
|
||||
import android.support.v4.view.ViewCompat;
|
||||
import android.support.v7.widget.PopupMenu;
|
||||
import android.text.Spanned;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import com.keylesspalace.tusky.AccountActivity;
|
||||
import com.keylesspalace.tusky.BottomSheetActivity;
|
||||
import com.keylesspalace.tusky.ComposeActivity;
|
||||
import com.keylesspalace.tusky.R;
|
||||
import com.keylesspalace.tusky.ReportActivity;
|
||||
import com.keylesspalace.tusky.TuskyApplication;
|
||||
import com.keylesspalace.tusky.ViewMediaActivity;
|
||||
import com.keylesspalace.tusky.ViewTagActivity;
|
||||
import com.keylesspalace.tusky.ViewThreadActivity;
|
||||
import com.keylesspalace.tusky.ViewVideoActivity;
|
||||
import com.keylesspalace.tusky.db.AccountEntity;
|
||||
import com.keylesspalace.tusky.db.AccountManager;
|
||||
import com.keylesspalace.tusky.entity.Account;
|
||||
import com.keylesspalace.tusky.entity.Attachment;
|
||||
import com.keylesspalace.tusky.entity.SearchResults;
|
||||
import com.keylesspalace.tusky.entity.Status;
|
||||
import com.keylesspalace.tusky.interfaces.AdapterItemRemover;
|
||||
import com.keylesspalace.tusky.network.MastodonApi;
|
||||
import com.keylesspalace.tusky.network.TimelineCases;
|
||||
import com.keylesspalace.tusky.util.HtmlUtils;
|
||||
import com.keylesspalace.tusky.util.LinkHelper;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
||||
/* Note from Andrew on Jan. 22, 2017: This class is a design problem for me, so I left it with an
|
||||
* awkward name. TimelineFragment and NotificationFragment have significant overlap but the nature
|
||||
* of that is complicated by how they're coupled with Status and Notification and the corresponding
|
||||
@ -74,10 +60,10 @@ public abstract class SFragment extends BaseFragment implements AdapterItemRemov
|
||||
|
||||
protected String loggedInAccountId;
|
||||
protected String loggedInUsername;
|
||||
protected String searchUrl;
|
||||
|
||||
protected abstract TimelineCases timelineCases();
|
||||
protected BottomSheetBehavior bottomSheet;
|
||||
|
||||
private BottomSheetActivity bottomSheetActivity;
|
||||
|
||||
@Inject
|
||||
protected MastodonApi mastodonApi;
|
||||
@ -91,7 +77,6 @@ public abstract class SFragment extends BaseFragment implements AdapterItemRemov
|
||||
loggedInAccountId = activeAccount.getAccountId();
|
||||
loggedInUsername = activeAccount.getUsername();
|
||||
}
|
||||
setupBottomSheet(getView());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -100,9 +85,31 @@ public abstract class SFragment extends BaseFragment implements AdapterItemRemov
|
||||
getActivity().overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
if(context instanceof BottomSheetActivity) {
|
||||
bottomSheetActivity = (BottomSheetActivity)context;
|
||||
} else {
|
||||
throw new IllegalStateException("Fragment must be attached to a BottomSheetActivity!");
|
||||
}
|
||||
}
|
||||
|
||||
protected void openReblog(@Nullable final Status status) {
|
||||
if (status == null) return;
|
||||
viewAccount(status.getAccount().getId());
|
||||
bottomSheetActivity.viewAccount(status.getAccount().getId());
|
||||
}
|
||||
|
||||
protected void viewThread(Status status) {
|
||||
bottomSheetActivity.viewThread(status);
|
||||
}
|
||||
|
||||
protected void viewAccount(String accountId) {
|
||||
bottomSheetActivity.viewAccount(accountId);
|
||||
}
|
||||
|
||||
public void onViewUrl(String url) {
|
||||
bottomSheetActivity.viewUrl(url);
|
||||
}
|
||||
|
||||
protected void reply(Status status) {
|
||||
@ -229,27 +236,12 @@ public abstract class SFragment extends BaseFragment implements AdapterItemRemov
|
||||
}
|
||||
}
|
||||
|
||||
protected void viewThread(Status status) {
|
||||
if (!isSearching()) {
|
||||
Intent intent = new Intent(getContext(), ViewThreadActivity.class);
|
||||
intent.putExtra("id", status.getActionableId());
|
||||
intent.putExtra("url", status.getActionableStatus().getUrl());
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
||||
|
||||
protected void viewTag(String tag) {
|
||||
Intent intent = new Intent(getContext(), ViewTagActivity.class);
|
||||
intent.putExtra("hashtag", tag);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
protected void viewAccount(String id) {
|
||||
Intent intent = new Intent(getContext(), AccountActivity.class);
|
||||
intent.putExtra("id", id);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
protected void openReportPage(String accountId, String accountUsername, String statusId,
|
||||
Spanned statusContent) {
|
||||
Intent intent = new Intent(getContext(), ReportActivity.class);
|
||||
@ -260,144 +252,5 @@ public abstract class SFragment extends BaseFragment implements AdapterItemRemov
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
// 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
|
||||
static boolean looksLikeMastodonUrl(String urlString) {
|
||||
URI uri;
|
||||
try {
|
||||
uri = new URI(urlString);
|
||||
} catch (URISyntaxException e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (uri.getQuery() != null ||
|
||||
uri.getFragment() != null ||
|
||||
uri.getPath() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String path = uri.getPath();
|
||||
return path.matches("^/@[^/]+$") ||
|
||||
path.matches("^/users/[^/]+$") ||
|
||||
path.matches("^/@[^/]+/\\d+$") ||
|
||||
path.matches("^/notice/\\d+$") ||
|
||||
path.matches("^/objects/[-a-f0-9]+$");
|
||||
}
|
||||
|
||||
void onBeginSearch(@NonNull String url) {
|
||||
searchUrl = url;
|
||||
showQuerySheet();
|
||||
}
|
||||
|
||||
boolean getCancelSearchRequested(@NonNull String url) {
|
||||
return !url.equals(searchUrl);
|
||||
}
|
||||
|
||||
boolean isSearching() {
|
||||
return searchUrl != null;
|
||||
}
|
||||
|
||||
void onEndSearch(@NonNull String url) {
|
||||
if (url.equals(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();
|
||||
}
|
||||
}
|
||||
|
||||
void cancelActiveSearch()
|
||||
{
|
||||
if (isSearching()) {
|
||||
onEndSearch(searchUrl);
|
||||
}
|
||||
}
|
||||
|
||||
void openLink(@NonNull String url) {
|
||||
LinkHelper.openLink(url, getContext());
|
||||
}
|
||||
|
||||
public void onViewURL(String url) {
|
||||
if (!looksLikeMastodonUrl(url)) {
|
||||
openLink(url);
|
||||
return;
|
||||
}
|
||||
|
||||
Call<SearchResults> call = mastodonApi.search(url, true);
|
||||
call.enqueue(new Callback<SearchResults>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<SearchResults> call, @NonNull Response<SearchResults> response) {
|
||||
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
|
||||
List<Status> statuses = response.body().getStatuses();
|
||||
List<Account> accounts = response.body().getAccounts();
|
||||
if (statuses != null && !statuses.isEmpty()) {
|
||||
viewThread(statuses.get(0));
|
||||
return;
|
||||
} else if (accounts != null && !accounts.isEmpty()) {
|
||||
viewAccount(accounts.get(0).getId());
|
||||
return;
|
||||
}
|
||||
}
|
||||
openLink(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<SearchResults> call, @NonNull Throwable t) {
|
||||
if (!getCancelSearchRequested(url)) {
|
||||
onEndSearch(url);
|
||||
openLink(url);
|
||||
}
|
||||
}
|
||||
});
|
||||
callList.add(call);
|
||||
onBeginSearch(url);
|
||||
}
|
||||
|
||||
protected void setupBottomSheet(View view)
|
||||
{
|
||||
LinearLayout bottomSheetLayout = view.findViewById(R.id.item_status_bottom_sheet);
|
||||
if (bottomSheetLayout != null) {
|
||||
bottomSheet = BottomSheetBehavior.from(bottomSheetLayout);
|
||||
bottomSheet.setState(BottomSheetBehavior.STATE_HIDDEN);
|
||||
bottomSheet.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
|
||||
@Override
|
||||
public void onStateChanged(@NonNull View bottomSheet, int newState) {
|
||||
switch(newState) {
|
||||
case BottomSheetBehavior.STATE_HIDDEN:
|
||||
cancelActiveSearch();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSlide(@NonNull View bottomSheet, float slideOffset) {
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void showQuerySheet() {
|
||||
if (bottomSheet != null)
|
||||
bottomSheet.setState(BottomSheetBehavior.STATE_COLLAPSED);
|
||||
}
|
||||
|
||||
private void hideQuerySheet() {
|
||||
if (bottomSheet != null)
|
||||
bottomSheet.setState(BottomSheetBehavior.STATE_HIDDEN);
|
||||
}
|
||||
}
|
||||
|
@ -18,5 +18,5 @@ package com.keylesspalace.tusky.interfaces;
|
||||
public interface LinkListener {
|
||||
void onViewTag(String tag);
|
||||
void onViewAccount(String id);
|
||||
void onViewURL(String url);
|
||||
void onViewUrl(String url);
|
||||
}
|
||||
|
@ -20,12 +20,10 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.provider.Browser;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.customtabs.CustomTabsIntent;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextPaint;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.text.style.ClickableSpan;
|
||||
import android.text.style.URLSpan;
|
||||
@ -109,7 +107,7 @@ public class LinkHelper {
|
||||
customSpan = new CustomURLSpan(span.getURL()) {
|
||||
@Override
|
||||
public void onClick(View widget) {
|
||||
listener.onViewURL(getURL());
|
||||
listener.onViewUrl(getURL());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -232,4 +232,6 @@
|
||||
android:contentDescription="@string/action_mention"
|
||||
app:srcCompat="@drawable/ic_create_24dp" />
|
||||
|
||||
<include layout="@layout/item_status_bottom_sheet"/>
|
||||
|
||||
</android.support.design.widget.CoordinatorLayout>
|
||||
|
@ -17,4 +17,6 @@
|
||||
|
||||
<include layout="@layout/toolbar_shadow_shim" />
|
||||
|
||||
<include layout="@layout/item_status_bottom_sheet"/>
|
||||
|
||||
</android.support.design.widget.CoordinatorLayout>
|
@ -90,4 +90,6 @@
|
||||
app:layout_anchorGravity="bottom|end"
|
||||
app:srcCompat="@drawable/ic_create_24dp" />
|
||||
|
||||
<include layout="@layout/item_status_bottom_sheet"/>
|
||||
|
||||
</android.support.design.widget.CoordinatorLayout>
|
||||
|
@ -1,27 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/activity_view_thread"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context="com.keylesspalace.tusky.ModalTimelineActivity">
|
||||
|
||||
<include layout="@layout/toolbar_basic" />
|
||||
|
||||
<include
|
||||
layout="@layout/toolbar_shadow_shim"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="4dp"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/appbar" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/content_frame"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/toolbar_shadow_shim"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"/>
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||
|
||||
<include layout="@layout/toolbar_shadow_shim" />
|
||||
|
||||
<include layout="@layout/item_status_bottom_sheet"/>
|
||||
|
||||
</android.support.design.widget.CoordinatorLayout>
|
||||
|
@ -31,4 +31,6 @@
|
||||
|
||||
<include layout="@layout/toolbar_shadow_shim" />
|
||||
|
||||
<include layout="@layout/item_status_bottom_sheet"/>
|
||||
|
||||
</android.support.design.widget.CoordinatorLayout>
|
@ -17,4 +17,6 @@
|
||||
|
||||
<include layout="@layout/toolbar_shadow_shim" />
|
||||
|
||||
<include layout="@layout/item_status_bottom_sheet"/>
|
||||
|
||||
</android.support.design.widget.CoordinatorLayout>
|
@ -17,4 +17,6 @@
|
||||
|
||||
<include layout="@layout/toolbar_shadow_shim" />
|
||||
|
||||
<include layout="@layout/item_status_bottom_sheet"/>
|
||||
|
||||
</android.support.design.widget.CoordinatorLayout>
|
@ -26,6 +26,4 @@
|
||||
android:text="@string/search_no_results"
|
||||
android:visibility="gone" />
|
||||
|
||||
<include layout="@layout/item_status_bottom_sheet" />
|
||||
|
||||
</android.support.design.widget.CoordinatorLayout>
|
||||
|
@ -1,19 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.design.widget.CoordinatorLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/swipe_refresh_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent">
|
||||
<android.support.v4.widget.SwipeRefreshLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/swipe_refresh_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="top">
|
||||
android:layout_gravity="top">
|
||||
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/recycler_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
</android.support.v4.widget.SwipeRefreshLayout>
|
||||
<include layout="@layout/item_status_bottom_sheet"/>
|
||||
</android.support.design.widget.CoordinatorLayout>
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/recycler_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
</android.support.v4.widget.SwipeRefreshLayout>
|
@ -1,19 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.design.widget.CoordinatorLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/swipe_refresh_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent">
|
||||
<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/swipe_refresh_layout"
|
||||
android:layout_gravity="top">
|
||||
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/recycler_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="top">
|
||||
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/recycler_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scrollbars="vertical" />
|
||||
</android.support.v4.widget.SwipeRefreshLayout>
|
||||
<include layout="@layout/item_status_bottom_sheet" />
|
||||
</android.support.design.widget.CoordinatorLayout>
|
||||
android:scrollbars="vertical" />
|
||||
</android.support.v4.widget.SwipeRefreshLayout>
|
||||
|
@ -13,14 +13,15 @@
|
||||
* 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.fragment
|
||||
package com.keylesspalace.tusky
|
||||
|
||||
import android.support.design.widget.BottomSheetBehavior
|
||||
import android.text.SpannedString
|
||||
import android.widget.LinearLayout
|
||||
import com.keylesspalace.tusky.entity.Account
|
||||
import com.keylesspalace.tusky.entity.SearchResults
|
||||
import com.keylesspalace.tusky.entity.Status
|
||||
import com.keylesspalace.tusky.network.MastodonApi
|
||||
import com.keylesspalace.tusky.network.TimelineCases
|
||||
import okhttp3.Request
|
||||
import org.junit.Assert
|
||||
import org.junit.Before
|
||||
@ -35,8 +36,8 @@ import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
import java.util.*
|
||||
|
||||
class SFragmentTest {
|
||||
private lateinit var fragment : FakeSFragment
|
||||
class BottomSheetActivityTest {
|
||||
private lateinit var activity : FakeBottomSheetActivity
|
||||
private lateinit var apiMock: MastodonApi
|
||||
private val accountQuery = "http://mastodon.foo.bar/@User"
|
||||
private val statusQuery = "http://mastodon.foo.bar/@User/345678"
|
||||
@ -85,13 +86,12 @@ class SFragmentTest {
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
fragment = FakeSFragment()
|
||||
|
||||
apiMock = Mockito.mock(MastodonApi::class.java)
|
||||
`when`(apiMock.search(eq(accountQuery), ArgumentMatchers.anyBoolean())).thenReturn(accountCallback)
|
||||
`when`(apiMock.search(eq(statusQuery), ArgumentMatchers.anyBoolean())).thenReturn(statusCallback)
|
||||
`when`(apiMock.search(eq(nonMastodonQuery), ArgumentMatchers.anyBoolean())).thenReturn(emptyCallback)
|
||||
fragment.mastodonApi = apiMock
|
||||
|
||||
activity = FakeBottomSheetActivity(apiMock)
|
||||
}
|
||||
|
||||
@RunWith(Parameterized::class)
|
||||
@ -131,22 +131,22 @@ class SFragmentTest {
|
||||
|
||||
@Test
|
||||
fun test() {
|
||||
Assert.assertEquals(expectedResult, SFragment.looksLikeMastodonUrl(url))
|
||||
Assert.assertEquals(expectedResult, looksLikeMastodonUrl(url))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun beginEndSearch_setIsSearching_isSearchingAfterBegin() {
|
||||
fragment.onBeginSearch("https://mastodon.foo.bar/@User")
|
||||
Assert.assertTrue(fragment.isSearching)
|
||||
activity.onBeginSearch("https://mastodon.foo.bar/@User")
|
||||
Assert.assertTrue(activity.isSearching())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun beginEndSearch_setIsSearching_isNotSearchingAfterEnd() {
|
||||
val validUrl = "https://mastodon.foo.bar/@User"
|
||||
fragment.onBeginSearch(validUrl)
|
||||
fragment.onEndSearch(validUrl)
|
||||
Assert.assertFalse(fragment.isSearching)
|
||||
activity.onBeginSearch(validUrl)
|
||||
activity.onEndSearch(validUrl)
|
||||
Assert.assertFalse(activity.isSearching())
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -154,18 +154,18 @@ class SFragmentTest {
|
||||
val validUrl = "https://mastodon.foo.bar/@User"
|
||||
val invalidUrl = ""
|
||||
|
||||
fragment.onBeginSearch(validUrl)
|
||||
fragment.onEndSearch(invalidUrl)
|
||||
Assert.assertTrue(fragment.isSearching)
|
||||
activity.onBeginSearch(validUrl)
|
||||
activity.onEndSearch(invalidUrl)
|
||||
Assert.assertTrue(activity.isSearching())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun cancelActiveSearch() {
|
||||
val url = "https://mastodon.foo.bar/@User"
|
||||
|
||||
fragment.onBeginSearch(url)
|
||||
fragment.cancelActiveSearch()
|
||||
Assert.assertFalse(fragment.isSearching)
|
||||
activity.onBeginSearch(url)
|
||||
activity.cancelActiveSearch()
|
||||
Assert.assertFalse(activity.isSearching())
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -173,85 +173,84 @@ class SFragmentTest {
|
||||
val firstUrl = "https://mastodon.foo.bar/@User"
|
||||
val secondUrl = "https://mastodon.foo.bar/@meh"
|
||||
|
||||
fragment.onBeginSearch(firstUrl)
|
||||
fragment.cancelActiveSearch()
|
||||
activity.onBeginSearch(firstUrl)
|
||||
activity.cancelActiveSearch()
|
||||
|
||||
fragment.onBeginSearch(secondUrl)
|
||||
Assert.assertTrue(fragment.getCancelSearchRequested(firstUrl))
|
||||
Assert.assertFalse(fragment.getCancelSearchRequested(secondUrl))
|
||||
activity.onBeginSearch(secondUrl)
|
||||
Assert.assertTrue(activity.getCancelSearchRequested(firstUrl))
|
||||
Assert.assertFalse(activity.getCancelSearchRequested(secondUrl))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun search_inIdealConditions_returnsRequestedResults_forAccount() {
|
||||
fragment.onViewURL(accountQuery)
|
||||
activity.viewUrl(accountQuery)
|
||||
accountCallback.invokeCallback()
|
||||
Assert.assertEquals(account.id, fragment.accountId)
|
||||
Assert.assertEquals(account.id, activity.accountId)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun search_inIdealConditions_returnsRequestedResults_forStatus() {
|
||||
fragment.onViewURL(statusQuery)
|
||||
activity.viewUrl(statusQuery)
|
||||
statusCallback.invokeCallback()
|
||||
Assert.assertEquals(status, fragment.status)
|
||||
Assert.assertEquals(status, activity.status)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun search_inIdealConditions_returnsRequestedResults_forNonMastodonURL() {
|
||||
fragment.onViewURL(nonMastodonQuery)
|
||||
activity.viewUrl(nonMastodonQuery)
|
||||
emptyCallback.invokeCallback()
|
||||
Assert.assertEquals(nonMastodonQuery, fragment.url)
|
||||
Assert.assertEquals(nonMastodonQuery, activity.link)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun search_withCancellation_doesNotLoadUrl_forAccount() {
|
||||
fragment.onViewURL(accountQuery)
|
||||
Assert.assertTrue(fragment.isSearching)
|
||||
fragment.cancelActiveSearch()
|
||||
Assert.assertFalse(fragment.isSearching)
|
||||
activity.viewUrl(accountQuery)
|
||||
Assert.assertTrue(activity.isSearching())
|
||||
activity.cancelActiveSearch()
|
||||
Assert.assertFalse(activity.isSearching())
|
||||
accountCallback.invokeCallback()
|
||||
Assert.assertEquals(null, fragment.accountId)
|
||||
Assert.assertEquals(null, activity.accountId)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun search_withCancellation_doesNotLoadUrl_forStatus() {
|
||||
fragment.onViewURL(accountQuery)
|
||||
fragment.cancelActiveSearch()
|
||||
activity.viewUrl(accountQuery)
|
||||
activity.cancelActiveSearch()
|
||||
accountCallback.invokeCallback()
|
||||
Assert.assertEquals(null, fragment.accountId)
|
||||
Assert.assertEquals(null, activity.accountId)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun search_withCancellation_doesNotLoadUrl_forNonMastodonURL() {
|
||||
fragment.onViewURL(nonMastodonQuery)
|
||||
fragment.cancelActiveSearch()
|
||||
activity.viewUrl(nonMastodonQuery)
|
||||
activity.cancelActiveSearch()
|
||||
emptyCallback.invokeCallback()
|
||||
Assert.assertEquals(null, fragment.url)
|
||||
Assert.assertEquals(null, activity.searchUrl)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun search_withPreviousCancellation_completes() {
|
||||
// begin/cancel account search
|
||||
fragment.onViewURL(accountQuery)
|
||||
fragment.cancelActiveSearch()
|
||||
activity.viewUrl(accountQuery)
|
||||
activity.cancelActiveSearch()
|
||||
|
||||
// begin status search
|
||||
fragment.onViewURL(statusQuery)
|
||||
activity.viewUrl(statusQuery)
|
||||
|
||||
// return response from account search
|
||||
accountCallback.invokeCallback()
|
||||
|
||||
// ensure that status search is still ongoing
|
||||
Assert.assertTrue(fragment.isSearching)
|
||||
Assert.assertTrue(activity.isSearching())
|
||||
statusCallback.invokeCallback()
|
||||
|
||||
// ensure that the result of the status search was recorded
|
||||
// and the account search wasn't
|
||||
Assert.assertEquals(status, fragment.status)
|
||||
Assert.assertEquals(null, fragment.accountId)
|
||||
Assert.assertEquals(status, activity.status)
|
||||
Assert.assertEquals(null, activity.accountId)
|
||||
}
|
||||
|
||||
class FakeSearchResults : Call<SearchResults>
|
||||
{
|
||||
class FakeSearchResults : Call<SearchResults> {
|
||||
private var searchResults: SearchResults
|
||||
private var callback: Callback<SearchResults>? = null
|
||||
|
||||
@ -283,29 +282,33 @@ class SFragmentTest {
|
||||
override fun request(): Request { throw NotImplementedError() }
|
||||
}
|
||||
|
||||
class FakeSFragment : SFragment() {
|
||||
class FakeBottomSheetActivity(val api: MastodonApi) : BottomSheetActivity() {
|
||||
|
||||
var status: Status? = null
|
||||
var accountId: String? = null
|
||||
var url: String? = null
|
||||
var link: String? = null
|
||||
|
||||
init {
|
||||
callList = mutableListOf()
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
bottomSheet = Mockito.mock(BottomSheetBehavior::class.java) as BottomSheetBehavior<LinearLayout>
|
||||
callList = arrayListOf()
|
||||
}
|
||||
|
||||
override fun getMastodonApi(): MastodonApi {
|
||||
return api
|
||||
}
|
||||
|
||||
override fun openLink(url: String) {
|
||||
this.url = url
|
||||
this.link = url
|
||||
}
|
||||
|
||||
override fun viewAccount(id: String?) {
|
||||
accountId = id
|
||||
override fun viewAccount(id: String) {
|
||||
this.accountId = id
|
||||
}
|
||||
|
||||
override fun viewThread(status: Status?) {
|
||||
override fun viewThread(status: Status) {
|
||||
this.status = status
|
||||
}
|
||||
|
||||
override fun removeItem(position: Int) { throw NotImplementedError() }
|
||||
override fun removeAllByAccountId(accountId: String?) { throw NotImplementedError() }
|
||||
override fun timelineCases(): TimelineCases { throw NotImplementedError() }
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user