diff --git a/mastodon/build.gradle b/mastodon/build.gradle index 18684e10..f119b73a 100644 --- a/mastodon/build.gradle +++ b/mastodon/build.gradle @@ -13,7 +13,7 @@ android { applicationId "org.joinmastodon.android" minSdk 23 targetSdk 33 - versionCode 97 + versionCode 99 versionName "2.5.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } diff --git a/mastodon/src/main/java/org/joinmastodon/android/MainActivity.java b/mastodon/src/main/java/org/joinmastodon/android/MainActivity.java index 02cc15b4..0fc11997 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/MainActivity.java +++ b/mastodon/src/main/java/org/joinmastodon/android/MainActivity.java @@ -61,6 +61,7 @@ public class MainActivity extends FragmentStackActivity{ @Override protected void onNewIntent(Intent intent){ super.onNewIntent(intent); + setIntent(intent); if(intent.getBooleanExtra("fromNotification", false)){ String accountID=intent.getStringExtra("accountID"); AccountSession accountSession; @@ -85,6 +86,8 @@ public class MainActivity extends FragmentStackActivity{ showCompose(); }else if(Intent.ACTION_VIEW.equals(intent.getAction())){ handleURL(intent.getData(), null); + }else if(intent.getBooleanExtra("explore", false)){ + restartHomeFragment(); }/*else if(intent.hasExtra(PackageInstaller.EXTRA_STATUS) && GithubSelfUpdater.needSelfUpdating()){ GithubSelfUpdater.getInstance().handleIntentFromInstaller(intent, this); }*/ @@ -211,6 +214,8 @@ public class MainActivity extends FragmentStackActivity{ } }else if(intent.getBooleanExtra("compose", false)){ showCompose(); + }else if(intent.getBooleanExtra("explore", false) && fragment instanceof HomeFragment hf){ + getWindow().getDecorView().post(()->hf.setCurrentTab(R.id.tab_search)); }else if(Intent.ACTION_VIEW.equals(intent.getAction())){ handleURL(intent.getData(), null); }else{ @@ -218,4 +223,10 @@ public class MainActivity extends FragmentStackActivity{ } } } + + public Fragment getTopmostFragment(){ + if(fragmentContainers.isEmpty()) + return null; + return getFragmentManager().findFragmentById(fragmentContainers.get(fragmentContainers.size()-1).getId()); + } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/api/requests/catalog/GetCatalogInstances.java b/mastodon/src/main/java/org/joinmastodon/android/api/requests/catalog/GetCatalogInstances.java index 54a55df5..a1f6a159 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/api/requests/catalog/GetCatalogInstances.java +++ b/mastodon/src/main/java/org/joinmastodon/android/api/requests/catalog/GetCatalogInstances.java @@ -13,11 +13,13 @@ import java.util.List; public class GetCatalogInstances extends MastodonAPIRequest>{ private String lang, category; + private boolean includeClosedSignups; - public GetCatalogInstances(String lang, String category){ + public GetCatalogInstances(String lang, String category, boolean includeClosedSignups){ super(HttpMethod.GET, null, new TypeToken<>(){}); this.lang=lang; this.category=category; + this.includeClosedSignups=includeClosedSignups; } @Override @@ -30,6 +32,8 @@ public class GetCatalogInstances extends MastodonAPIRequest exten protected HashMap relationships=new HashMap<>(); protected Rect tmpRect=new Rect(); protected TypedObjectPool attachmentViewsPool=new TypedObjectPool<>(this::makeNewMediaAttachmentView); + private SpringAnimation listShakeAnimation; public BaseStatusListFragment(){ super(20); @@ -675,6 +679,17 @@ public abstract class BaseStatusListFragment exten protected void onModifyItemViewHolder(BindableViewHolder holder){} + public void shakeListView(){ + if(listShakeAnimation!=null) + listShakeAnimation.cancel(); + SpringAnimation anim=new SpringAnimation(list, DynamicAnimation.TRANSLATION_X, 0); + anim.setStartVelocity(V.dp(-500)); + anim.getSpring().setStiffness(500).setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY); + listShakeAnimation=anim; + anim.addEndListener((animation, canceled, value, velocity)->listShakeAnimation=null); + anim.start(); + } + protected class DisplayItemsAdapter extends UsableRecyclerView.Adapter> implements ImageLoaderRecyclerAdapter{ public DisplayItemsAdapter(){ diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/HashtagTimelineFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/HashtagTimelineFragment.java index 8fdbb49a..dc7a76c5 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/HashtagTimelineFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/HashtagTimelineFragment.java @@ -275,4 +275,8 @@ public class HashtagTimelineFragment extends StatusListFragment{ }) .exec(accountID); } + + public String getHashtagName(){ + return hashtagName; + } } 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 9602a1a9..deb45ae7 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java @@ -1149,7 +1149,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList startImagePicker(COVER_RESULT); }else{ Drawable drawable=cover.getDrawable(); - if(drawable==null || drawable instanceof ColorDrawable) + if(drawable==null || drawable instanceof ColorDrawable || account.headerStatic.endsWith("/missing.png")) return; currentPhotoViewer=new PhotoViewer(getActivity(), createFakeAttachments(account.header, drawable), 0, null, accountID, new SingleImagePhotoViewerListener(cover, cover, null, this, ()->currentPhotoViewer=null, ()->drawable, ()->avatarBorder.setTranslationZ(2), ()->avatarBorder.setTranslationZ(0))); diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/InstanceCatalogSignupFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/InstanceCatalogSignupFragment.java index 37cb5b84..8c18bee9 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/InstanceCatalogSignupFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/InstanceCatalogSignupFragment.java @@ -37,7 +37,6 @@ import org.joinmastodon.android.model.catalog.CatalogInstance; import org.joinmastodon.android.ui.BetterItemAnimator; import org.joinmastodon.android.ui.M3AlertDialogBuilder; import org.joinmastodon.android.ui.text.HtmlParser; -import org.joinmastodon.android.ui.utils.HideableSingleViewRecyclerAdapter; import org.joinmastodon.android.ui.utils.SimpleTextWatcher; import org.joinmastodon.android.ui.utils.UiUtils; import org.joinmastodon.android.ui.views.FilterChipView; @@ -61,8 +60,6 @@ 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.MergeRecyclerAdapter; -import me.grishka.appkit.utils.SingleViewRecyclerAdapter; import me.grishka.appkit.utils.V; import me.grishka.appkit.views.UsableRecyclerView; @@ -106,7 +103,7 @@ public class InstanceCatalogSignupFragment extends InstanceCatalogFragment imple @Override protected void doLoadData(int offset, int count){ - currentRequest=new GetCatalogInstances(null, null) + currentRequest=new GetCatalogInstances(null, null, false) .setCallback(new Callback<>(){ @Override public void onSuccess(List result){ diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/InstanceChooserLoginFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/InstanceChooserLoginFragment.java index 5f810fd3..38ed1c53 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/InstanceChooserLoginFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/InstanceChooserLoginFragment.java @@ -96,7 +96,7 @@ public class InstanceChooserLoginFragment extends InstanceCatalogFragment{ private void loadAutocompleteServers(){ loadedAutocomplete=true; - new GetCatalogInstances(null, null) + new GetCatalogInstances(null, null, true) .setCallback(new Callback<>(){ @Override public void onSuccess(List result){ diff --git a/mastodon/src/main/java/org/joinmastodon/android/model/catalog/CatalogInstance.java b/mastodon/src/main/java/org/joinmastodon/android/model/catalog/CatalogInstance.java index d6f8a7db..0d916cae 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/model/catalog/CatalogInstance.java +++ b/mastodon/src/main/java/org/joinmastodon/android/model/catalog/CatalogInstance.java @@ -7,6 +7,7 @@ import com.google.gson.annotations.SerializedName; import org.joinmastodon.android.api.AllFieldsAreRequired; import org.joinmastodon.android.api.ObjectValidationException; +import org.joinmastodon.android.api.RequiredField; import org.joinmastodon.android.model.BaseModel; import java.net.IDN; @@ -15,14 +16,18 @@ import java.util.List; import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest; import me.grishka.appkit.utils.V; -@AllFieldsAreRequired public class CatalogInstance extends BaseModel{ + @RequiredField public String domain; + @RequiredField public String version; + @RequiredField public String description; + @RequiredField public List languages; @SerializedName("region") private String _region; + @RequiredField public List categories; public String proxiedThumbnail; public int totalUsers; diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java b/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java index ce7e6f44..2c2d6eee 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java @@ -52,6 +52,7 @@ import android.widget.Toast; import org.joinmastodon.android.E; import org.joinmastodon.android.FileProvider; import org.joinmastodon.android.GlobalUserPreferences; +import org.joinmastodon.android.MainActivity; import org.joinmastodon.android.MastodonApp; import org.joinmastodon.android.R; import org.joinmastodon.android.api.requests.accounts.SetAccountBlocked; @@ -368,6 +369,8 @@ public class UiUtils{ } public static void openHashtagTimeline(Context context, String accountID, Hashtag hashtag){ + if(checkIfAlreadyDisplayingSameHashtag(context, hashtag.name)) + return; Bundle args=new Bundle(); args.putString("account", accountID); args.putParcelable("hashtag", Parcels.wrap(hashtag)); @@ -375,12 +378,22 @@ public class UiUtils{ } public static void openHashtagTimeline(Context context, String accountID, String hashtag){ + if(checkIfAlreadyDisplayingSameHashtag(context, hashtag)) + return; Bundle args=new Bundle(); args.putString("account", accountID); args.putString("hashtagName", hashtag); Nav.go((Activity)context, HashtagTimelineFragment.class, args); } + private static boolean checkIfAlreadyDisplayingSameHashtag(Context context, String hashtag){ + if(context instanceof MainActivity ma && ma.getTopmostFragment() instanceof HashtagTimelineFragment htf && htf.getHashtagName().equalsIgnoreCase(hashtag)){ + htf.shakeListView(); + return true; + } + return false; + } + public static void showConfirmationAlert(Context context, @StringRes int title, @StringRes int message, @StringRes int confirmButton, Runnable onConfirmed){ showConfirmationAlert(context, context.getString(title), message==0 ? null : context.getString(message), context.getString(confirmButton), onConfirmed); } diff --git a/mastodon/src/main/res/drawable/ic_explore_foreground.xml b/mastodon/src/main/res/drawable/ic_explore_foreground.xml new file mode 100644 index 00000000..8b28b47b --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_explore_foreground.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/mastodon/src/main/res/mipmap-anydpi-v26/ic_shortcut_explore.xml b/mastodon/src/main/res/mipmap-anydpi-v26/ic_shortcut_explore.xml new file mode 100644 index 00000000..d8aec538 --- /dev/null +++ b/mastodon/src/main/res/mipmap-anydpi-v26/ic_shortcut_explore.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file