diff --git a/app/build.gradle b/app/build.gradle index f6096214..8b9632e6 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -66,11 +66,13 @@ android { dimension "repo" // versionNameSuffix "-github" // appended in assemble task buildConfigField("String", "dsn", SENTRY_DSN) + buildConfigField("boolean", "isPre", "false") } fdroid { dimension "repo" versionNameSuffix "-fdroid" + buildConfigField("boolean", "isPre", "false") } } @@ -84,6 +86,7 @@ android { def suffix = "${versionName}-${flavor}_${builtType}" // eg. 19.1.0-github_debug or release if (builtType.toString() == 'release' && project.hasProperty("pre")) { + buildConfigField("boolean", "isPre", "true") // append latest commit short hash for pre-release suffix = "${versionName}.${getGitHash()}-${flavor}" // eg. 19.1.0.b123456-github } diff --git a/app/src/fdroid/java/awais/instagrabber/utils/UpdateChecker.java b/app/src/fdroid/java/awais/instagrabber/utils/UpdateChecker.java new file mode 100644 index 00000000..f25b8aa4 --- /dev/null +++ b/app/src/fdroid/java/awais/instagrabber/utils/UpdateChecker.java @@ -0,0 +1,64 @@ +package awais.instagrabber.utils; + +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; + +import org.json.JSONObject; + +import java.net.HttpURLConnection; +import java.net.URL; + +public class UpdateChecker { + private static final Object LOCK = new Object(); + private static final String TAG = UpdateChecker.class.getSimpleName(); + + private static UpdateChecker instance; + + public static UpdateChecker getInstance() { + if (instance == null) { + synchronized (LOCK) { + if (instance == null) { + instance = new UpdateChecker(); + } + } + } + return instance; + } + + /** + * Needs to be called asynchronously + * + * @return the latest version from f-droid + */ + @Nullable + public String getLatestVersion() { + HttpURLConnection conn = null; + try { + conn = (HttpURLConnection) new URL("https://f-droid.org/api/v1/packages/me.austinhuang.instagrabber").openConnection(); + conn.setUseCaches(false); + conn.setRequestProperty("User-Agent", "https://Barinsta.AustinHuang.me / mailto:Barinsta@AustinHuang.me"); + conn.connect(); + final int responseCode = conn.getResponseCode(); + if (responseCode == HttpURLConnection.HTTP_OK) { + final JSONObject data = new JSONObject(NetworkUtils.readFromConnection(conn)); + return "v" + data.getJSONArray("packages").getJSONObject(0).getString("versionName"); + // if (BuildConfig.VERSION_CODE < data.getInt("suggestedVersionCode")) { + // } + } + } catch (final Exception e) { + Log.e(TAG, "", e); + } finally { + if (conn != null) { + conn.disconnect(); + } + } + return null; + } + + public void onDownload(@NonNull final AppCompatActivity context) { + Utils.openURL(context, "https://f-droid.org/packages/me.austinhuang.instagrabber/"); + } +} diff --git a/app/src/github/java/awais/instagrabber/utils/UpdateChecker.java b/app/src/github/java/awais/instagrabber/utils/UpdateChecker.java new file mode 100644 index 00000000..70bc2699 --- /dev/null +++ b/app/src/github/java/awais/instagrabber/utils/UpdateChecker.java @@ -0,0 +1,61 @@ +package awais.instagrabber.utils; + +import android.content.Context; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.net.HttpURLConnection; +import java.net.URL; + +public class UpdateChecker { + private static final Object LOCK = new Object(); + private static final String TAG = UpdateChecker.class.getSimpleName(); + + private static UpdateChecker instance; + + public static UpdateChecker getInstance() { + if (instance == null) { + synchronized (LOCK) { + if (instance == null) { + instance = new UpdateChecker(); + } + } + } + return instance; + } + + /** + * Needs to be called asynchronously + * + * @return the latest version from Github + */ + @Nullable + public String getLatestVersion() { + HttpURLConnection conn = null; + try { + conn = (HttpURLConnection) new URL("https://github.com/austinhuang0131/barinsta/releases/latest").openConnection(); + conn.setInstanceFollowRedirects(false); + conn.setUseCaches(false); + conn.setRequestProperty("User-Agent", "https://Barinsta.AustinHuang.me / mailto:Barinsta@AustinHuang.me"); + conn.connect(); + final int responseCode = conn.getResponseCode(); + if (responseCode == HttpURLConnection.HTTP_MOVED_TEMP) { + return "v" + conn.getHeaderField("Location").split("/v")[1]; + // return !version.equals(BuildConfig.VERSION_NAME); + } + } catch (final Exception e) { + Log.e(TAG, "", e); + } finally { + if (conn != null) { + conn.disconnect(); + } + } + return null; + } + + public void onDownload(@NonNull final Context context) { + Utils.openURL(context, "https://github.com/austinhuang0131/instagrabber/releases/latest"); + } +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ce5e77c9..a958a9a3 100755 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -130,6 +130,9 @@ android:name="android.support.PARENT_ACTIVITY" android:value=".activities.MainActivity" /> + { + final Context context = getContext(); + if (context == null) return; + ProcessPhoenix.triggerRebirth(context); + }, 200); }; private final AccountSwitcherAdapter.OnAccountLongClickListener accountLongClickListener = (model, isCurrent) -> { diff --git a/app/src/main/java/awais/instagrabber/fragments/NotificationsViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/NotificationsViewerFragment.java index d9b56ce7..15d5306d 100644 --- a/app/src/main/java/awais/instagrabber/fragments/NotificationsViewerFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/NotificationsViewerFragment.java @@ -31,7 +31,6 @@ import awais.instagrabber.R; import awais.instagrabber.adapters.NotificationsAdapter; import awais.instagrabber.adapters.NotificationsAdapter.OnNotificationClickListener; import awais.instagrabber.databinding.FragmentNotificationsViewerBinding; -import awais.instagrabber.fragments.settings.MorePreferencesFragmentDirections; import awais.instagrabber.models.enums.NotificationType; import awais.instagrabber.repositories.requests.StoryViewerOptions; import awais.instagrabber.repositories.responses.FriendshipChangeResponse; @@ -79,8 +78,7 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe try { binding.swipeRefreshLayout.setRefreshing(false); Toast.makeText(getContext(), t.getMessage(), Toast.LENGTH_SHORT).show(); - } - catch(Throwable e) {} + } catch (Throwable ignored) {} } }; @@ -93,10 +91,10 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe @Override public void onPreviewClick(final Notification model) { final NotificationImage notificationImage = model.getArgs().getMedia().get(0); - final long mediaId = Long.valueOf(notificationImage.getId().split("_")[0]); + final long mediaId = Long.parseLong(notificationImage.getId().split("_")[0]); if (model.getType() == NotificationType.RESPONDED_STORY) { final NavDirections action = NotificationsViewerFragmentDirections - .actionNotificationsViewerFragmentToStoryViewerFragment( + .actionNotificationsToStory( StoryViewerOptions.forStory( mediaId, model.getArgs().getUsername())); @@ -278,8 +276,7 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe } private void openProfile(final String username) { - final NavDirections action = MorePreferencesFragmentDirections - .actionGlobalProfileFragment("@" + username); + final NavDirections action = NotificationsViewerFragmentDirections.actionGlobalProfileFragment("@" + username); NavHostFragment.findNavController(this).navigate(action); } } \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java index 82438999..ddd84ce3 100644 --- a/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java @@ -137,14 +137,14 @@ public class StoryViewerFragment extends Fragment { private String[] mentions; private QuizModel quiz; private SliderModel slider; - private MenuItem menuDownload; - private MenuItem menuDm; + private MenuItem menuDownload, menuDm, menuProfile; private SimpleExoPlayer player; // private boolean isHashtag; // private boolean isLoc; // private String highlight; - private String actionBarTitle; + private String actionBarTitle, actionBarSubtitle; private boolean fetching = false, sticking = false, shouldRefresh = true; + private boolean downloadVisible = false, dmVisible = false, profileVisible = true; private int currentFeedStoryIndex; private double sliderValue; private StoriesViewModel storiesViewModel; @@ -195,8 +195,10 @@ public class StoryViewerFragment extends Fragment { menuInflater.inflate(R.menu.story_menu, menu); menuDownload = menu.findItem(R.id.action_download); menuDm = menu.findItem(R.id.action_dms); - menuDownload.setVisible(false); - menuDm.setVisible(false); + menuProfile = menu.findItem(R.id.action_profile); + menuDownload.setVisible(downloadVisible); + menuDm.setVisible(dmVisible); + menuProfile.setVisible(profileVisible); } @Override @@ -215,7 +217,8 @@ public class StoryViewerFragment extends Fragment { else ActivityCompat.requestPermissions(requireActivity(), DownloadUtils.PERMS, 8020); return true; - } else if (itemId == R.id.action_dms) { + } + if (itemId == R.id.action_dms) { final EditText input = new EditText(context); input.setHint(R.string.reply_hint); new AlertDialog.Builder(context) @@ -259,6 +262,9 @@ public class StoryViewerFragment extends Fragment { .show(); return true; } + if (itemId == R.id.action_profile) { + openProfile("@" + currentStory.getUsername()); + } return false; } @@ -281,7 +287,9 @@ public class StoryViewerFragment extends Fragment { final ActionBar actionBar = fragmentActivity.getSupportActionBar(); if (actionBar != null) { actionBar.setTitle(actionBarTitle); + actionBar.setSubtitle(actionBarSubtitle); } + setHasOptionsMenu(true); } @Override @@ -697,6 +705,10 @@ public class StoryViewerFragment extends Fragment { lastSlidePos = 0; if (menuDownload != null) menuDownload.setVisible(false); if (menuDm != null) menuDm.setVisible(false); + if (menuProfile != null) menuProfile.setVisible(false); + downloadVisible = false; + dmVisible = false; + profileVisible = false; binding.imageViewer.setController(null); releasePlayer(); String currentStoryMediaId = null; @@ -846,7 +858,6 @@ public class StoryViewerFragment extends Fragment { final MediaItemType itemType = currentStory.getItemType(); - if (menuDownload != null) menuDownload.setVisible(false); url = itemType == MediaItemType.MEDIA_TYPE_IMAGE ? currentStory.getStoryUrl() : currentStory.getVideoUrl(); if (itemType != MediaItemType.MEDIA_TYPE_LIVE) { @@ -900,9 +911,10 @@ public class StoryViewerFragment extends Fragment { else setupImage(); final ActionBar actionBar = fragmentActivity.getSupportActionBar(); + actionBarSubtitle = Utils.datetimeParser.format(new Date(currentStory.getTimestamp() * 1000L)); if (actionBar != null) { try { - actionBar.setSubtitle(Utils.datetimeParser.format(new Date(currentStory.getTimestamp() * 1000L))); + actionBar.setSubtitle(actionBarSubtitle); } catch (Exception e) { Log.e(TAG, "refreshStory: ", e); } @@ -948,11 +960,17 @@ public class StoryViewerFragment extends Fragment { final ImageInfo imageInfo, final Animatable animatable) { if (menuDownload != null) { + downloadVisible = true; menuDownload.setVisible(true); } if (currentStory.canReply() && menuDm != null) { + dmVisible = true; menuDm.setVisible(true); } + if (!TextUtils.isEmpty(currentStory.getUsername())) { + profileVisible = true; + menuProfile.setVisible(true); + } binding.progressView.setVisibility(View.GONE); } }) @@ -982,9 +1000,18 @@ public class StoryViewerFragment extends Fragment { @Nullable final MediaSource.MediaPeriodId mediaPeriodId, @NonNull final LoadEventInfo loadEventInfo, @NonNull final MediaLoadData mediaLoadData) { - if (menuDownload != null) menuDownload.setVisible(true); - if (currentStory.canReply() && menuDm != null) + if (menuDownload != null) { + downloadVisible = true; + menuDownload.setVisible(true); + } + if (currentStory.canReply() && menuDm != null) { + dmVisible = true; menuDm.setVisible(true); + } + if (!TextUtils.isEmpty(currentStory.getUsername()) && menuProfile != null) { + profileVisible = true; + menuProfile.setVisible(true); + } binding.progressView.setVisibility(View.GONE); } @@ -993,9 +1020,18 @@ public class StoryViewerFragment extends Fragment { @Nullable final MediaSource.MediaPeriodId mediaPeriodId, @NonNull final LoadEventInfo loadEventInfo, @NonNull final MediaLoadData mediaLoadData) { - if (menuDownload != null) menuDownload.setVisible(true); - if (currentStory.canReply() && menuDm != null) + if (menuDownload != null) { + downloadVisible = true; + menuDownload.setVisible(true); + } + if (currentStory.canReply() && menuDm != null) { + dmVisible = true; menuDm.setVisible(true); + } + if (!TextUtils.isEmpty(currentStory.getUsername()) && menuProfile != null) { + profileVisible = true; + menuProfile.setVisible(true); + } binding.progressView.setVisibility(View.VISIBLE); } @@ -1014,8 +1050,18 @@ public class StoryViewerFragment extends Fragment { @NonNull final MediaLoadData mediaLoadData, @NonNull final IOException error, final boolean wasCanceled) { - if (menuDownload != null) menuDownload.setVisible(false); - if (menuDm != null) menuDm.setVisible(false); + if (menuDownload != null) { + downloadVisible = false; + menuDownload.setVisible(false); + } + if (menuDm != null) { + dmVisible = false; + menuDm.setVisible(false); + } + if (menuProfile != null) { + profileVisible = false; + menuProfile.setVisible(false); + } binding.progressView.setVisibility(View.GONE); } }); diff --git a/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java b/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java index f4b8d47e..bfc1dee3 100644 --- a/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java @@ -83,9 +83,12 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre new FeedStoriesAdapter.OnFeedStoryClickListener() { @Override public void onFeedStoryClick(FeedStoryModel model, int position) { - final NavDirections action = FeedFragmentDirections - .actionFeedFragmentToStoryViewerFragment(StoryViewerOptions.forFeedStoryPosition(position)); - NavHostFragment.findNavController(FeedFragment.this).navigate(action); + final NavController navController = NavHostFragment.findNavController(FeedFragment.this); + if (isSafeToNavigate(navController)) { + final NavDirections action = FeedFragmentDirections + .actionFeedFragmentToStoryViewerFragment(StoryViewerOptions.forFeedStoryPosition(position)); + navController.navigate(action); + } } @Override @@ -437,4 +440,9 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre binding.feedRecyclerView.smoothScrollToPosition(0); // binding.storiesContainer.setExpanded(true); } + + private boolean isSafeToNavigate(final NavController navController) { + return navController.getCurrentDestination() != null + && navController.getCurrentDestination().getId() == R.id.feedFragment; + } } diff --git a/app/src/main/java/awais/instagrabber/fragments/settings/DownloadsPreferencesFragment.java b/app/src/main/java/awais/instagrabber/fragments/settings/DownloadsPreferencesFragment.java index c2637b78..4e75a97f 100644 --- a/app/src/main/java/awais/instagrabber/fragments/settings/DownloadsPreferencesFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/settings/DownloadsPreferencesFragment.java @@ -29,6 +29,7 @@ public class DownloadsPreferencesFragment extends BasePreferencesFragment { if (context == null) return; screen.addPreference(getDownloadUserFolderPreference(context)); screen.addPreference(getSaveToCustomFolderPreference(context)); + screen.addPreference(getPrependUsernameToFilenamePreference(context)); } private Preference getDownloadUserFolderPreference(@NonNull final Context context) { @@ -49,6 +50,14 @@ public class DownloadsPreferencesFragment extends BasePreferencesFragment { .show(getParentFragmentManager(), null)); } + private Preference getPrependUsernameToFilenamePreference(@NonNull final Context context) { + final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context); + preference.setKey(Constants.DOWNLOAD_PREPEND_USER_NAME); + preference.setTitle(R.string.download_prepend_username); + preference.setIconSpaceReserved(false); + return preference; + } + public static class SaveToCustomFolderPreference extends Preference { private AppCompatTextView customPathTextView; private final OnSelectFolderButtonClickListener onSelectFolderButtonClickListener; diff --git a/app/src/main/java/awais/instagrabber/fragments/settings/MorePreferencesFragment.java b/app/src/main/java/awais/instagrabber/fragments/settings/MorePreferencesFragment.java index ecfcff77..8348123b 100644 --- a/app/src/main/java/awais/instagrabber/fragments/settings/MorePreferencesFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/settings/MorePreferencesFragment.java @@ -11,8 +11,6 @@ import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.app.AppCompatActivity; -import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentManager; import androidx.navigation.NavController; import androidx.navigation.NavDirections; @@ -35,9 +33,11 @@ import awais.instagrabber.db.repositories.AccountRepository; import awais.instagrabber.db.repositories.RepositoryCallback; import awais.instagrabber.dialogs.AccountSwitcherDialogFragment; import awais.instagrabber.repositories.responses.User; +import awais.instagrabber.utils.AppExecutors; import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.CookieUtils; import awais.instagrabber.utils.FlavorTown; +import awais.instagrabber.utils.ProcessPhoenix; import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.Utils; import awais.instagrabber.webservices.ServiceCallback; @@ -71,11 +71,15 @@ public class MorePreferencesFragment extends BasePreferencesFragment { accountCategory.setSummary(R.string.account_hint); accountCategory.addPreference(getAccountSwitcherPreference(cookie, context)); accountCategory.addPreference(getPreference(R.string.logout, R.string.logout_summary, R.drawable.ic_logout_24, preference -> { - if (getContext() == null) return false; + final Context context1 = getContext(); + if (context1 == null) return false; CookieUtils.setupCookies("LOGOUT"); - shouldRecreate(); - Toast.makeText(context, R.string.logout_success, Toast.LENGTH_SHORT).show(); + // shouldRecreate(); + Toast.makeText(context1, R.string.logout_success, Toast.LENGTH_SHORT).show(); settingsHelper.putString(Constants.COOKIE, ""); + AppExecutors.getInstance().mainThread().execute(() -> { + ProcessPhoenix.triggerRebirth(context1); + }, 200); return true; })); } @@ -103,9 +107,14 @@ public class MorePreferencesFragment extends BasePreferencesFragment { CookieUtils.removeAllAccounts(context, new RepositoryCallback() { @Override public void onSuccess(final Void result) { - shouldRecreate(); - Toast.makeText(context, R.string.logout_success, Toast.LENGTH_SHORT).show(); + // shouldRecreate(); + final Context context1 = getContext(); + if (context1 == null) return; + Toast.makeText(context1, R.string.logout_success, Toast.LENGTH_SHORT).show(); settingsHelper.putString(Constants.COOKIE, ""); + AppExecutors.getInstance().mainThread().execute(() -> { + ProcessPhoenix.triggerRebirth(context1); + }, 200); } @Override @@ -222,7 +231,8 @@ public class MorePreferencesFragment extends BasePreferencesFragment { BuildConfig.VERSION_NAME + " (" + BuildConfig.VERSION_CODE + ")", -1, preference -> { - FlavorTown.updateCheck((AppCompatActivity) requireActivity(), true); + if (BuildConfig.isPre) return true; + FlavorTown.updateCheck(activity, true); return true; })); screen.addPreference(getDivider(context)); @@ -264,9 +274,14 @@ public class MorePreferencesFragment extends BasePreferencesFragment { new RepositoryCallback() { @Override public void onSuccess(final Account result) { - final FragmentActivity activity = getActivity(); - if (activity == null) return; - activity.recreate(); + // final FragmentActivity activity = getActivity(); + // if (activity == null) return; + // activity.recreate(); + AppExecutors.getInstance().mainThread().execute(() -> { + final Context context = getContext(); + if (context == null) return; + ProcessPhoenix.triggerRebirth(context); + }, 200); } @Override diff --git a/app/src/main/java/awais/instagrabber/fragments/settings/StoriesPreferencesFragment.java b/app/src/main/java/awais/instagrabber/fragments/settings/StoriesPreferencesFragment.java index 8337f7c3..99eee7c7 100644 --- a/app/src/main/java/awais/instagrabber/fragments/settings/StoriesPreferencesFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/settings/StoriesPreferencesFragment.java @@ -37,6 +37,14 @@ public class StoriesPreferencesFragment extends BasePreferencesFragment { return preference; } + private Preference getHideMutedReelsPreference(@NonNull final Context context) { + final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context); + preference.setKey(Constants.HIDE_MUTED_REELS); + preference.setTitle(R.string.hide_muted_reels_setting); + preference.setIconSpaceReserved(false); + return preference; + } + private Preference getMarkStoriesSeenPreference(@NonNull final Context context) { final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context); preference.setKey(Constants.MARK_AS_SEEN); diff --git a/app/src/main/java/awais/instagrabber/utils/Constants.java b/app/src/main/java/awais/instagrabber/utils/Constants.java index 219185bf..a2a5fd13 100644 --- a/app/src/main/java/awais/instagrabber/utils/Constants.java +++ b/app/src/main/java/awais/instagrabber/utils/Constants.java @@ -20,6 +20,7 @@ public final class Constants { // boolean prefs public static final String DOWNLOAD_USER_FOLDER = "download_user_folder"; public static final String TOGGLE_KEYWORD_FILTER = "toggle_keyword_filter"; + public static final String DOWNLOAD_PREPEND_USER_NAME = "download_user_name"; // deprecated: public static final String BOTTOM_TOOLBAR = "bottom_toolbar"; public static final String FOLDER_SAVE_TO = "saved_to"; public static final String AUTOPLAY_VIDEOS = "autoplay_videos"; @@ -28,6 +29,7 @@ public final class Constants { public static final String CUSTOM_DATE_TIME_FORMAT_ENABLED = "data_time_custom_enabled"; public static final String SWAP_DATE_TIME_FORMAT_ENABLED = "swap_date_time_enabled"; public static final String MARK_AS_SEEN = "mark_as_seen"; + public static final String HIDE_MUTED_REELS = "hide_muted_reels"; public static final String DM_MARK_AS_SEEN = "dm_mark_as_seen"; // deprecated: public static final String INSTADP = "instadp"; // deprecated: public static final String STORIESIG = "storiesig"; @@ -79,7 +81,6 @@ public final class Constants { // public static final String SIGNATURE_KEY = "9193488027538fd3450b83b7d05286d4ca9599a0f7eeed90d8c85925698a05dc"; public static final String BREADCRUMB_KEY = "iN4$aGr0m"; public static final int LOGIN_RESULT_CODE = 5000; - public static final String FDROID_SHA1_FINGERPRINT = "C1661EB8FD09F618307E687786D5E5056F65084D"; public static final String SKIPPED_VERSION = "skipped_version"; public static final String DEFAULT_TAB = "default_tab"; public static final String PREF_DARK_THEME = "dark_theme"; diff --git a/app/src/main/java/awais/instagrabber/utils/DownloadUtils.java b/app/src/main/java/awais/instagrabber/utils/DownloadUtils.java index 85f45a21..18bf59c7 100644 --- a/app/src/main/java/awais/instagrabber/utils/DownloadUtils.java +++ b/app/src/main/java/awais/instagrabber/utils/DownloadUtils.java @@ -98,41 +98,52 @@ public final class DownloadUtils { // } // } - private static void dmDownloadImpl(@NonNull final Context context, - @Nullable final String username, - final String modelId, - final String url) { - final File dir = getDownloadDir(context, username); - if (dir.exists() || dir.mkdirs()) { - download(context, - url, - getDownloadSaveFile(dir, modelId, url).getAbsolutePath()); - return; - } - Toast.makeText(context, R.string.error_creating_folders, Toast.LENGTH_SHORT).show(); - } +// private static void dmDownloadImpl(@NonNull final Context context, +// @Nullable final String username, +// final String modelId, +// final String url) { +// final File dir = getDownloadDir(context, username); +// if (dir.exists() || dir.mkdirs()) { +// download(context, +// url, +// getDownloadSaveFile(dir, modelId, url).getAbsolutePath()); +// return; +// } +// Toast.makeText(context, R.string.error_creating_folders, Toast.LENGTH_SHORT).show(); +// } @NonNull private static File getDownloadSaveFile(final File finalDir, final String postId, final String displayUrl) { - return getDownloadSaveFile(finalDir, postId, "", displayUrl); + return getDownloadSaveFile(finalDir, postId, "", displayUrl, ""); + } + + @NonNull + private static File getDownloadSaveFile(final File finalDir, + final String postId, + final String displayUrl, + final String username) { + return getDownloadSaveFile(finalDir, postId, "", displayUrl, username); } private static File getDownloadChildSaveFile(final File downloadDir, final String postId, final int childPosition, - final String url) { + final String url, + final String username) { final String sliderPostfix = "_slide_" + childPosition; - return getDownloadSaveFile(downloadDir, postId, sliderPostfix, url); + return getDownloadSaveFile(downloadDir, postId, sliderPostfix, url, username); } @NonNull private static File getDownloadSaveFile(final File finalDir, final String postId, final String sliderPostfix, - final String displayUrl) { - final String fileName = postId + sliderPostfix + getFileExtensionFromUrl(displayUrl); + final String displayUrl, + final String username) { + final String usernamePrepend = TextUtils.isEmpty(username) ? "" : (username + "_"); + final String fileName = usernamePrepend + postId + sliderPostfix + getFileExtensionFromUrl(displayUrl); return new File(finalDir, fileName); } @@ -205,8 +216,9 @@ public final class DownloadUtils { case MEDIA_TYPE_IMAGE: case MEDIA_TYPE_VIDEO: { final String url = ResponseBodyUtils.getImageUrl(media); - final File file = getDownloadSaveFile(downloadDir, media.getCode(), url); - checkList.add(file.exists()); + final File file = getDownloadSaveFile(downloadDir, media.getCode(), url, ""); + final File usernamePrependedFile = getDownloadSaveFile(downloadDir, media.getCode(), url, username); + checkList.add(file.exists() || usernamePrependedFile.exists()); break; } case MEDIA_TYPE_SLIDER: @@ -215,8 +227,9 @@ public final class DownloadUtils { final Media child = sliderItems.get(i); if (child == null) continue; final String url = ResponseBodyUtils.getImageUrl(child); - final File file = getDownloadChildSaveFile(downloadDir, media.getCode(), i + 1, url); - checkList.add(file.exists()); + final File file = getDownloadChildSaveFile(downloadDir, media.getCode(), i + 1, url, ""); + final File usernamePrependedFile = getDownloadChildSaveFile(downloadDir, media.getCode(), i + 1, url, username); + checkList.add(file.exists() || usernamePrependedFile.exists()); } break; default: @@ -262,10 +275,12 @@ public final class DownloadUtils { final String url = storyModel.getItemType() == MediaItemType.MEDIA_TYPE_VIDEO ? storyModel.getVideoUrl() : storyModel.getStoryUrl(); + final String baseFileName = storyModel.getStoryMediaId() + "_" + + storyModel.getTimestamp() + DownloadUtils.getFileExtensionFromUrl(url); + final String usernamePrepend = Utils.settingsHelper.getBoolean(Constants.DOWNLOAD_PREPEND_USER_NAME) + && storyModel.getUsername() != null ? storyModel.getUsername() + "_" : ""; final File saveFile = new File(downloadDir, - storyModel.getStoryMediaId() - + "_" + storyModel.getTimestamp() - + DownloadUtils.getFileExtensionFromUrl(url)); + usernamePrepend + baseFileName); download(context, url, saveFile.getAbsolutePath()); } @@ -291,13 +306,23 @@ public final class DownloadUtils { final Map map = new HashMap<>(); for (final Media media : feedModels) { final User mediaUser = media.getUser(); - final File downloadDir = getDownloadDir(context, mediaUser == null ? "" : "@" + mediaUser.getUsername()); + final File downloadDir = getDownloadDir(context, mediaUser == null ? "" : mediaUser.getUsername()); if (downloadDir == null) return; switch (media.getMediaType()) { case MEDIA_TYPE_IMAGE: case MEDIA_TYPE_VIDEO: { final String url = getUrlOfType(media); - final File file = getDownloadSaveFile(downloadDir, media.getCode(), url); + String fileName = media.getId(); + if (mediaUser != null && TextUtils.isEmpty(media.getCode())) { + fileName = mediaUser.getUsername() + "_" + fileName; + } + if (!TextUtils.isEmpty(media.getCode())) { + fileName = media.getCode(); + if (Utils.settingsHelper.getBoolean(Constants.DOWNLOAD_PREPEND_USER_NAME) && mediaUser != null) { + fileName = mediaUser.getUsername() + "_" + fileName; + } + } + final File file = getDownloadSaveFile(downloadDir, fileName, url); map.put(url, file.getAbsolutePath()); break; } @@ -319,7 +344,8 @@ public final class DownloadUtils { } final Media child = sliderItems.get(i); final String url = getUrlOfType(child); - final File file = getDownloadChildSaveFile(downloadDir, media.getCode(), i + 1, url); + final String usernamePrepend = Utils.settingsHelper.getBoolean(Constants.DOWNLOAD_PREPEND_USER_NAME) && mediaUser != null ? mediaUser.getUsername() : ""; + final File file = getDownloadChildSaveFile(downloadDir, media.getCode(), i + 1, url, usernamePrepend); map.put(url, file.getAbsolutePath()); } break; diff --git a/app/src/main/java/awais/instagrabber/utils/ExportImportUtils.java b/app/src/main/java/awais/instagrabber/utils/ExportImportUtils.java index ca5ca21c..a0a2e190 100755 --- a/app/src/main/java/awais/instagrabber/utils/ExportImportUtils.java +++ b/app/src/main/java/awais/instagrabber/utils/ExportImportUtils.java @@ -156,12 +156,21 @@ public final class ExportImportUtils { query, favoriteType, favsObject.optString("s"), - favoriteType == FavoriteType.HASHTAG ? null - : favsObject.optString("pic_url"), + favoriteType == FavoriteType.USER ? favsObject.optString("pic_url") : null, new Date(favsObject.getLong("d"))); // Log.d(TAG, "importJson: favoriteModel: " + favoriteModel); - FavoriteRepository.getInstance(FavoriteDataSource.getInstance(context)) - .insertOrUpdateFavorite(favorite, null); + final FavoriteRepository favRepo = FavoriteRepository.getInstance(FavoriteDataSource.getInstance(context)); + favRepo.getFavorite(query, favoriteType, new RepositoryCallback() { + @Override + public void onSuccess(final Favorite result) { + // local has priority since it's more frequently updated + } + + @Override + public void onDataNotAvailable() { + favRepo.insertOrUpdateFavorite(favorite, null); + } + }); } } diff --git a/app/src/main/java/awais/instagrabber/utils/FlavorTown.java b/app/src/main/java/awais/instagrabber/utils/FlavorTown.java index ee859700..8f06eb8b 100755 --- a/app/src/main/java/awais/instagrabber/utils/FlavorTown.java +++ b/app/src/main/java/awais/instagrabber/utils/FlavorTown.java @@ -1,104 +1,72 @@ package awais.instagrabber.utils; -import android.annotation.SuppressLint; -import android.content.ActivityNotFoundException; import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.Signature; -import android.content.res.Resources; -import android.net.Uri; -import android.os.AsyncTask; import android.util.Log; import android.widget.Toast; import androidx.annotation.NonNull; -import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; +import java.util.Objects; import java.util.concurrent.ThreadLocalRandom; - -import javax.security.cert.CertificateException; -import javax.security.cert.X509Certificate; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import awais.instagrabber.BuildConfig; import awais.instagrabber.R; -import awais.instagrabber.databinding.DialogUpdateBinding; import static awais.instagrabber.utils.Utils.settingsHelper; public final class FlavorTown { private static final String TAG = "FlavorTown"; - private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray(); - private static AlertDialog dialog; + private static final UpdateChecker UPDATE_CHECKER = UpdateChecker.getInstance(); + private static final Pattern VERSION_NAME_PATTERN = Pattern.compile("v?(\\d+\\.\\d+\\.\\d+)(?:_?)(\\w*)(?:-?)(\\w*)"); + + private static boolean checking = false; public static void updateCheck(@NonNull final AppCompatActivity context) { updateCheck(context, false); } - @SuppressLint("PackageManagerGetSignatures") - public static void updateCheck(@NonNull final AppCompatActivity context, final boolean force) { - boolean isInstalledFromFdroid = false; - final PackageInfo packageInfo; - try { - packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES); - for (Signature signature : packageInfo.signatures) { - final X509Certificate cert = X509Certificate.getInstance(signature.toByteArray()); - final String fingerprint = bytesToHex(MessageDigest.getInstance("SHA-1").digest(cert.getEncoded())); - isInstalledFromFdroid = fingerprint.equals(Constants.FDROID_SHA1_FINGERPRINT); - // Log.d(TAG, "fingerprint:" + fingerprint); - } - } catch (PackageManager.NameNotFoundException | NoSuchAlgorithmException | CertificateException e) { - Log.e(TAG, "Error", e); - } - if (isInstalledFromFdroid) return; - final DialogUpdateBinding binding = DialogUpdateBinding.inflate(context.getLayoutInflater(), null, false); - binding.skipUpdate.setOnCheckedChangeListener((buttonView, isChecked) -> { - if (dialog == null) return; - dialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(!isChecked); - }); - Resources res = context.getResources(); - new UpdateChecker(version -> { - if (force && version.equals(BuildConfig.VERSION_NAME)) { - Toast.makeText(context, "You're already on the latest version", Toast.LENGTH_SHORT).show(); + public static void updateCheck(@NonNull final AppCompatActivity context, + final boolean force) { + if (checking) return; + checking = true; + AppExecutors.getInstance().networkIO().execute(() -> { + final String onlineVersionName = UPDATE_CHECKER.getLatestVersion(); + if (onlineVersionName == null) return; + final String onlineVersion = getVersion(onlineVersionName); + final String localVersion = getVersion(BuildConfig.VERSION_NAME); + if (Objects.equals(onlineVersion, localVersion)) { + if (force) { + AppExecutors.getInstance().mainThread().execute(() -> { + final Context applicationContext = context.getApplicationContext(); + // Check if app was closed or crashed before reaching here + if (applicationContext == null) return; + // Show toast if version number preference was tapped + Toast.makeText(applicationContext, R.string.on_latest_version, Toast.LENGTH_SHORT).show(); + }); + } return; } - final String skippedVersion = settingsHelper.getString(Constants.SKIPPED_VERSION); - final boolean shouldShowDialog = force || (!version.equals(BuildConfig.VERSION_NAME) && !BuildConfig.DEBUG && !skippedVersion - .equals(version)); + final boolean shouldShowDialog = UpdateCheckCommon.shouldShowUpdateDialog(force, onlineVersionName); if (!shouldShowDialog) return; - dialog = new AlertDialog.Builder(context) - .setTitle(res.getString(R.string.update_available, version)) - .setView(binding.getRoot()) - .setNeutralButton(R.string.cancel, (dialog, which) -> { - if (binding.skipUpdate.isChecked()) { - settingsHelper.putString(Constants.SKIPPED_VERSION, version); - } - dialog.dismiss(); - }) - .setPositiveButton(R.string.action_github, (dialog1, which) -> { - try { - context.startActivity(new Intent(Intent.ACTION_VIEW).setData( - Uri.parse("https://github.com/austinhuang0131/instagrabber/releases/latest"))); - } catch (final ActivityNotFoundException e) { - // do nothing - } - }) - // if we don't show dialog for fdroid users, is the below required? - .setNegativeButton(R.string.action_fdroid, (dialog, which) -> { - try { - context.startActivity(new Intent(Intent.ACTION_VIEW).setData( - Uri.parse("https://f-droid.org/packages/me.austinhuang.instagrabber/"))); - } catch (final ActivityNotFoundException e) { - // do nothing - } - }) - .show(); - }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + UpdateCheckCommon.showUpdateDialog(context, onlineVersionName, (dialog, which) -> { + UPDATE_CHECKER.onDownload(context); + dialog.dismiss(); + }); + }); + } + + private static String getVersion(@NonNull final String versionName) { + final Matcher matcher = VERSION_NAME_PATTERN.matcher(versionName); + if (!matcher.matches()) return versionName; + try { + return matcher.group(1); + } catch (Exception e) { + Log.e(TAG, "getVersion: ", e); + } + return versionName; } public static void changelogCheck(@NonNull final Context context) { @@ -121,14 +89,4 @@ public final class FlavorTown { settingsHelper.putInteger(Constants.PREV_INSTALL_VERSION, BuildConfig.VERSION_CODE); } } - - public static String bytesToHex(byte[] bytes) { - char[] hexChars = new char[bytes.length * 2]; - for (int j = 0; j < bytes.length; j++) { - int v = bytes[j] & 0xFF; - hexChars[j * 2] = HEX_ARRAY[v >>> 4]; - hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F]; - } - return new String(hexChars); - } } \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/utils/ProcessPhoenix.java b/app/src/main/java/awais/instagrabber/utils/ProcessPhoenix.java new file mode 100644 index 00000000..5dcc6e19 --- /dev/null +++ b/app/src/main/java/awais/instagrabber/utils/ProcessPhoenix.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2014 Jake Wharton + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package awais.instagrabber.utils; + +import android.app.Activity; +import android.app.ActivityManager; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.Process; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK; +import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; + +/** + * Process Phoenix facilitates restarting your application process. This should only be used for + * things like fundamental state changes in your debug builds (e.g., changing from staging to + * production). + *

+ * Trigger process recreation by calling {@link #triggerRebirth} with a {@link Context} instance. + */ +public final class ProcessPhoenix extends Activity { + private static final String KEY_RESTART_INTENTS = "phoenix_restart_intents"; + + /** + * Call to restart the application process using the {@linkplain Intent#CATEGORY_DEFAULT default} + * activity as an intent. + *

+ * Behavior of the current process after invoking this method is undefined. + */ + public static void triggerRebirth(Context context) { + triggerRebirth(context, getRestartIntent(context)); + } + + /** + * Call to restart the application process using the specified intents. + *

+ * Behavior of the current process after invoking this method is undefined. + */ + public static void triggerRebirth(Context context, Intent... nextIntents) { + Intent intent = new Intent(context, ProcessPhoenix.class); + intent.addFlags(FLAG_ACTIVITY_NEW_TASK); // In case we are called with non-Activity context. + intent.putParcelableArrayListExtra(KEY_RESTART_INTENTS, new ArrayList<>(Arrays.asList(nextIntents))); + context.startActivity(intent); + if (context instanceof Activity) { + ((Activity) context).finish(); + } + Runtime.getRuntime().exit(0); // Kill kill kill! + } + + private static Intent getRestartIntent(Context context) { + String packageName = context.getPackageName(); + Intent defaultIntent = context.getPackageManager().getLaunchIntentForPackage(packageName); + if (defaultIntent != null) { + defaultIntent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK); + return defaultIntent; + } + + throw new IllegalStateException("Unable to determine default activity for " + + packageName + + ". Does an activity specify the DEFAULT category in its intent filter?"); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + ArrayList intents = getIntent().getParcelableArrayListExtra(KEY_RESTART_INTENTS); + startActivities(intents.toArray(new Intent[intents.size()])); + finish(); + Runtime.getRuntime().exit(0); // Kill kill kill! + } + + /** + * Checks if the current process is a temporary Phoenix Process. + * This can be used to avoid initialisation of unused resources or to prevent running code that + * is not multi-process ready. + * + * @return true if the current process is a temporary Phoenix Process + */ + public static boolean isPhoenixProcess(Context context) { + int currentPid = Process.myPid(); + ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); + List runningProcesses = manager.getRunningAppProcesses(); + if (runningProcesses != null) { + for (ActivityManager.RunningAppProcessInfo processInfo : runningProcesses) { + if (processInfo.pid == currentPid && processInfo.processName.endsWith(":phoenix")) { + return true; + } + } + } + return false; + } +} diff --git a/app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java b/app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java index 2722d11e..0d7acc16 100644 --- a/app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java +++ b/app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java @@ -1090,11 +1090,19 @@ public final class ResponseBodyUtils { if (imageVersions2 == null) return null; final List candidates = imageVersions2.getCandidates(); if (candidates == null || candidates.isEmpty()) return null; + final boolean isSquare = Integer.compare(media.getOriginalWidth(), media.getOriginalHeight()) == 0; final List sortedCandidates = candidates.stream() .sorted((c1, c2) -> Integer.compare(c2.getWidth(), c1.getWidth())) - .filter(c -> c.getWidth() < type.getValue()) .collect(Collectors.toList()); - final MediaCandidate candidate = sortedCandidates.get(0); + if (sortedCandidates.size() == 1) return sortedCandidates.get(0).getUrl(); + final List filteredCandidates = sortedCandidates.stream() + .filter(c -> + c.getWidth() <= media.getOriginalWidth() + && c.getWidth() <= type.getValue() + && (isSquare || Integer.compare(c.getWidth(), c.getHeight()) != 0) + ) + .collect(Collectors.toList()); + final MediaCandidate candidate = filteredCandidates.get(0); if (candidate == null) return null; return candidate.getUrl(); } diff --git a/app/src/main/java/awais/instagrabber/utils/SettingsHelper.java b/app/src/main/java/awais/instagrabber/utils/SettingsHelper.java index 9ae6cfd4..fc568d9e 100755 --- a/app/src/main/java/awais/instagrabber/utils/SettingsHelper.java +++ b/app/src/main/java/awais/instagrabber/utils/SettingsHelper.java @@ -34,10 +34,12 @@ import static awais.instagrabber.utils.Constants.DATE_TIME_SELECTION; import static awais.instagrabber.utils.Constants.DEFAULT_TAB; import static awais.instagrabber.utils.Constants.DEVICE_UUID; import static awais.instagrabber.utils.Constants.DM_MARK_AS_SEEN; +import static awais.instagrabber.utils.Constants.DOWNLOAD_PREPEND_USER_NAME; import static awais.instagrabber.utils.Constants.DOWNLOAD_USER_FOLDER; import static awais.instagrabber.utils.Constants.FLAG_SECURE; import static awais.instagrabber.utils.Constants.FOLDER_PATH; import static awais.instagrabber.utils.Constants.FOLDER_SAVE_TO; +import static awais.instagrabber.utils.Constants.HIDE_MUTED_REELS; import static awais.instagrabber.utils.Constants.KEYWORD_FILTERS; import static awais.instagrabber.utils.Constants.MARK_AS_SEEN; import static awais.instagrabber.utils.Constants.MUTED_VIDEOS; @@ -159,10 +161,10 @@ public final class SettingsHelper { STORY_SORT, PREF_EMOJI_VARIANTS, PREF_REACTIONS, PREF_ENABLE_DM_AUTO_REFRESH_FREQ_UNIT, PREF_TAB_ORDER}) public @interface StringSettings {} - @StringDef({DOWNLOAD_USER_FOLDER, FOLDER_SAVE_TO, AUTOPLAY_VIDEOS, SHOW_QUICK_ACCESS_DIALOG, MUTED_VIDEOS, + @StringDef({DOWNLOAD_USER_FOLDER, DOWNLOAD_PREPEND_USER_NAME, FOLDER_SAVE_TO, AUTOPLAY_VIDEOS, SHOW_QUICK_ACCESS_DIALOG, MUTED_VIDEOS, SHOW_CAPTIONS, CUSTOM_DATE_TIME_FORMAT_ENABLED, MARK_AS_SEEN, DM_MARK_AS_SEEN, CHECK_ACTIVITY, CHECK_UPDATES, SWAP_DATE_TIME_FORMAT_ENABLED, PREF_ENABLE_DM_NOTIFICATIONS, PREF_ENABLE_DM_AUTO_REFRESH, - FLAG_SECURE, TOGGLE_KEYWORD_FILTER, PREF_ENABLE_SENTRY}) + FLAG_SECURE, TOGGLE_KEYWORD_FILTER, PREF_ENABLE_SENTRY, HIDE_MUTED_REELS}) public @interface BooleanSettings {} @StringDef({PREV_INSTALL_VERSION, BROWSER_UA_CODE, APP_UA_CODE, PREF_ENABLE_DM_AUTO_REFRESH_FREQ_NUMBER}) diff --git a/app/src/main/java/awais/instagrabber/utils/UpdateCheckCommon.java b/app/src/main/java/awais/instagrabber/utils/UpdateCheckCommon.java new file mode 100644 index 00000000..285fbb13 --- /dev/null +++ b/app/src/main/java/awais/instagrabber/utils/UpdateCheckCommon.java @@ -0,0 +1,38 @@ +package awais.instagrabber.utils; + +import android.content.Context; +import android.content.DialogInterface; + +import androidx.annotation.NonNull; + +import com.google.android.material.dialog.MaterialAlertDialogBuilder; + +import awais.instagrabber.BuildConfig; +import awais.instagrabber.R; + +import static awais.instagrabber.utils.Utils.settingsHelper; + +public final class UpdateCheckCommon { + + public static boolean shouldShowUpdateDialog(final boolean force, + @NonNull final String version) { + final String skippedVersion = settingsHelper.getString(Constants.SKIPPED_VERSION); + return force || (!BuildConfig.DEBUG && !skippedVersion.equals(version)); + } + + public static void showUpdateDialog(@NonNull final Context context, + @NonNull final String version, + @NonNull final DialogInterface.OnClickListener onDownloadClickListener) { + AppExecutors.getInstance().mainThread().execute(() -> { + new MaterialAlertDialogBuilder(context) + .setTitle(context.getString(R.string.update_available, version)) + .setNeutralButton(R.string.skip_update, (dialog, which) -> { + settingsHelper.putString(Constants.SKIPPED_VERSION, version); + dialog.dismiss(); + }) + .setPositiveButton(R.string.action_download, onDownloadClickListener) + .setNegativeButton(R.string.cancel, null) + .show(); + }); + } +} diff --git a/app/src/main/java/awais/instagrabber/utils/UpdateChecker.java b/app/src/main/java/awais/instagrabber/utils/UpdateChecker.java deleted file mode 100755 index 3ea67ba1..00000000 --- a/app/src/main/java/awais/instagrabber/utils/UpdateChecker.java +++ /dev/null @@ -1,58 +0,0 @@ -package awais.instagrabber.utils; - -import android.os.AsyncTask; -import android.util.Log; - -import androidx.annotation.NonNull; - -import org.json.JSONObject; - -import java.net.HttpURLConnection; -import java.net.URL; - -import awais.instagrabber.BuildConfig; -import awais.instagrabber.interfaces.FetchListener; - -public final class UpdateChecker extends AsyncTask { - private final FetchListener fetchListener; - private String version; - - public UpdateChecker(final FetchListener fetchListener) { - this.fetchListener = fetchListener; - } - - @NonNull - @Override - protected Boolean doInBackground(final Void... voids) { - try { - version = ""; - - HttpURLConnection conn = - (HttpURLConnection) new URL("https://f-droid.org/api/v1/packages/me.austinhuang.instagrabber").openConnection(); - conn.setUseCaches(false); - conn.setRequestProperty("User-Agent", "https://Barinsta.AustinHuang.me / mailto:Barinsta@AustinHuang.me"); - conn.connect(); - - final int responseCode = conn.getResponseCode(); - if (responseCode == HttpURLConnection.HTTP_OK) { - final JSONObject data = new JSONObject(NetworkUtils.readFromConnection(conn)); - if (BuildConfig.VERSION_CODE < data.getInt("suggestedVersionCode")) { - version = data.getJSONArray("packages").getJSONObject(0).getString("versionName"); - return true; - } - } - - conn.disconnect(); - } catch (final Exception e) { - if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e); - } - - return false; - } - - @Override - protected void onPostExecute(final Boolean result) { - if (result != null && result && fetchListener != null) - fetchListener.onResult("v"+version); - } -} diff --git a/app/src/main/java/awais/instagrabber/utils/Utils.java b/app/src/main/java/awais/instagrabber/utils/Utils.java index 6f966c33..6f711a45 100644 --- a/app/src/main/java/awais/instagrabber/utils/Utils.java +++ b/app/src/main/java/awais/instagrabber/utils/Utils.java @@ -31,7 +31,6 @@ import android.webkit.MimeTypeMap; import android.widget.Toast; import androidx.annotation.DrawableRes; -import androidx.annotation.IdRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.ActionBar; @@ -474,7 +473,11 @@ public final class Utils { final List navGraphNameList = Arrays.asList(navGraphNames); if (TextUtils.isEmpty(tabOrderString)) { // Use top 5 entries for default list - return navGraphNameList.subList(0, 5); + final List top5navGraphNames = navGraphNameList.subList(0, 5); + final String newOrderString = android.text.TextUtils.join(",", top5navGraphNames); + Utils.settingsHelper.putString(PreferenceKeys.PREF_TAB_ORDER, newOrderString); + tabOrderString = newOrderString; + return top5navGraphNames; } // Make sure that the list from preference does not contain any invalid values final List orderGraphNames = Arrays.stream(tabOrderString.split(",")) @@ -489,6 +492,7 @@ public final class Utils { } public static boolean isNavRootInCurrentTabs(final String navRootString) { + if (navRootString == null || tabOrderString == null) return false; return tabOrderString.contains(navRootString); } } diff --git a/app/src/main/java/awais/instagrabber/webservices/NewsService.java b/app/src/main/java/awais/instagrabber/webservices/NewsService.java index 15b34def..580e2c04 100644 --- a/app/src/main/java/awais/instagrabber/webservices/NewsService.java +++ b/app/src/main/java/awais/instagrabber/webservices/NewsService.java @@ -54,9 +54,11 @@ public class NewsService extends BaseService { callback.onSuccess(null); return; } - final List result = new ArrayList<>(); - result.addAll(body.getNewStories()); - result.addAll(body.getOldStories()); + final List result = new ArrayList(); + final List newStories = body.getNewStories(); + if (newStories != null) result.addAll(newStories); + final List oldStories = body.getOldStories(); + if (oldStories != null) result.addAll(oldStories); callback.onSuccess(result); } diff --git a/app/src/main/java/awais/instagrabber/webservices/StoriesService.java b/app/src/main/java/awais/instagrabber/webservices/StoriesService.java index 059a8c7b..09a3a6b7 100644 --- a/app/src/main/java/awais/instagrabber/webservices/StoriesService.java +++ b/app/src/main/java/awais/instagrabber/webservices/StoriesService.java @@ -135,7 +135,7 @@ public class StoriesService extends BaseService { final JSONArray feedStoriesReel = new JSONObject(body).getJSONArray("tray"); for (int i = 0; i < feedStoriesReel.length(); ++i) { final JSONObject node = feedStoriesReel.getJSONObject(i); - if (node.optBoolean("hide_from_feed_unit")) continue; + if (node.optBoolean("hide_from_feed_unit") && Utils.settingsHelper.getBoolean(Constants.HIDE_MUTED_REELS)) continue; final JSONObject userJson = node.getJSONObject(node.has("user") ? "user" : "owner"); try { final User user = new User(userJson.getLong("pk"), @@ -177,18 +177,24 @@ public class StoriesService extends BaseService { null, null ); - final String id = node.getString("id"); final long timestamp = node.getLong("latest_reel_media"); - final int mediaCount = node.getInt("media_count"); final boolean fullyRead = !node.isNull("seen") && node.getLong("seen") == timestamp; final JSONObject itemJson = node.has("items") ? node.getJSONArray("items").optJSONObject(0) : null; - final boolean isBestie = node.optBoolean("has_besties_media", false); StoryModel firstStoryModel = null; if (itemJson != null) { firstStoryModel = ResponseBodyUtils.parseStoryItem(itemJson, false, null); } - feedStoryModels.add(new FeedStoryModel(id, user, fullyRead, timestamp, firstStoryModel, mediaCount, false, isBestie)); - } catch (Exception e) {} // to cover promotional reels with non-long user pk's + feedStoryModels.add(new FeedStoryModel( + node.getString("id"), + user, + fullyRead, + timestamp, + firstStoryModel, + node.getInt("media_count"), + false, + node.optBoolean("has_besties_media"))); + } + catch (Exception e) {} // to cover promotional reels with non-long user pk's } final JSONArray broadcasts = new JSONObject(body).getJSONArray("broadcasts"); for (int i = 0; i < broadcasts.length(); ++i) { @@ -239,13 +245,16 @@ public class StoriesService extends BaseService { null, null ); - final String id = node.getString("id"); - final long timestamp = node.getLong("published_time"); - // final JSONObject itemJson = node.has("items") ? node.getJSONArray("items").getJSONObject(0) : null; - final StoryModel firstStoryModel = ResponseBodyUtils.parseBroadcastItem(node); - // if (itemJson != null) { - // } - feedStoryModels.add(new FeedStoryModel(id, user, false, timestamp, firstStoryModel, 1, true, false)); + feedStoryModels.add(new FeedStoryModel( + node.getString("id"), + user, + false, + node.getLong("published_time"), + ResponseBodyUtils.parseBroadcastItem(node), + 1, + true, + false + )); } callback.onSuccess(sort(feedStoryModels)); } catch (JSONException e) { diff --git a/app/src/main/res/layout/layout_profile_details.xml b/app/src/main/res/layout/layout_profile_details.xml index 035a04b0..ea4df164 100644 --- a/app/src/main/res/layout/layout_profile_details.xml +++ b/app/src/main/res/layout/layout_profile_details.xml @@ -326,6 +326,7 @@ diff --git a/app/src/main/res/menu/story_menu.xml b/app/src/main/res/menu/story_menu.xml index a7293f91..8169fd62 100644 --- a/app/src/main/res/menu/story_menu.xml +++ b/app/src/main/res/menu/story_menu.xml @@ -5,13 +5,18 @@ + android:title="@string/reply_story" + android:titleCondensed="@string/reply_story" + app:showAsAction="never" /> + + app:showAsAction="never" /> \ No newline at end of file diff --git a/app/src/main/res/navigation/notification_viewer_nav_graph.xml b/app/src/main/res/navigation/notification_viewer_nav_graph.xml index 89df18be..50e2fbdf 100644 --- a/app/src/main/res/navigation/notification_viewer_nav_graph.xml +++ b/app/src/main/res/navigation/notification_viewer_nav_graph.xml @@ -5,24 +5,16 @@ android:id="@+id/notification_viewer_nav_graph" app:startDestination="@id/notificationsViewer"> - + + + - - - + app:nullable="true" /> + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/profile_nav_graph.xml b/app/src/main/res/navigation/profile_nav_graph.xml index 8ef07dca..8bff84b8 100644 --- a/app/src/main/res/navigation/profile_nav_graph.xml +++ b/app/src/main/res/navigation/profile_nav_graph.xml @@ -71,11 +71,9 @@ app:argType="long" /> - - + app:destination="@id/notificationsViewer"> + + + + + Cerca actualitzacions a l\'inici Block screenshots & app preview Descarrega les publicacions a carpetes de nom d\'usuari + Prepend Username to Filename Marca les històries com a vistes després de visualitzar-es L\'autor de la història sabrà que l\'has vista Marca els missatges com a vists després de visualitzar-los diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 0bb9e0ac..c5bc0f11 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -23,6 +23,7 @@ Zkontrolovat aktualizace při spuštění Block screenshots & app preview Stáhnout příspěvky do složek s uživatelským jménem + Prepend Username to Filename Označit příběhy po zhlédnutí jako zobrazené Autor příběhu bude vědět, že jsi si ho zobrazili Označovat přímou zprávu po zobrazení jako zobrazenou diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 1f541fcd..e023c6b9 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -23,6 +23,7 @@ Beim Start auf Aktualisierungen prüfen Block screenshots & app preview Beiträge in Benutzernamen-Ordner herunterladen + Prepend Username to Filename Stories nach dem Ansehen als gesehen markieren Die Person wird wissen, dass du dir die Story angesehen hast Direktnachrichten nach dem Ansehen als gesehen markieren diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index f9817a0a..b8c7df56 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -23,6 +23,7 @@ Έλεγχος για ενημερώσεις στο ξεκίνημα Block screenshots & app preview Λήψη δημοσίευσης στους φακέλους με ονόματα χρηστών + Prepend Username to Filename Επισήμανση ιστοριών ως προβληθέντων μετά την προβολή Ο συντάκτης της ιστορίας θα ξέρει ότι την προβάλατε Σήμανση ΠΜ ως αναγνωσμένου μετά την προβολή diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index f392ec86..9ee7d1d0 100755 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -23,6 +23,7 @@ Buscar actualizaciones al inicio Bloquea capturas de pantalla & vista previa de aplicaciones Usar subcarpetas con el nombre de usuario + Prepend Username to Filename Marcar historias como vistas después de verlas El autor de la historia sabrá que lo has visto Marcar Mensaje Directo como visto después de verlo diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index dd0321e2..8f217f17 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -23,6 +23,7 @@ Bilatu eguneratzeak abioan Block screenshots & app preview Deskargatu bidalketak erabiltzaile-izena duten karpetetara + Prepend Username to Filename Markatu istorioak ikusita gisa ikusi ondoren Istorioaren egileak ikusi duzula jakingo du Markatu MZ ikusita gisa ikusi ondoren diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index c6da89cf..75371835 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -23,6 +23,7 @@ بررسی بروزرسانی هنگام آغاز برنامه Block screenshots & app preview بارگیری پست ها در پوشه های به نام کاربر + Prepend Username to Filename نشان کرد استوری ها به عنوان دیده شده بعد از دیدن نویسنده استوری می داند که شما آن را دیده اید نشان کردن پیام خصوصی بعنوان دیده شده بعد از دیدن diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 15f8a5cc..d2fd93dd 100755 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -23,6 +23,7 @@ Rechercher les mises à jours au démarrage Bloquer les captures d\'écran & l\'aperçu de l\'application Télécharger les messages dans les dossiers des noms d\'utilisateurs + Prepend Username to Filename Marquer les stories comme vues après consultation L\'auteur de la story saura que vous l\'avez vue Marquer les messages privés comme vus après consultation diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index bc923135..623bc2ce 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -23,6 +23,7 @@ खुलने पर अपडेट के लिए जाँच करें Block screenshots & app preview पोस्ट को ब्यबहारकारी के नाम पर किये फोल्डरस में रखें + Prepend Username to Filename स्टोरि को दिखने के बाद \"दिखा गया\" दिखादें सटोरि के लेखक जानेगा कि तुम देखे हो इसको तुम देखने के बाद सीधा संदेश को \"दिखागया\" लिखा जाएगा diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 462c1a2e..6801759b 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -23,6 +23,7 @@ Cek pembaruan saat memulai Block screenshots & app preview Unduh kiriman ke folder nama pengguna + Prepend Username to Filename Tandai cerita dibaca setelah melihat Pembuat cerita akan tahu Anda melihatnya Tandai DM dibaca setelah melihat diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index d8202389..eefead25 100755 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -23,6 +23,7 @@ Verifica per aggiornamenti all\'avvio Blocca screenshot & anteprima app Scarica i post nelle cartelle del nome utente + Prepend Username to Filename Segna le storie come viste dopo la visualizzazione L\'autore della storia saprà che l\'hai visualizzata Segna il DM come visto dopo la visualizzazione diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 5a11dc06..178f3726 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -23,6 +23,7 @@ 起動時にアップデートを確認 Block screenshots & app preview ユーザ名のフォルダに投稿をダウンロード + Prepend Username to Filename ストーリーズを表示後に既読にする ストーリーの作成者は、あなたが閲覧したことを知ることができます。 DMを表示後に既読にする diff --git a/app/src/main/res/values-kn/strings.xml b/app/src/main/res/values-kn/strings.xml index 80d8b915..85c0390c 100644 --- a/app/src/main/res/values-kn/strings.xml +++ b/app/src/main/res/values-kn/strings.xml @@ -23,6 +23,7 @@ Check for updates at startup Block screenshots & app preview Download posts to username folders + Prepend Username to Filename Mark stories as seen after viewing Story author will know you viewed it Mark DM as seen after viewing diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index abb89551..118e07f4 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -23,6 +23,7 @@ Провери за ажурирање Block screenshots & app preview Превземи објави во папката со кориснички имиња + Prepend Username to Filename Означи ги приказните како видени Авторот на приказната ќе знае дека сте ја погледнале приказната Означи порака како видена diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index fda4c863..b904bed6 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -23,6 +23,7 @@ Controleer op updates bij het opstarten Block screenshots & app preview Download berichten naar gebruikersnaam mappen + Prepend Username to Filename Markeer verhalen als gelezen na bekijken Verhaalmaker zal het weten als je het bekeken hebt Markeer privéberichten als gelezen na bekijken diff --git a/app/src/main/res/values-or/strings.xml b/app/src/main/res/values-or/strings.xml index ce4427ec..12b95d7b 100644 --- a/app/src/main/res/values-or/strings.xml +++ b/app/src/main/res/values-or/strings.xml @@ -23,6 +23,7 @@ ଖୋଲିବା ସମୟରେ ଅପଡେଟ ପାଇଁ ଯାଞ୍ଚ କରନ୍ତୁ Block screenshots & app preview ଡାଉନଲୋଡ ପୋଷ୍ଟକୁ ବ୍ୟବହାରକାରୀଙ୍କ ନାମରେ ହୋଇଥିବା ସ୍ଥାନ ରେ ରଖ + Prepend Username to Filename କାହାଣୀଗୁଡିକ ଦେଖିବା ପରେ \'ଦେଖାଗଲା\' ଚିହ୍ନିତ କରନ୍ତୁ | କାହାଣୀ ପ୍ରେରକ ଜାଣିବେ ତୁମେ ଏହାକୁ ଦେଖିଛ ବାର୍ତା ଦେଖିବା ପରେ \'ଦେଖାଗଲା\' ଚିହ୍ନିତ କରନ୍ତୁ | diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 93fb3ff8..2c449634 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -23,6 +23,7 @@ Sprawdź aktualizacje przy starcie Blokuj zrzuty ekranu & podgląd aplikacji Pobierz posty do folderów o nazwie użytkownika + Prepend Username to Filename Oznacz relacje jako widoczne po wyświetleniu Autor relacji będzie widział, że to wyświetliłeś Oznacz wiadomość jako przeczytaną diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 4c7bbbc4..f8c3f401 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -23,6 +23,7 @@ Verificar se há atualizações ao iniciar Block screenshots & app preview Baixar publicações para pastas com o nome de usuário + Prepend Username to Filename Marcar stories como vistos após a visualização O autor do story saberá que você viu Marcar DM como vista após a visualização diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index b491a1c5..ca3155ef 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -23,6 +23,7 @@ Проверять наличие обновлений при запуске Block screenshots & app preview Скачать публикации в папки с именем пользователя + Prepend Username to Filename Отметить истории как увиденные после просмотра Автор истории узнает, что вы просмотрели её Отметить ЛС как увиденные после просмотра diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index f12178ec..7b1c175a 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -23,6 +23,7 @@ Kontrolovať aktualizácie pri štarte Block screenshots & app preview Ukľadať do priečinkov podľa mena + Prepend Username to Filename Označiť príbehy po videní ako videné Autor príbehu bude vedieť že ste ho videli Po prečítaní, označiť správu ako prečítanú diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 4ca6836d..fea3a299 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -23,6 +23,7 @@ Güncellemeleri başlangıçta kontrol et Block screenshots & app preview İndirmeleri kullanıcı adından oluşan bir alt klasörün içine yap + Prepend Username to Filename Hikayeleri gördükten sonra görüldü olarak işaretle Hikayeyi paylaşan gördüğünüzü bilecek DM\'leri gördükten sonra görüldü olarak işaretle diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index debb92fc..858193f6 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -23,6 +23,7 @@ Kiểm tra cập nhật khi khởi động Block screenshots & app preview Tải bài viết xuống theo thư mục tên người dùng trong Downloads + Prepend Username to Filename Đánh dấu story là đã xem sau khi xem Người đăng story sẽ biết bạn đã xem nó Đánh dấu DM là đã xem sau khi xem diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index a6fe9c77..0fb2193f 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -23,6 +23,7 @@ 启动时检查更新 屏蔽截图及应用预览 下载帖子到用户名文件夹 + Prepend Username to Filename 查看快拍后将其标记为已读 快拍作者会知道您已看过 查看私信后将其标记为已读 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 26371e94..3ce706fa 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -23,6 +23,7 @@ 啟動時檢查更新 Block screenshots & app preview 將貼文下載到用戶名資料夾 + Prepend Username to Filename 檢視完限時動態後標記為已讀 限時動態的作者會知道您已查看了此限時動態 檢視完訊息後標記為已讀 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e7d2052f..9cf27407 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -26,8 +26,10 @@ Check for updates at startup Block screenshots & app preview Download posts to username folders + Prepend Username to Filename Mark stories as seen after viewing Story author will know you viewed it + Hide muted stories from feed Mark DM as seen after viewing Other members will know you viewed it Enable activity notifications @@ -483,6 +485,8 @@ Select an email app to send crash logs Not found! Your IP has been rate limited by Instagram. Wait for an hour and try again. + Skip this update + You\'re already on the latest version Screen order Other tabs The tab order will be reflected on next launch diff --git a/app/src/main/resources/feed_response.json b/app/src/main/resources/feed_response.json deleted file mode 100644 index 9e26dfee..00000000 --- a/app/src/main/resources/feed_response.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/app/src/main/resources/stories_response.json b/app/src/main/resources/stories_response.json deleted file mode 100644 index 9e26dfee..00000000 --- a/app/src/main/resources/stories_response.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file