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