diff --git a/mastodon/build.gradle b/mastodon/build.gradle index f119b73aa..8fc25ecc5 100644 --- a/mastodon/build.gradle +++ b/mastodon/build.gradle @@ -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' diff --git a/mastodon/src/main/AndroidManifest.xml b/mastodon/src/main/AndroidManifest.xml index ecd97cd36..0e6c31f0b 100644 --- a/mastodon/src/main/AndroidManifest.xml +++ b/mastodon/src/main/AndroidManifest.xml @@ -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"> 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(); } diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeImageDescriptionFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeImageDescriptionFragment.java index 5e3cc22c3..efb6aa31b 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeImageDescriptionFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeImageDescriptionFragment.java @@ -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 diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/CreateListAddMembersFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/CreateListAddMembersFragment.java index afd92a0c2..a5406726b 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/CreateListAddMembersFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/CreateListAddMembersFragment.java @@ -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 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); diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeFragment.java index f40283f07..7329a4846 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeFragment.java @@ -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); diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ListMembersFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ListMembersFragment.java index f6a5b364b..13f5aba9d 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ListMembersFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ListMembersFragment.java @@ -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 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); } diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java index 2662d4f5a..0045291f5 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java @@ -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 createFakeAttachments(String url, Drawable drawable){ diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileQrCodeFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileQrCodeFragment.java index 919b13a77..32094a641 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileQrCodeFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileQrCodeFragment.java @@ -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 diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/discover/DiscoverFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/discover/DiscoverFragment.java index a1934c147..ca93bdd45 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/discover/DiscoverFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/discover/DiscoverFragment.java @@ -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){ diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/discover/SearchQueryFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/discover/SearchQueryFragment.java index 3ff7fc794..16768a38a 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/discover/SearchQueryFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/discover/SearchQueryFragment.java @@ -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 implements CustomTransitionsFragment, OnBackPressedListener{ +public class SearchQueryFragment extends MastodonRecyclerFragment 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 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(""); diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/settings/EditFilterFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/settings/EditFilterFragment.java index 49a963207..83f1f43f4 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/settings/EditFilterFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/settings/EditFilterFragment.java @@ -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 implements OnBackPressedListener{ +public class EditFilterFragment extends BaseSettingsFragment{ private static final int WORDS_RESULT=370; private static final int CONTEXT_RESULT=651; @@ -63,6 +63,13 @@ public class EditFilterFragment extends BaseSettingsFragment implements On private ArrayList deletedWordIDs=new ArrayList<>(); private EnumSet 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 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 implements On } a.dismiss(); } + updateBackCallback(); }) .show(); alert.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false); @@ -309,6 +318,7 @@ public class EditFilterFragment extends BaseSettingsFragment implements On } deletedWordIDs.addAll(result.getStringArrayList("deleted")); } + updateBackCallback(); } } @@ -317,11 +327,19 @@ public class EditFilterFragment extends BaseSettingsFragment 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; } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/settings/FilterContextFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/settings/FilterContextFragment.java index 37d911072..a98503529 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/settings/FilterContextFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/settings/FilterContextFragment.java @@ -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 implements OnBackPressedListener{ +public class FilterContextFragment extends BaseSettingsFragment{ private EnumSet context; @Override @@ -33,7 +31,8 @@ public class FilterContextFragment extends BaseSettingsFragment i protected void doLoadData(int offset, int count){} @Override - public boolean onBackPressed(){ + public void onStop(){ + super.onStop(); context=EnumSet.noneOf(FilterContext.class); for(ListItem item:data){ if(((CheckableListItem) item).checked) @@ -42,6 +41,5 @@ public class FilterContextFragment extends BaseSettingsFragment i Bundle args=new Bundle(); args.putSerializable("context", context); setResult(true, args); - return false; } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/settings/FilterWordsFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/settings/FilterWordsFragment.java index 0dec195b8..943050a99 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/settings/FilterWordsFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/settings/FilterWordsFragment.java @@ -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 implements OnBackPressedListener{ +public class FilterWordsFragment extends BaseSettingsFragment{ private Button fab; private ActionMode actionMode; private ArrayList> selectedItems=new ArrayList<>(); private ArrayList 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 imp } @Override - public boolean onBackPressed(){ + public void onStop(){ + super.onStop(); Bundle result=new Bundle(); result.putParcelableArrayList("words", (ArrayList) 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 imp } itemsAdapter.notifyItemRangeChanged(0, data.size()); updateActionModeTitle(); + addBackCallback(actionModeDismisser); } private void leaveSelectionMode(boolean fromActionMode){ @@ -280,6 +278,7 @@ public class FilterWordsFragment extends BaseSettingsFragment imp data.set(i, newItem); } itemsAdapter.notifyItemRangeChanged(0, data.size()); + removeBackCallback(actionModeDismisser); } private void updateActionModeTitle(){ diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/photoviewer/PhotoViewer.java b/mastodon/src/main/java/org/joinmastodon/android/ui/photoviewer/PhotoViewer.java index 4d22f5cb9..19cec6b78 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/photoviewer/PhotoViewer.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/photoviewer/PhotoViewer.java @@ -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=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 diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/viewcontrollers/ToolbarDropdownMenuController.java b/mastodon/src/main/java/org/joinmastodon/android/ui/viewcontrollers/ToolbarDropdownMenuController.java index 520ef51ef..e2e2b73e2 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/viewcontrollers/ToolbarDropdownMenuController.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/viewcontrollers/ToolbarDropdownMenuController.java @@ -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_INT1) popSubmenuController();