diff options
36 files changed, 1407 insertions, 296 deletions
diff --git a/app/build.gradle b/app/build.gradle index 37c31f3e4..6c334cbd6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,8 +13,8 @@ android { defaultConfig { minSdk 21 targetSdk 33 - versionCode 489 - versionName "3.21.2" + versionCode 490 + versionName "3.22.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } flavorDimensions "default" @@ -25,6 +25,7 @@ android { } debug { applicationIdSuffix '.debug' + pseudoLocalesEnabled true } } compileOptions { @@ -99,7 +100,7 @@ dependencies { implementation 'androidx.appcompat:appcompat:1.6.1' - implementation 'com.google.android.material:material:1.9.0-beta01' + implementation 'com.google.android.material:material:1.9.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' diff --git a/app/src/main/assets/release_notes/notes.json b/app/src/main/assets/release_notes/notes.json index dc6d3f74d..291192b06 100644 --- a/app/src/main/assets/release_notes/notes.json +++ b/app/src/main/assets/release_notes/notes.json @@ -1,5 +1,10 @@ [ { + "version": "3.22.0", + "code": "490", + "note": "Fixed:\n- Too many requests\n- Blank Home page\n- Crashes when visiting profiles\n- Some audio files cannot be uploaded" + }, + { "version": "3.21.2", "code": "489", "note": "Added:\n- Android 12+ : Customize accent colors for light/dark theme and per account (Settings > Theming > Custom accent color)\n" diff --git a/app/src/main/java/app/fedilab/android/BaseMainActivity.java b/app/src/main/java/app/fedilab/android/BaseMainActivity.java index 57fb34bdf..2483855d2 100644 --- a/app/src/main/java/app/fedilab/android/BaseMainActivity.java +++ b/app/src/main/java/app/fedilab/android/BaseMainActivity.java @@ -17,6 +17,7 @@ package app.fedilab.android; import static app.fedilab.android.BaseMainActivity.status.DISCONNECTED; import static app.fedilab.android.BaseMainActivity.status.UNKNOWN; import static app.fedilab.android.mastodon.helper.CacheHelper.deleteDir; +import static app.fedilab.android.mastodon.helper.Helper.ARG_REFRESH_NOTFICATION; import static app.fedilab.android.mastodon.helper.Helper.PREF_USER_ID; import static app.fedilab.android.mastodon.helper.Helper.PREF_USER_INSTANCE; import static app.fedilab.android.mastodon.helper.Helper.PREF_USER_SOFTWARE; @@ -172,6 +173,7 @@ import app.fedilab.android.mastodon.helper.Helper; import app.fedilab.android.mastodon.helper.MastodonHelper; import app.fedilab.android.mastodon.helper.PinnedTimelineHelper; import app.fedilab.android.mastodon.helper.PushHelper; +import app.fedilab.android.mastodon.helper.ThemeHelper; import app.fedilab.android.mastodon.ui.drawer.AccountsSearchTopBarAdapter; import app.fedilab.android.mastodon.ui.drawer.TagSearchTopBarAdapter; import app.fedilab.android.mastodon.ui.fragment.timeline.FragmentMastodonConversation; @@ -671,6 +673,11 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt } viewPager.setCurrentItem(position); } + Bundle b = new Bundle(); + b.putBoolean(ARG_REFRESH_NOTFICATION, true); + Intent intentBC = new Intent(Helper.RECEIVE_STATUS_ACTION); + intentBC.putExtras(b); + LocalBroadcastManager.getInstance(activity).sendBroadcast(intentBC); } }, 1000); intent.removeExtra(Helper.INTENT_ACTION); @@ -719,6 +726,7 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt Toasty.info(activity, activity.getString(R.string.toast_account_changed, acct), Toasty.LENGTH_LONG).show(); BaseMainActivity.currentToken = account.token; BaseMainActivity.currentUserID = account.user_id; + ThemeHelper.applyThemeColor(activity); api = account.api; SharedPreferences.Editor editor = sharedpreferences.edit(); editor.putString(PREF_USER_TOKEN, account.token); diff --git a/app/src/main/java/app/fedilab/android/mastodon/helper/CrossActionHelper.java b/app/src/main/java/app/fedilab/android/mastodon/helper/CrossActionHelper.java index b3a8867e5..c56335120 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/helper/CrossActionHelper.java +++ b/app/src/main/java/app/fedilab/android/mastodon/helper/CrossActionHelper.java @@ -508,7 +508,7 @@ public class CrossActionHelper { } } } - } catch (IOException e) { + } catch (Exception e) { e.printStackTrace(); } } diff --git a/app/src/main/java/app/fedilab/android/mastodon/helper/Helper.java b/app/src/main/java/app/fedilab/android/mastodon/helper/Helper.java index 34adc4e9d..368c493de 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/helper/Helper.java +++ b/app/src/main/java/app/fedilab/android/mastodon/helper/Helper.java @@ -194,6 +194,8 @@ public class Helper { public static final String RECEIVE_REDRAW_BOTTOM = "RECEIVE_REDRAW_BOTTOM"; public static final String RECEIVE_STATUS_ACTION = "RECEIVE_STATUS_ACTION"; + + public static final String RECEIVE_REFRESH_NOTIFICATIONS_ACTION = "RECEIVE_REFRESH_NOTIFICATIONS_ACTION"; public static final String RECEIVE_ERROR_MESSAGE = "RECEIVE_ERROR_MESSAGE"; public static final String RECEIVE_RECREATE_ACTIVITY = "RECEIVE_RECREATE_ACTIVITY"; diff --git a/app/src/main/java/app/fedilab/android/mastodon/helper/NotificationsHelper.java b/app/src/main/java/app/fedilab/android/mastodon/helper/NotificationsHelper.java index 4fb6fcbe8..32ac893fe 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/helper/NotificationsHelper.java +++ b/app/src/main/java/app/fedilab/android/mastodon/helper/NotificationsHelper.java @@ -34,11 +34,7 @@ import androidx.annotation.Nullable; import androidx.preference.PreferenceManager; import com.bumptech.glide.Glide; -import com.bumptech.glide.load.DataSource; -import com.bumptech.glide.load.engine.GlideException; -import com.bumptech.glide.request.RequestListener; import com.bumptech.glide.request.target.CustomTarget; -import com.bumptech.glide.request.target.Target; import com.bumptech.glide.request.transition.Transition; import java.util.ArrayList; @@ -67,7 +63,7 @@ public class NotificationsHelper { public static HashMap<String, String> since_ids = new HashMap<>(); - public static void task(Context context, String slug) throws DBException { + public static synchronized void task(Context context, String slug) throws DBException { SharedPreferences prefs = PreferenceManager .getDefaultSharedPreferences(context); @@ -77,10 +73,13 @@ public class NotificationsHelper { return; } String last_notifid; + last_notifid = prefs.getString(context.getString(R.string.LAST_NOTIFICATION_ID) + slug, null); if (since_ids.containsKey(slug)) { - last_notifid = since_ids.get(slug); + String last_notifid_reccorded = since_ids.get(slug); + if (last_notifid_reccorded != null && last_notifid_reccorded.compareToIgnoreCase(last_notifid) == 0) { + return; + } } else { - last_notifid = prefs.getString(context.getString(R.string.LAST_NOTIFICATION_ID) + slug, null); since_ids.put(slug, last_notifid); } @@ -379,42 +378,35 @@ public class NotificationsHelper { Helper.NotifType finalNotifType = notifType; String finalMessage = message; String finalTitle = title; - StatusAdapter.sendAction(context, Helper.ARG_REFRESH_NOTFICATION, null, null); + StatusAdapter.sendAction(context, Helper.RECEIVE_REFRESH_NOTIFICATIONS_ACTION, null, null); Runnable myRunnable = () -> Glide.with(context) .asBitmap() .load(finalNotificationUrl != null ? finalNotificationUrl : R.drawable.fedilab_logo_bubbles) - .listener(new RequestListener<Bitmap>() { - + .into(new CustomTarget<Bitmap>() { @Override - public boolean onResourceReady(Bitmap resource, Object model, Target<Bitmap> target, DataSource dataSource, boolean isFirstResource) { - return false; + public void onResourceReady(@NonNull Bitmap resource, Transition<? super Bitmap> transition) { + String lastNotif = prefs.getString(context.getString(R.string.LAST_NOTIFICATION_ID) + account.user_id + "@" + account.instance, null); + // if (lastNotif == null || Helper.compareTo(notification.id, lastNotif) > 0) { + SharedPreferences.Editor editor = prefs.edit(); + editor.putString(context.getString(R.string.LAST_NOTIFICATION_ID) + account.user_id + "@" + account.instance, notifications.get(0).id); + editor.commit(); + since_ids.put(account.user_id + "@" + account.instance, lastNotif); + Helper.notify_user(context, account, intent, resource, finalNotifType, finalTitle, finalMessage); + // } } @Override - public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) { + public void onLoadFailed(@Nullable Drawable errorDrawable) { + super.onLoadFailed(errorDrawable); String lastNotif = prefs.getString(context.getString(R.string.LAST_NOTIFICATION_ID) + account.user_id + "@" + account.instance, null); - if (lastNotif == null || Helper.compareTo(notification.id, lastNotif) > 0) { + // if (lastNotif == null || Helper.compareTo(notification.id, lastNotif) > 0) { SharedPreferences.Editor editor = prefs.edit(); since_ids.put(account.user_id + "@" + account.instance, lastNotif); editor.putString(context.getString(R.string.LAST_NOTIFICATION_ID) + account.user_id + "@" + account.instance, notifications.get(0).id); - editor.apply(); + editor.commit(); Helper.notify_user(context, account, intent, BitmapFactory.decodeResource(context.getResources(), getMainLogo(context)), finalNotifType, finalTitle, finalMessage); - } - return false; - } - }) - .into(new CustomTarget<Bitmap>() { - @Override - public void onResourceReady(@NonNull Bitmap resource, Transition<? super Bitmap> transition) { - String lastNotif = prefs.getString(context.getString(R.string.LAST_NOTIFICATION_ID) + account.user_id + "@" + account.instance, null); - if (lastNotif == null || Helper.compareTo(notification.id, lastNotif) > 0) { - SharedPreferences.Editor editor = prefs.edit(); - editor.putString(context.getString(R.string.LAST_NOTIFICATION_ID) + account.user_id + "@" + account.instance, notifications.get(0).id); - editor.apply(); - since_ids.put(account.user_id + "@" + account.instance, lastNotif); - Helper.notify_user(context, account, intent, resource, finalNotifType, finalTitle, finalMessage); - } + // } } @Override diff --git a/app/src/main/java/app/fedilab/android/mastodon/helper/TimelineHelper.java b/app/src/main/java/app/fedilab/android/mastodon/helper/TimelineHelper.java index ffdc6bdc4..e7e03ae18 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/helper/TimelineHelper.java +++ b/app/src/main/java/app/fedilab/android/mastodon/helper/TimelineHelper.java @@ -132,10 +132,14 @@ public class TimelineHelper { continue; } String content; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) - content = Html.fromHtml(status.reblog != null ? status.reblog.content : status.content, Html.FROM_HTML_MODE_LEGACY).toString(); - else - content = Html.fromHtml(status.reblog != null ? status.reblog.content : status.content).toString(); + try { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) + content = Html.fromHtml(status.reblog != null ? status.reblog.content : status.content, Html.FROM_HTML_MODE_LEGACY).toString(); + else + content = Html.fromHtml(status.reblog != null ? status.reblog.content : status.content).toString(); + } catch (Exception e) { + content = status.reblog != null ? status.reblog.content : status.content; + } Matcher m = p.matcher(content); if (m.find()) { status.filteredByApp = filter; diff --git a/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/ComposeAdapter.java b/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/ComposeAdapter.java index e175a73eb..457285b43 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/ComposeAdapter.java +++ b/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/ComposeAdapter.java @@ -431,7 +431,7 @@ public class ComposeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder if (instanceInfo != null && instanceInfo.getMimeTypeAudio() != null && instanceInfo.getMimeTypeAudio().size() > 0) { mimetypes = instanceInfo.getMimeTypeAudio().toArray(new String[0]); } else { - mimetypes = new String[]{"audio/mpeg", "audio/opus", "audio/flac", "audio/wav", "audio/ogg"}; + mimetypes = new String[]{"audio/*"}; } } else if (type == ComposeActivity.mediaType.ALL) { if (instanceInfo != null && instanceInfo.getMimeTypeOther() != null && instanceInfo.getMimeTypeOther().size() > 0) { @@ -1519,7 +1519,7 @@ public class ComposeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder if (!unlisted_changed && position == 0 && unlistedReplies && statusDraft.visibility.equalsIgnoreCase("public") && statusList.size() > 1) { statusDraft.visibility = "unlisted"; } - } else if (!unlisted_changed && position == statusCount && unlistedReplies && statusDraft.visibility.equalsIgnoreCase("public") && statusList.size() > 1) { + } else if (!unlisted_changed && position > 0 && position == statusCount && unlistedReplies && statusDraft.visibility.equalsIgnoreCase("public") && statusList.size() > 1) { statusDraft.visibility = "unlisted"; } @@ -1656,14 +1656,16 @@ public class ComposeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder } } else if (forwardTag && position > 0 && statusDraft.text != null && statusDraft.text.contains("#") && !statusList.get(position).tagAdded) { Status status = statusList.get(position - 1).reblog == null ? statusList.get(position - 1) : statusList.get(position - 1).reblog; - statusList.get(position).tagAdded = true; - int lenght = 0; - for (Tag tag : status.tags) { - lenght += ("#" + tag.name + " ").length(); + if (status.tags != null && status.tags.size() > 0) { + statusList.get(position).tagAdded = true; + int lenght = 0; + for (Tag tag : status.tags) { + lenght += ("#" + tag.name + " ").length(); + } + statusDraft.cursorPosition = statusDraft.text.length() - lenght - 3; + statusDraft.setCursorToEnd = false; + holder.binding.content.setSelection(statusDraft.text.length() - lenght - 3); } - statusDraft.cursorPosition = statusDraft.text.length() - lenght - 3; - statusDraft.setCursorToEnd = false; - holder.binding.content.setSelection(statusDraft.text.length() - lenght - 3); } if (statusDraft.spoiler_text != null) { diff --git a/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/StatusAdapter.java b/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/StatusAdapter.java index cc6e68035..bebbeba45 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/StatusAdapter.java +++ b/app/src/main/java/app/fedilab/android/mastodon/ui/drawer/StatusAdapter.java @@ -2726,10 +2726,10 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> b.putSerializable(type, status); } if (id != null) { - b.putSerializable(type, id); + b.putString(type, id); } if (type == ARG_TIMELINE_REFRESH_ALL) { - b.putSerializable(ARG_TIMELINE_REFRESH_ALL, true); + b.putBoolean(ARG_TIMELINE_REFRESH_ALL, true); } Intent intentBC = new Intent(Helper.RECEIVE_STATUS_ACTION); intentBC.putExtras(b); diff --git a/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonAccount.java b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonAccount.java index 3acf80e2a..55879749c 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonAccount.java +++ b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonAccount.java @@ -133,8 +133,9 @@ public class FragmentMastodonAccount extends Fragment { if (results != null && results.accounts.size() > 0) { remoteAccountId = results.accounts.get(0).id; fetchAccount(firstLoad, remoteAccountId); - } else { - Toasty.error(requireActivity(), getString(R.string.toast_error), Toasty.LENGTH_SHORT).show(); + } else { //FallBack the app failed to find remote accounts + checkRemotely = false; + fetchAccount(firstLoad, accountTimeline != null ? accountTimeline.id : null); } }); } else { diff --git a/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonNotification.java b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonNotification.java index 7455ecab4..d8f5bc3b6 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonNotification.java +++ b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonNotification.java @@ -72,7 +72,10 @@ public class FragmentMastodonNotification extends Fragment implements Notificati if (b != null) { Status receivedStatus = (Status) b.getSerializable(Helper.ARG_STATUS_ACTION); String delete_all_for_account_id = b.getString(Helper.ARG_DELETE_ALL_FOR_ACCOUNT_ID); - if (receivedStatus != null && notificationAdapter != null) { + boolean refreshNotifications = b.getBoolean(Helper.ARG_REFRESH_NOTFICATION, false); + if (refreshNotifications) { + scrollToTop(); + } else if (receivedStatus != null && notificationAdapter != null) { int position = getPosition(receivedStatus); if (position >= 0) { if (notificationList.get(position).status != null) { @@ -203,7 +206,7 @@ public class FragmentMastodonNotification extends Fragment implements Notificati aggregateNotification = false; LocalBroadcastManager.getInstance(requireActivity()).registerReceiver(receive_action, new IntentFilter(Helper.RECEIVE_STATUS_ACTION)); - LocalBroadcastManager.getInstance(requireActivity()).registerReceiver(receive_refresh, new IntentFilter(Helper.ARG_REFRESH_NOTFICATION)); + LocalBroadcastManager.getInstance(requireActivity()).registerReceiver(receive_refresh, new IntentFilter(Helper.RECEIVE_REFRESH_NOTIFICATIONS_ACTION)); return root; } diff --git a/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonTimeline.java b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonTimeline.java index ad1e04ba6..07e87bfda 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonTimeline.java +++ b/app/src/main/java/app/fedilab/android/mastodon/ui/fragment/timeline/FragmentMastodonTimeline.java @@ -87,6 +87,9 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter. private StatusAdapter statusAdapter; private Timeline.TimeLineEnum timelineType; private List<Status> timelineStatuses; + + private boolean retry_for_home_done; + //Handle actions that can be done in other fragments private final BroadcastReceiver receive_action = new BroadcastReceiver() { @Override @@ -376,6 +379,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter. ViewGroup container, Bundle savedInstanceState) { timelineType = Timeline.TimeLineEnum.HOME; canBeFederated = true; + retry_for_home_done = false; if (getArguments() != null) { timelineType = (Timeline.TimeLineEnum) getArguments().get(Helper.ARG_TIMELINE_TYPE); list_id = getArguments().getString(Helper.ARG_LIST_ID, null); @@ -794,6 +798,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter. boolean useCache = sharedpreferences.getBoolean(getString(R.string.SET_USE_CACHE), true); if (useCache && direction != DIRECTION.SCROLL_TOP && direction != DIRECTION.FETCH_NEW) { getCachedStatus(direction, fetchingMissing, timelineParams, fetchStatus); + } else { getLiveStatus(direction, fetchingMissing, timelineParams, true, fetchStatus); } @@ -855,7 +860,6 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter. } private void getCachedStatus(DIRECTION direction, boolean fetchingMissing, TimelinesVM.TimelineParams timelineParams, Status fetchStatus) { - if (direction == null) { timelinesVM.getTimelineCache(timelineStatuses, timelineParams) .observe(getViewLifecycleOwner(), statusesCached -> { @@ -913,7 +917,14 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter. timelinesVM.getTimeline(timelineStatuses, timelineParams) .observe(getViewLifecycleOwner(), statuses -> { initialStatuses = statuses; - initializeStatusesCommonView(statuses); + if (!retry_for_home_done && timelineType == Timeline.TimeLineEnum.HOME && (statuses == null || statuses.statuses == null || statuses.statuses.size() == 0) && timelineParams.maxId != null) { + retry_for_home_done = true; + timelineParams.maxId = null; + max_id = null; + getLiveStatus(null, fetchingMissing, timelineParams, true, fetchStatus); + } else { + initializeStatusesCommonView(statuses); + } }); } else if (direction == DIRECTION.BOTTOM) { timelinesVM.getTimeline(timelineStatuses, timelineParams) @@ -1036,15 +1047,15 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter. } else if (timelineType == Timeline.TimeLineEnum.TAG || timelineType == Timeline.TimeLineEnum.ART) { //TAG TIMELINE routeCommon(direction, fetchingMissing, fetchStatus); } else if (timelineType == Timeline.TimeLineEnum.ACCOUNT_TIMELINE) { //PROFILE TIMELINES - String tempToken; - String tempInstance; - String accountId; + final String[] tempToken = new String[1]; + final String[] tempInstance = new String[1]; + final String[] accountId = new String[1]; if (checkRemotely) { - tempToken = null; - tempInstance = remoteInstance; - accountId = accountIDInRemoteInstance; + tempToken[0] = null; + tempInstance[0] = remoteInstance; + accountId[0] = accountIDInRemoteInstance; if (accountIDInRemoteInstance == null) { - CrossActionHelper.fetchAccountInRemoteInstance(requireActivity |