Add support for predictive back navigation
This commit is contained in:
parent
767e414c94
commit
513b29f57d
@ -90,7 +90,7 @@ dependencies {
|
||||
implementation 'me.grishka.litex:viewpager:1.0.0'
|
||||
implementation 'me.grishka.litex:viewpager2:1.0.0'
|
||||
implementation 'me.grishka.litex:palette:1.0.0'
|
||||
implementation 'me.grishka.appkit:appkit:1.2.17'
|
||||
implementation 'me.grishka.appkit:appkit:1.3.0'
|
||||
implementation 'com.google.code.gson:gson:2.8.9'
|
||||
implementation 'org.jsoup:jsoup:1.14.3'
|
||||
implementation 'com.squareup:otto:1.3.8'
|
||||
|
@ -33,7 +33,8 @@
|
||||
android:supportsRtl="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:theme="@style/Theme.Mastodon.AutoLightDark"
|
||||
android:largeHeap="true">
|
||||
android:largeHeap="true"
|
||||
android:enableOnBackInvokedCallback="true">
|
||||
|
||||
<meta-data
|
||||
android:name="com.google.mlkit.vision.DEPENDENCIES"
|
||||
|
@ -102,13 +102,12 @@ import me.grishka.appkit.Nav;
|
||||
import me.grishka.appkit.api.Callback;
|
||||
import me.grishka.appkit.api.ErrorResponse;
|
||||
import me.grishka.appkit.fragments.CustomTransitionsFragment;
|
||||
import me.grishka.appkit.fragments.OnBackPressedListener;
|
||||
import me.grishka.appkit.imageloader.ViewImageLoader;
|
||||
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
|
||||
import me.grishka.appkit.utils.CubicBezierInterpolator;
|
||||
import me.grishka.appkit.utils.V;
|
||||
|
||||
public class ComposeFragment extends MastodonToolbarFragment implements OnBackPressedListener, ComposeEditText.SelectionListener, CustomTransitionsFragment{
|
||||
public class ComposeFragment extends MastodonToolbarFragment implements ComposeEditText.SelectionListener, CustomTransitionsFragment{
|
||||
|
||||
private static final int MEDIA_RESULT=717;
|
||||
public static final int IMAGE_DESCRIPTION_RESULT=363;
|
||||
@ -173,6 +172,11 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
private BackgroundColorSpan overLimitBG;
|
||||
private ForegroundColorSpan overLimitFG;
|
||||
|
||||
private Runnable emojiKeyboardHider;
|
||||
private Runnable sendingBackButtonBlocker;
|
||||
private Runnable discardConfirmationCallback=this::confirmDiscardDraftAndFinish;
|
||||
private boolean prevHadDraft;
|
||||
|
||||
public ComposeFragment(){
|
||||
super(R.layout.toolbar_fragment_with_progressbar);
|
||||
}
|
||||
@ -249,6 +253,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
getActivity().dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL));
|
||||
}
|
||||
});
|
||||
emojiKeyboardHider=emojiKeyboard::hide;
|
||||
|
||||
View view=inflater.inflate(R.layout.fragment_compose, container, false);
|
||||
mainLayout=view.findViewById(R.id.compose_main_ll);
|
||||
@ -305,6 +310,10 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
public void onIconChanged(int icon){
|
||||
emojiBtn.setSelected(icon!=PopupKeyboard.ICON_HIDDEN);
|
||||
updateNavigationBarColor(icon!=PopupKeyboard.ICON_HIDDEN);
|
||||
if(icon!=PopupKeyboard.ICON_HIDDEN)
|
||||
addBackCallback(emojiKeyboardHider);
|
||||
else
|
||||
removeBackCallback(emojiKeyboardHider);
|
||||
if(autocompleteViewController.getMode()==ComposeAutocompleteViewController.Mode.EMOJIS){
|
||||
contentView.layout(contentView.getLeft(), contentView.getTop(), contentView.getRight(), contentView.getBottom());
|
||||
if(icon==PopupKeyboard.ICON_HIDDEN)
|
||||
@ -480,6 +489,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
}
|
||||
|
||||
updateCharCounter();
|
||||
updateDraftState();
|
||||
}
|
||||
});
|
||||
spoilerEdit.addTextChangedListener(new SimpleTextWatcher(e->updateCharCounter()));
|
||||
@ -621,6 +631,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
if(publishButton==null)
|
||||
return;
|
||||
publishButton.setEnabled((trimmedCharCount>0 || !mediaViewController.isEmpty()) && charCount<=charLimit && mediaViewController.getNonDoneAttachmentCount()==0 && (pollViewController.isEmpty() || pollViewController.getNonEmptyOptionsCount()>1));
|
||||
updateDraftState();
|
||||
}
|
||||
|
||||
private void onCustomEmojiClick(Emoji emoji){
|
||||
@ -696,6 +707,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
overlayParams.softInputMode=WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED;
|
||||
overlayParams.token=mainEditText.getWindowToken();
|
||||
wm.addView(sendingOverlay, overlayParams);
|
||||
addBackCallback(sendingBackButtonBlocker);
|
||||
|
||||
publishButton.setEnabled(false);
|
||||
V.setVisibilityAnimated(sendProgress, View.VISIBLE);
|
||||
@ -737,6 +749,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
public void onSuccess(Status result){
|
||||
wm.removeView(sendingOverlay);
|
||||
sendingOverlay=null;
|
||||
removeBackCallback(sendingBackButtonBlocker);
|
||||
if(editingStatus==null){
|
||||
E.post(new StatusCreatedEvent(result, accountID));
|
||||
if(replyTo!=null){
|
||||
@ -769,6 +782,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
private void handlePublishError(ErrorResponse error){
|
||||
wm.removeView(sendingOverlay);
|
||||
sendingOverlay=null;
|
||||
removeBackCallback(sendingBackButtonBlocker);
|
||||
V.setVisibilityAnimated(sendProgress, View.GONE);
|
||||
publishButton.setEnabled(true);
|
||||
if(error instanceof MastodonErrorResponse me){
|
||||
@ -796,19 +810,16 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
return (mainEditText.length()>0 && !mainEditText.getText().toString().equals(initialText)) || !mediaViewController.isEmpty() || pollFieldsHaveContent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onBackPressed(){
|
||||
if(emojiKeyboard.isVisible()){
|
||||
emojiKeyboard.hide();
|
||||
return true;
|
||||
private void updateDraftState(){
|
||||
boolean hasDraft=hasDraft();
|
||||
if(hasDraft!=prevHadDraft){
|
||||
prevHadDraft=hasDraft;
|
||||
if(hasDraft){
|
||||
addBackCallback(discardConfirmationCallback);
|
||||
}else{
|
||||
removeBackCallback(discardConfirmationCallback);
|
||||
}
|
||||
}
|
||||
if(hasDraft()){
|
||||
confirmDiscardDraftAndFinish();
|
||||
return true;
|
||||
}
|
||||
if(sendingOverlay!=null)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -842,7 +853,10 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
private void confirmDiscardDraftAndFinish(){
|
||||
new M3AlertDialogBuilder(getActivity())
|
||||
.setTitle(editingStatus==null ? R.string.discard_draft : R.string.discard_changes)
|
||||
.setPositiveButton(R.string.discard, (dialog, which)->Nav.finish(this))
|
||||
.setPositiveButton(R.string.discard, (dialog, which)->{
|
||||
removeBackCallback(discardConfirmationCallback);
|
||||
Nav.finish(this);
|
||||
})
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show();
|
||||
}
|
||||
|
@ -32,12 +32,11 @@ import java.util.Collections;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import me.grishka.appkit.fragments.OnBackPressedListener;
|
||||
import me.grishka.appkit.imageloader.ViewImageLoader;
|
||||
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
|
||||
import me.grishka.appkit.utils.V;
|
||||
|
||||
public class ComposeImageDescriptionFragment extends MastodonToolbarFragment implements OnBackPressedListener{
|
||||
public class ComposeImageDescriptionFragment extends MastodonToolbarFragment{
|
||||
private static final String TAG="ComposeImageDescription";
|
||||
|
||||
private String accountID, attachmentID;
|
||||
@ -138,9 +137,9 @@ public class ComposeImageDescriptionFragment extends MastodonToolbarFragment imp
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onBackPressed(){
|
||||
public void onStop(){
|
||||
super.onStop();
|
||||
deliverResult();
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -42,13 +42,11 @@ import me.grishka.appkit.Nav;
|
||||
import me.grishka.appkit.api.Callback;
|
||||
import me.grishka.appkit.api.ErrorResponse;
|
||||
import me.grishka.appkit.api.SimpleCallback;
|
||||
import me.grishka.appkit.fragments.OnBackPressedListener;
|
||||
import me.grishka.appkit.fragments.WindowInsetsAwareFragment;
|
||||
import me.grishka.appkit.utils.CubicBezierInterpolator;
|
||||
import me.grishka.appkit.utils.V;
|
||||
import me.grishka.appkit.views.FragmentRootLinearLayout;
|
||||
|
||||
public class CreateListAddMembersFragment extends BaseAccountListFragment implements OnBackPressedListener, AddNewListMembersFragment.Listener{
|
||||
public class CreateListAddMembersFragment extends BaseAccountListFragment implements AddNewListMembersFragment.Listener{
|
||||
private FollowList followList;
|
||||
private Button nextButton;
|
||||
private View buttonBar;
|
||||
@ -59,6 +57,7 @@ public class CreateListAddMembersFragment extends BaseAccountListFragment implem
|
||||
private WindowInsets lastInsets;
|
||||
private boolean dismissingSearchFragment;
|
||||
private HashSet<String> accountIDsInList=new HashSet<>();
|
||||
private Runnable searchFragmentDismisser=this::dismissSearchFragment;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState){
|
||||
@ -156,6 +155,7 @@ public class CreateListAddMembersFragment extends BaseAccountListFragment implem
|
||||
searchFragmentContainer.animate().translationX(0).alpha(1).setDuration(300).withLayer().setInterpolator(CubicBezierInterpolator.DEFAULT).withEndAction(()->{
|
||||
rootView.setVisibility(View.GONE);
|
||||
}).start();
|
||||
addBackCallback(searchFragmentDismisser);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -183,6 +183,7 @@ public class CreateListAddMembersFragment extends BaseAccountListFragment implem
|
||||
private void dismissSearchFragment(){
|
||||
if(searchFragment==null || dismissingSearchFragment)
|
||||
return;
|
||||
removeBackCallback(searchFragmentDismisser);
|
||||
dismissingSearchFragment=true;
|
||||
rootView.setVisibility(View.VISIBLE);
|
||||
searchFragmentContainer.animate().translationX(V.dp(100)).alpha(0).setDuration(200).withLayer().setInterpolator(CubicBezierInterpolator.DEFAULT).withEndAction(()->{
|
||||
@ -201,15 +202,6 @@ public class CreateListAddMembersFragment extends BaseAccountListFragment implem
|
||||
Nav.finish(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onBackPressed(){
|
||||
if(searchFragment!=null){
|
||||
dismissSearchFragment();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccountInList(AccountViewModel account){
|
||||
return accountIDsInList.contains(account.account.id);
|
||||
|
@ -30,8 +30,8 @@ import org.joinmastodon.android.fragments.onboarding.OnboardingFollowSuggestions
|
||||
import org.joinmastodon.android.model.Account;
|
||||
import org.joinmastodon.android.model.Notification;
|
||||
import org.joinmastodon.android.model.PaginatedResponse;
|
||||
import org.joinmastodon.android.ui.sheets.AccountSwitcherSheet;
|
||||
import org.joinmastodon.android.ui.OutlineProviders;
|
||||
import org.joinmastodon.android.ui.sheets.AccountSwitcherSheet;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
import org.joinmastodon.android.ui.views.TabBar;
|
||||
import org.joinmastodon.android.utils.ObjectIdComparator;
|
||||
@ -48,13 +48,12 @@ import me.grishka.appkit.api.Callback;
|
||||
import me.grishka.appkit.api.ErrorResponse;
|
||||
import me.grishka.appkit.fragments.AppKitFragment;
|
||||
import me.grishka.appkit.fragments.LoaderFragment;
|
||||
import me.grishka.appkit.fragments.OnBackPressedListener;
|
||||
import me.grishka.appkit.imageloader.ViewImageLoader;
|
||||
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
|
||||
import me.grishka.appkit.utils.V;
|
||||
import me.grishka.appkit.views.FragmentRootLinearLayout;
|
||||
|
||||
public class HomeFragment extends AppKitFragment implements OnBackPressedListener{
|
||||
public class HomeFragment extends AppKitFragment{
|
||||
private FragmentRootLinearLayout content;
|
||||
private HomeTimelineFragment homeTimelineFragment;
|
||||
private NotificationsListFragment notificationsFragment;
|
||||
@ -272,15 +271,6 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onBackPressed(){
|
||||
if(currentTab==R.id.tab_profile)
|
||||
return profileFragment.onBackPressed();
|
||||
if(currentTab==R.id.tab_search)
|
||||
return searchFragment.onBackPressed();
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState){
|
||||
super.onSaveInstanceState(outState);
|
||||
|
@ -44,12 +44,11 @@ import java.util.stream.Collectors;
|
||||
import me.grishka.appkit.Nav;
|
||||
import me.grishka.appkit.api.Callback;
|
||||
import me.grishka.appkit.api.ErrorResponse;
|
||||
import me.grishka.appkit.fragments.OnBackPressedListener;
|
||||
import me.grishka.appkit.utils.CubicBezierInterpolator;
|
||||
import me.grishka.appkit.utils.V;
|
||||
import me.grishka.appkit.views.FragmentRootLinearLayout;
|
||||
|
||||
public class ListMembersFragment extends PaginatedAccountListFragment implements AddNewListMembersFragment.Listener, OnBackPressedListener{
|
||||
public class ListMembersFragment extends PaginatedAccountListFragment implements AddNewListMembersFragment.Listener{
|
||||
private ImageButton fab;
|
||||
private FollowList followList;
|
||||
private boolean inSelectionMode;
|
||||
@ -63,6 +62,8 @@ public class ListMembersFragment extends PaginatedAccountListFragment implements
|
||||
private WindowInsets lastInsets;
|
||||
private HashSet<String> accountIDsInList=new HashSet<>();
|
||||
private boolean dismissingSearchFragment;
|
||||
private Runnable searchFragmentDismisser=this::dismissSearchFragment;;
|
||||
private Runnable actionModeDismisser=()->actionMode.finish();
|
||||
|
||||
public ListMembersFragment(){
|
||||
setListLayoutId(R.layout.recycler_fragment_with_fab);
|
||||
@ -214,6 +215,7 @@ public class ListMembersFragment extends PaginatedAccountListFragment implements
|
||||
searchFragmentContainer.animate().translationX(0).alpha(1).setDuration(300).withLayer().setInterpolator(CubicBezierInterpolator.DEFAULT).withEndAction(()->{
|
||||
rootView.setVisibility(View.GONE);
|
||||
}).start();
|
||||
addBackCallback(searchFragmentDismisser);
|
||||
}
|
||||
|
||||
private void onItemClick(AccountViewHolder holder){
|
||||
@ -293,9 +295,11 @@ public class ListMembersFragment extends PaginatedAccountListFragment implements
|
||||
selectedAccounts.clear();
|
||||
updateItemsForSelectionModeTransition();
|
||||
V.setVisibilityAnimated(fab, View.VISIBLE);
|
||||
removeBackCallback(actionModeDismisser);
|
||||
}
|
||||
});
|
||||
updateActionModeTitle();
|
||||
addBackCallback(actionModeDismisser);
|
||||
}
|
||||
|
||||
private void updateActionModeTitle(){
|
||||
@ -371,15 +375,6 @@ public class ListMembersFragment extends PaginatedAccountListFragment implements
|
||||
removeAccounts(Set.of(account.account.id), onDone);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onBackPressed(){
|
||||
if(searchFragment!=null){
|
||||
dismissSearchFragment();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void dismissSearchFragment(){
|
||||
if(searchFragment==null || dismissingSearchFragment)
|
||||
return;
|
||||
@ -393,6 +388,7 @@ public class ListMembersFragment extends PaginatedAccountListFragment implements
|
||||
searchFragment=null;
|
||||
dismissingSearchFragment=false;
|
||||
}).start();
|
||||
removeBackCallback(searchFragmentDismisser);
|
||||
getActivity().getSystemService(InputMethodManager.class).hideSoftInputFromWindow(contentView.getWindowToken(), 0);
|
||||
}
|
||||
|
||||
|
@ -100,14 +100,13 @@ import me.grishka.appkit.api.ErrorResponse;
|
||||
import me.grishka.appkit.api.SimpleCallback;
|
||||
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
||||
import me.grishka.appkit.fragments.LoaderFragment;
|
||||
import me.grishka.appkit.fragments.OnBackPressedListener;
|
||||
import me.grishka.appkit.imageloader.ViewImageLoader;
|
||||
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
|
||||
import me.grishka.appkit.utils.CubicBezierInterpolator;
|
||||
import me.grishka.appkit.utils.V;
|
||||
import me.grishka.appkit.views.FragmentRootLinearLayout;
|
||||
|
||||
public class ProfileFragment extends LoaderFragment implements OnBackPressedListener, ScrollableToTop{
|
||||
public class ProfileFragment extends LoaderFragment implements ScrollableToTop{
|
||||
private static final int AVATAR_RESULT=722;
|
||||
private static final int COVER_RESULT=343;
|
||||
|
||||
@ -158,6 +157,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
private Animator tabBarColorAnim;
|
||||
private MenuItem editSaveMenuItem;
|
||||
private boolean savingEdits;
|
||||
private Runnable editModeBackCallback=this::onEditModeBackCallback;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState){
|
||||
@ -983,12 +983,14 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
refreshLayout.setEnabled(false);
|
||||
editDirty=false;
|
||||
V.setVisibilityAnimated(fab, View.GONE);
|
||||
addBackCallback(editModeBackCallback);
|
||||
}
|
||||
|
||||
private void exitEditMode(){
|
||||
if(!isInEditMode)
|
||||
throw new IllegalStateException();
|
||||
isInEditMode=false;
|
||||
removeBackCallback(editModeBackCallback);
|
||||
|
||||
invalidateOptionsMenu();
|
||||
actionButton.setText(R.string.edit_profile);
|
||||
@ -1098,23 +1100,18 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
updateRelationship();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onBackPressed(){
|
||||
if(isInEditMode){
|
||||
if(savingEdits)
|
||||
return true;
|
||||
if(editDirty || aboutFragment.isEditDirty()){
|
||||
new M3AlertDialogBuilder(getActivity())
|
||||
.setTitle(R.string.discard_changes)
|
||||
.setPositiveButton(R.string.discard, (dlg, btn)->exitEditMode())
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show();
|
||||
}else{
|
||||
exitEditMode();
|
||||
}
|
||||
return true;
|
||||
private void onEditModeBackCallback(){
|
||||
if(savingEdits)
|
||||
return;
|
||||
if(editDirty || aboutFragment.isEditDirty()){
|
||||
new M3AlertDialogBuilder(getActivity())
|
||||
.setTitle(R.string.discard_changes)
|
||||
.setPositiveButton(R.string.discard, (dlg, btn)->exitEditMode())
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show();
|
||||
}else{
|
||||
exitEditMode();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private List<Attachment> createFakeAttachments(String url, Drawable drawable){
|
||||
|
@ -53,6 +53,8 @@ import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import android.window.OnBackInvokedCallback;
|
||||
import android.window.OnBackInvokedDispatcher;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.EncodeHintType;
|
||||
@ -144,12 +146,16 @@ public class ProfileQrCodeFragment extends AppKitFragment{
|
||||
if(!isTablet){
|
||||
getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
||||
}
|
||||
dlg.setOnKeyListener((dialog, keyCode, event)->{
|
||||
if(keyCode==KeyEvent.KEYCODE_BACK && event.getAction()==KeyEvent.ACTION_DOWN){
|
||||
dismiss();
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.TIRAMISU){
|
||||
dlg.getOnBackInvokedDispatcher().registerOnBackInvokedCallback(OnBackInvokedDispatcher.PRIORITY_DEFAULT, this::dismiss);
|
||||
}else{
|
||||
dlg.setOnKeyListener((dialog, keyCode, event)->{
|
||||
if(keyCode==KeyEvent.KEYCODE_BACK && event.getAction()==KeyEvent.ACTION_DOWN){
|
||||
dismiss();
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -36,10 +36,9 @@ import androidx.viewpager2.widget.ViewPager2;
|
||||
import me.grishka.appkit.Nav;
|
||||
import me.grishka.appkit.fragments.AppKitFragment;
|
||||
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
||||
import me.grishka.appkit.fragments.OnBackPressedListener;
|
||||
import me.grishka.appkit.utils.V;
|
||||
|
||||
public class DiscoverFragment extends AppKitFragment implements ScrollableToTop, OnBackPressedListener{
|
||||
public class DiscoverFragment extends AppKitFragment implements ScrollableToTop{
|
||||
private static final int QUERY_RESULT=937;
|
||||
private static final int SCAN_RESULT=456;
|
||||
|
||||
@ -62,6 +61,7 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
|
||||
private String accountID;
|
||||
private String currentQuery;
|
||||
private Intent scannerIntent;
|
||||
private Runnable searchExitCallback=this::exitSearch;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState){
|
||||
@ -232,6 +232,7 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
|
||||
searchBack.setEnabled(true);
|
||||
searchBack.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
|
||||
tabsDivider.setVisibility(View.GONE);
|
||||
addBackCallback(searchExitCallback);
|
||||
}
|
||||
}
|
||||
|
||||
@ -248,6 +249,7 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
|
||||
searchBack.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
|
||||
tabsDivider.setVisibility(View.VISIBLE);
|
||||
currentQuery=null;
|
||||
removeBackCallback(searchExitCallback);
|
||||
}
|
||||
|
||||
private Fragment getFragmentForPage(int page){
|
||||
@ -260,15 +262,6 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onBackPressed(){
|
||||
if(searchActive){
|
||||
exitSearch();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFragmentResult(int reqCode, boolean success, Bundle result){
|
||||
if(reqCode==QUERY_RESULT && success){
|
||||
|
@ -57,14 +57,13 @@ import androidx.recyclerview.widget.RecyclerView;
|
||||
import me.grishka.appkit.Nav;
|
||||
import me.grishka.appkit.api.SimpleCallback;
|
||||
import me.grishka.appkit.fragments.CustomTransitionsFragment;
|
||||
import me.grishka.appkit.fragments.OnBackPressedListener;
|
||||
import me.grishka.appkit.imageloader.ImageLoaderRecyclerAdapter;
|
||||
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
|
||||
import me.grishka.appkit.utils.MergeRecyclerAdapter;
|
||||
import me.grishka.appkit.utils.V;
|
||||
import me.grishka.appkit.views.UsableRecyclerView;
|
||||
|
||||
public class SearchQueryFragment extends MastodonRecyclerFragment<SearchResultViewModel> implements CustomTransitionsFragment, OnBackPressedListener{
|
||||
public class SearchQueryFragment extends MastodonRecyclerFragment<SearchResultViewModel> implements CustomTransitionsFragment{
|
||||
private static final Pattern HASHTAG_REGEX=Pattern.compile("^(\\w*[a-zA-Z·]\\w*)$", Pattern.CASE_INSENSITIVE);
|
||||
private static final Pattern USERNAME_REGEX=Pattern.compile("^@?([a-z0-9_-]+)(@[^\\s]+)?$", Pattern.CASE_INSENSITIVE);
|
||||
|
||||
@ -371,6 +370,11 @@ public class SearchQueryFragment extends MastodonRecyclerFragment<SearchResultVi
|
||||
container.invalidateOutline();
|
||||
navigationIcon.invalidateSelf();
|
||||
});
|
||||
if(!enter){
|
||||
String initialQuery=getArguments().getString("query");
|
||||
searchViewHelper.setQuery(TextUtils.isEmpty(initialQuery) ? "" : initialQuery);
|
||||
currentQuery=initialQuery;
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
@ -437,14 +441,6 @@ public class SearchQueryFragment extends MastodonRecyclerFragment<SearchResultVi
|
||||
Nav.finish(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onBackPressed(){
|
||||
String initialQuery=getArguments().getString("query");
|
||||
searchViewHelper.setQuery(TextUtils.isEmpty(initialQuery) ? "" : initialQuery);
|
||||
currentQuery=initialQuery;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static class AnimatableOutlineProvider extends ViewOutlineProvider{
|
||||
private float boundsFraction, radius;
|
||||
private final Rect boundsFrom, boundsTo;
|
||||
|
@ -58,12 +58,11 @@ import androidx.recyclerview.widget.RecyclerView;
|
||||
import me.grishka.appkit.Nav;
|
||||
import me.grishka.appkit.api.Callback;
|
||||
import me.grishka.appkit.api.ErrorResponse;
|
||||
import me.grishka.appkit.fragments.OnBackPressedListener;
|
||||
import me.grishka.appkit.utils.BindableViewHolder;
|
||||
import me.grishka.appkit.utils.V;
|
||||
import me.grishka.appkit.views.UsableRecyclerView;
|
||||
|
||||
public class InstanceCatalogSignupFragment extends InstanceCatalogFragment implements OnBackPressedListener{
|
||||
public class InstanceCatalogSignupFragment extends InstanceCatalogFragment{
|
||||
private View topBar;
|
||||
|
||||
private List<String> languages=Collections.emptyList();
|
||||
@ -84,6 +83,8 @@ public class InstanceCatalogSignupFragment extends InstanceCatalogFragment imple
|
||||
private String inviteCode, inviteCodeHost;
|
||||
private AlertDialog currentInviteLinkAlert;
|
||||
|
||||
private Runnable exitQueryModeCallback=()->setSearchQueryMode(false);
|
||||
|
||||
public InstanceCatalogSignupFragment(){
|
||||
super(R.layout.fragment_onboarding_common, 10);
|
||||
}
|
||||
@ -582,19 +583,13 @@ public class InstanceCatalogSignupFragment extends InstanceCatalogFragment imple
|
||||
super.onApplyWindowInsets(insets.replaceSystemWindowInsets(insets.getSystemWindowInsetLeft(), 0, insets.getSystemWindowInsetRight(), insets.getSystemWindowInsetBottom()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onBackPressed(){
|
||||
if(searchQueryMode){
|
||||
setSearchQueryMode(false);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void setSearchQueryMode(boolean enabled){
|
||||
if(searchQueryMode==enabled)
|
||||
return;
|
||||
searchQueryMode=enabled;
|
||||
RelativeLayout.LayoutParams lp=(RelativeLayout.LayoutParams) searchEdit.getLayoutParams();
|
||||
if(searchQueryMode){
|
||||
addBackCallback(exitQueryModeCallback);
|
||||
filtersScroll.setVisibility(View.GONE);
|
||||
lp.removeRule(RelativeLayout.END_OF);
|
||||
backBtn.setScaleX(0.83333333f);
|
||||
@ -602,6 +597,7 @@ public class InstanceCatalogSignupFragment extends InstanceCatalogFragment imple
|
||||
backBtn.setTranslationX(V.dp(8));
|
||||
searchEdit.setCompoundDrawableTintList(ColorStateList.valueOf(0));
|
||||
}else{
|
||||
removeBackCallback(exitQueryModeCallback);
|
||||
filtersScroll.setVisibility(View.VISIBLE);
|
||||
focusThing.requestFocus();
|
||||
searchEdit.setText("");
|
||||
|
@ -24,6 +24,7 @@ import org.joinmastodon.android.model.FilterKeyword;
|
||||
import org.joinmastodon.android.model.viewmodel.CheckableListItem;
|
||||
import org.joinmastodon.android.model.viewmodel.ListItem;
|
||||
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
||||
import org.joinmastodon.android.ui.utils.SimpleTextWatcher;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
import org.joinmastodon.android.ui.views.FloatingHintEditTextLayout;
|
||||
import org.parceler.Parcels;
|
||||
@ -44,11 +45,10 @@ import androidx.recyclerview.widget.RecyclerView;
|
||||
import me.grishka.appkit.Nav;
|
||||
import me.grishka.appkit.api.Callback;
|
||||
import me.grishka.appkit.api.ErrorResponse;
|
||||
import me.grishka.appkit.fragments.OnBackPressedListener;
|
||||
import me.grishka.appkit.utils.MergeRecyclerAdapter;
|
||||
import me.grishka.appkit.utils.SingleViewRecyclerAdapter;
|
||||
|
||||
public class EditFilterFragment extends BaseSettingsFragment<Void> implements OnBackPressedListener{
|
||||
public class EditFilterFragment extends BaseSettingsFragment<Void>{
|
||||
private static final int WORDS_RESULT=370;
|
||||
private static final int CONTEXT_RESULT=651;
|
||||
|
||||
@ -63,6 +63,13 @@ public class EditFilterFragment extends BaseSettingsFragment<Void> implements On
|
||||
private ArrayList<String> deletedWordIDs=new ArrayList<>();
|
||||
private EnumSet<FilterContext> context=EnumSet.allOf(FilterContext.class);
|
||||
private boolean dirty;
|
||||
private boolean wasDirty;
|
||||
|
||||
private Runnable confirmCallback=()->{
|
||||
if(isDirty()){
|
||||
UiUtils.showConfirmationAlert(getActivity(), R.string.discard_changes, 0, R.string.discard, ()->Nav.finish(this));
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState){
|
||||
@ -101,6 +108,7 @@ public class EditFilterFragment extends BaseSettingsFragment<Void> implements On
|
||||
titleEditLayout.updateHint();
|
||||
if(filter!=null)
|
||||
titleEdit.setText(filter.title);
|
||||
titleEdit.addTextChangedListener(new SimpleTextWatcher(e->updateBackCallback()));
|
||||
|
||||
MergeRecyclerAdapter adapter=new MergeRecyclerAdapter();
|
||||
adapter.addAdapter(new SingleViewRecyclerAdapter(titleEditLayout));
|
||||
@ -158,6 +166,7 @@ public class EditFilterFragment extends BaseSettingsFragment<Void> implements On
|
||||
}
|
||||
a.dismiss();
|
||||
}
|
||||
updateBackCallback();
|
||||
})
|
||||
.show();
|
||||
alert.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
|
||||
@ -309,6 +318,7 @@ public class EditFilterFragment extends BaseSettingsFragment<Void> implements On
|
||||
}
|
||||
deletedWordIDs.addAll(result.getStringArrayList("deleted"));
|
||||
}
|
||||
updateBackCallback();
|
||||
}
|
||||
}
|
||||
|
||||
@ -317,11 +327,19 @@ public class EditFilterFragment extends BaseSettingsFragment<Void> implements On
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onBackPressed(){
|
||||
if(isDirty()){
|
||||
UiUtils.showConfirmationAlert(getActivity(), R.string.discard_changes, 0, R.string.discard, ()->Nav.finish(this));
|
||||
return true;
|
||||
protected void toggleCheckableItem(ListItem<?> item){
|
||||
super.toggleCheckableItem(item);
|
||||
updateBackCallback();
|
||||
}
|
||||
|
||||
private void updateBackCallback(){
|
||||
boolean dirty=isDirty();
|
||||
if(dirty!=wasDirty){
|
||||
wasDirty=dirty;
|
||||
if(dirty)
|
||||
addBackCallback(confirmCallback);
|
||||
else
|
||||
removeBackCallback(confirmCallback);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -11,9 +11,7 @@ import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import me.grishka.appkit.fragments.OnBackPressedListener;
|
||||
|
||||
public class FilterContextFragment extends BaseSettingsFragment<FilterContext> implements OnBackPressedListener{
|
||||
public class FilterContextFragment extends BaseSettingsFragment<FilterContext>{
|
||||
private EnumSet<FilterContext> context;
|
||||
|
||||
@Override
|
||||
@ -33,7 +31,8 @@ public class FilterContextFragment extends BaseSettingsFragment<FilterContext> i
|
||||
protected void doLoadData(int offset, int count){}
|
||||
|
||||
@Override
|
||||
public boolean onBackPressed(){
|
||||
public void onStop(){
|
||||
super.onStop();
|
||||
context=EnumSet.noneOf(FilterContext.class);
|
||||
for(ListItem<FilterContext> item:data){
|
||||
if(((CheckableListItem<FilterContext>) item).checked)
|
||||
@ -42,6 +41,5 @@ public class FilterContextFragment extends BaseSettingsFragment<FilterContext> i
|
||||
Bundle args=new Bundle();
|
||||
args.putSerializable("context", context);
|
||||
setResult(true, args);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package org.joinmastodon.android.fragments.settings;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
import android.text.InputType;
|
||||
@ -11,11 +10,9 @@ import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowInsets;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageButton;
|
||||
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.model.FilterKeyword;
|
||||
@ -33,15 +30,15 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import me.grishka.appkit.fragments.OnBackPressedListener;
|
||||
import me.grishka.appkit.utils.V;
|
||||
|
||||
public class FilterWordsFragment extends BaseSettingsFragment<FilterKeyword> implements OnBackPressedListener{
|
||||
public class FilterWordsFragment extends BaseSettingsFragment<FilterKeyword>{
|
||||
private Button fab;
|
||||
private ActionMode actionMode;
|
||||
private ArrayList<ListItem<FilterKeyword>> selectedItems=new ArrayList<>();
|
||||
private ArrayList<String> deletedItemIDs=new ArrayList<>();
|
||||
private MenuItem deleteItem;
|
||||
private Runnable actionModeDismisser=()->actionMode.finish();
|
||||
|
||||
public FilterWordsFragment(){
|
||||
setListLayoutId(R.layout.recycler_fragment_with_text_fab);
|
||||
@ -80,12 +77,12 @@ public class FilterWordsFragment extends BaseSettingsFragment<FilterKeyword> imp
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onBackPressed(){
|
||||
public void onStop(){
|
||||
super.onStop();
|
||||
Bundle result=new Bundle();
|
||||
result.putParcelableArrayList("words", (ArrayList<? extends Parcelable>) data.stream().map(i->i.parentObject).map(Parcels::wrap).collect(Collectors.toCollection(ArrayList::new)));
|
||||
result.putStringArrayList("deleted", deletedItemIDs);
|
||||
setResult(true, result);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -259,6 +256,7 @@ public class FilterWordsFragment extends BaseSettingsFragment<FilterKeyword> imp
|
||||
}
|
||||
itemsAdapter.notifyItemRangeChanged(0, data.size());
|
||||
updateActionModeTitle();
|
||||
addBackCallback(actionModeDismisser);
|
||||
}
|
||||
|
||||
private void leaveSelectionMode(boolean fromActionMode){
|
||||
@ -280,6 +278,7 @@ public class FilterWordsFragment extends BaseSettingsFragment<FilterKeyword> imp
|
||||
data.set(i, newItem);
|
||||
}
|
||||
itemsAdapter.notifyItemRangeChanged(0, data.size());
|
||||
removeBackCallback(actionModeDismisser);
|
||||
}
|
||||
|
||||
private void updateActionModeTitle(){
|
||||
|
@ -54,6 +54,7 @@ import android.widget.SeekBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import android.widget.Toolbar;
|
||||
import android.window.OnBackInvokedDispatcher;
|
||||
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.MastodonAPIController;
|
||||
@ -169,7 +170,7 @@ public class PhotoViewer implements ZoomPanView.Listener{
|
||||
windowView=new FrameLayout(activity){
|
||||
@Override
|
||||
public boolean dispatchKeyEvent(KeyEvent event){
|
||||
if(event.getKeyCode()==KeyEvent.KEYCODE_BACK){
|
||||
if(Build.VERSION.SDK_INT<Build.VERSION_CODES.TIRAMISU && event.getKeyCode()==KeyEvent.KEYCODE_BACK){
|
||||
if(event.getAction()==KeyEvent.ACTION_DOWN){
|
||||
onStartSwipeToDismissTransition(0f);
|
||||
}
|
||||
@ -257,6 +258,10 @@ public class PhotoViewer implements ZoomPanView.Listener{
|
||||
wlp.layoutInDisplayCutoutMode=Build.VERSION.SDK_INT>=30 ? WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS : WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
|
||||
windowView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
|
||||
wm.addView(windowView, wlp);
|
||||
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.TIRAMISU){
|
||||
// TODO make use of the progress callback for nicer animation
|
||||
windowView.findOnBackInvokedDispatcher().registerOnBackInvokedCallback(OnBackInvokedDispatcher.PRIORITY_DEFAULT, ()->onStartSwipeToDismissTransition(0));
|
||||
}
|
||||
|
||||
windowView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener(){
|
||||
@Override
|
||||
|
@ -10,6 +10,7 @@ import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Build;
|
||||
import android.view.Gravity;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
@ -19,6 +20,7 @@ import android.view.ViewTreeObserver;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.Toolbar;
|
||||
import android.window.OnBackInvokedDispatcher;
|
||||
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.ui.OutlineProviders;
|
||||
@ -83,6 +85,15 @@ public class ToolbarDropdownMenuController{
|
||||
.withLayer()
|
||||
.start();
|
||||
controllerStack.add(initialSubmenu);
|
||||
|
||||
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.TIRAMISU){
|
||||
windowView.findOnBackInvokedDispatcher().registerOnBackInvokedCallback(OnBackInvokedDispatcher.PRIORITY_DEFAULT, ()->{
|
||||
if(controllerStack.size()>1)
|
||||
popSubmenuController();
|
||||
else
|
||||
dismiss();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void dismiss(){
|
||||
@ -243,7 +254,7 @@ public class ToolbarDropdownMenuController{
|
||||
|
||||
@Override
|
||||
public boolean dispatchKeyEvent(KeyEvent event){
|
||||
if(event.getKeyCode()==KeyEvent.KEYCODE_BACK){
|
||||
if(Build.VERSION.SDK_INT<Build.VERSION_CODES.TIRAMISU && event.getKeyCode()==KeyEvent.KEYCODE_BACK){
|
||||
if(event.getAction()==KeyEvent.ACTION_DOWN){
|
||||
if(controllerStack.size()>1)
|
||||
popSubmenuController();
|
||||
|
Loading…
x
Reference in New Issue
Block a user