diff --git a/app/build.gradle b/app/build.gradle index 5be8edf2..da978aae 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -9,8 +9,8 @@ android { minSdkVersion 16 targetSdkVersion 29 - versionCode 40 - versionName '17.4' + versionCode 41 + versionName '17.5' multiDexEnabled true diff --git a/app/src/main/java/awais/instagrabber/MainHelper.java b/app/src/main/java/awais/instagrabber/MainHelper.java index ba325352..51a2f4ab 100755 --- a/app/src/main/java/awais/instagrabber/MainHelper.java +++ b/app/src/main/java/awais/instagrabber/MainHelper.java @@ -240,7 +240,7 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener { if (tag instanceof FeedStoryModel) { final FeedStoryModel feedStoryModel = (FeedStoryModel) tag; final int index = indexOfIntArray(stories, feedStoryModel); - new iStoryStatusFetcher(feedStoryModel.getStoryMediaId(), null, false, false, result -> { + new iStoryStatusFetcher(feedStoryModel.getStoryMediaId(), null, false, false, false, false, result -> { if (result != null && result.length > 0) main.startActivity(new Intent(main, StoryViewer.class) .putExtra(Constants.EXTRAS_STORIES, result) @@ -733,31 +733,31 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener { main.mainBinding.btnFollowTag.setVisibility(View.VISIBLE); if (isLoggedIn) { - new iStoryStatusFetcher(hashtagModel.getName(), null, false, true, result -> { + new iStoryStatusFetcher(hashtagModel.getName(), null, false, true, false, false, result -> { main.storyModels = result; if (result != null && result.length > 0) main.mainBinding.mainHashtagImage.setStoriesBorder(); }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); if (hashtagModel.getFollowing() == true) { main.mainBinding.btnFollowTag.setText(R.string.unfollow); - main.mainBinding.btnFollowTag.setBackgroundTintList(ColorStateList.valueOf(resources.getColor( - R.color.btn_purple_background, null))); + main.mainBinding.btnFollowTag.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor( + main, R.color.btn_purple_background))); } else { main.mainBinding.btnFollowTag.setText(R.string.follow); - main.mainBinding.btnFollowTag.setBackgroundTintList(ColorStateList.valueOf(resources.getColor( - R.color.btn_pink_background, null))); + main.mainBinding.btnFollowTag.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor( + main, R.color.btn_pink_background))); } } else { if (Utils.dataBox.getFavorite(main.userQuery) != null) { main.mainBinding.btnFollowTag.setText(R.string.unfavorite_short); - main.mainBinding.btnFollowTag.setBackgroundTintList(ColorStateList.valueOf(resources.getColor( - R.color.btn_purple_background, null))); + main.mainBinding.btnFollowTag.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor( + main, R.color.btn_purple_background))); } else { main.mainBinding.btnFollowTag.setText(R.string.favorite_short); - main.mainBinding.btnFollowTag.setBackgroundTintList(ColorStateList.valueOf(resources.getColor( - R.color.btn_pink_background, null))); + main.mainBinding.btnFollowTag.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor( + main, R.color.btn_pink_background))); } } @@ -795,20 +795,22 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener { final String cookie = Utils.settingsHelper.getString(Constants.COOKIE); final boolean isLoggedIn = !Utils.isEmpty(cookie); + new iStoryStatusFetcher(profileId, profileModel.getUsername(), false, false, + (!isLoggedIn && Utils.settingsHelper.getBoolean(Constants.STORIESIG)), false, + result -> { + main.storyModels = result; + if (result != null && result.length > 0) main.mainBinding.mainProfileImage.setStoriesBorder(); + }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + + new HighlightsFetcher(profileId, (!isLoggedIn && Utils.settingsHelper.getBoolean(Constants.STORIESIG)), result -> { + if (result != null && result.length > 0) { + main.mainBinding.highlightsList.setVisibility(View.VISIBLE); + main.highlightsAdapter.setData(result); + } + else main.mainBinding.highlightsList.setVisibility(View.GONE); + }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + if (isLoggedIn) { - new iStoryStatusFetcher(profileId, profileModel.getUsername(), false, false, result -> { - main.storyModels = result; - if (result != null && result.length > 0) main.mainBinding.mainProfileImage.setStoriesBorder(); - }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - - new HighlightsFetcher(profileId, result -> { - if (result != null && result.length > 0) { - main.mainBinding.highlightsList.setVisibility(View.VISIBLE); - main.highlightsAdapter.setData(result); - } - else main.mainBinding.highlightsList.setVisibility(View.GONE); - }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - final String myId = Utils.getUserIdFromCookie(Utils.settingsHelper.getString(Constants.COOKIE)); if (!profileId.equals(myId)) { main.mainBinding.btnTagged.setVisibility(View.GONE); @@ -817,41 +819,41 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener { main.mainBinding.btnFollow.setVisibility(View.VISIBLE); if (profileModel.getFollowing() == true) { main.mainBinding.btnFollow.setText(R.string.unfollow); - main.mainBinding.btnFollow.setBackgroundTintList(ColorStateList.valueOf(resources.getColor( - R.color.btn_purple_background, null))); + main.mainBinding.btnFollow.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor( + main, R.color.btn_purple_background))); } else if (profileModel.getRequested() == true) { main.mainBinding.btnFollow.setText(R.string.cancel); - main.mainBinding.btnFollow.setBackgroundTintList(ColorStateList.valueOf(resources.getColor( - R.color.btn_purple_background, null))); + main.mainBinding.btnFollow.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor( + main, R.color.btn_purple_background))); } else { main.mainBinding.btnFollow.setText(R.string.follow); - main.mainBinding.btnFollow.setBackgroundTintList(ColorStateList.valueOf(resources.getColor( - R.color.btn_pink_background, null))); + main.mainBinding.btnFollow.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor( + main, R.color.btn_pink_background))); } main.mainBinding.btnRestrict.setVisibility(View.VISIBLE); if (profileModel.getRestricted() == true) { main.mainBinding.btnRestrict.setText(R.string.unrestrict); - main.mainBinding.btnRestrict.setBackgroundTintList(ColorStateList.valueOf(resources.getColor( - R.color.btn_green_background, null))); + main.mainBinding.btnRestrict.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor( + main, R.color.btn_green_background))); } else { main.mainBinding.btnRestrict.setText(R.string.restrict); - main.mainBinding.btnRestrict.setBackgroundTintList(ColorStateList.valueOf(resources.getColor( - R.color.btn_orange_background, null))); + main.mainBinding.btnRestrict.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor( + main, R.color.btn_orange_background))); } if (profileModel.isReallyPrivate()) { main.mainBinding.btnBlock.setVisibility(View.VISIBLE); main.mainBinding.btnTagged.setVisibility(View.GONE); if (profileModel.getBlocked() == true) { main.mainBinding.btnBlock.setText(R.string.unblock); - main.mainBinding.btnBlock.setBackgroundTintList(ColorStateList.valueOf(resources.getColor( - R.color.btn_green_background, null))); + main.mainBinding.btnBlock.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor( + main, R.color.btn_green_background))); } else { main.mainBinding.btnBlock.setText(R.string.block); - main.mainBinding.btnBlock.setBackgroundTintList(ColorStateList.valueOf(resources.getColor( - R.color.btn_red_background, null))); + main.mainBinding.btnBlock.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor( + main, R.color.btn_red_background))); } } else { main.mainBinding.btnBlock.setVisibility(View.GONE); @@ -859,12 +861,12 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener { main.mainBinding.btnTagged.setVisibility(View.VISIBLE); if (profileModel.getBlocked() == true) { main.mainBinding.btnSaved.setText(R.string.unblock); - main.mainBinding.btnSaved.setBackgroundTintList(ColorStateList.valueOf(resources.getColor( - R.color.btn_green_background, null))); + main.mainBinding.btnSaved.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor( + main, R.color.btn_green_background))); } else { main.mainBinding.btnSaved.setText(R.string.block); - main.mainBinding.btnSaved.setBackgroundTintList(ColorStateList.valueOf(resources.getColor( - R.color.btn_red_background, null))); + main.mainBinding.btnSaved.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor( + main, R.color.btn_red_background))); } } } @@ -873,26 +875,26 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener { main.mainBinding.btnSaved.setVisibility(View.VISIBLE); main.mainBinding.btnLiked.setVisibility(View.VISIBLE); main.mainBinding.btnSaved.setText(R.string.saved); - main.mainBinding.btnSaved.setBackgroundTintList(ColorStateList.valueOf(resources.getColor( - R.color.btn_orange_background, null))); + main.mainBinding.btnSaved.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor( + main, R.color.btn_orange_background))); } } else { if (Utils.dataBox.getFavorite(main.userQuery) != null) { main.mainBinding.btnFollow.setText(R.string.unfavorite_short); - main.mainBinding.btnFollow.setBackgroundTintList(ColorStateList.valueOf(resources.getColor( - R.color.btn_purple_background, null))); + main.mainBinding.btnFollow.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor( + main, R.color.btn_purple_background))); } else { main.mainBinding.btnFollow.setText(R.string.favorite_short); - main.mainBinding.btnFollow.setBackgroundTintList(ColorStateList.valueOf(resources.getColor( - R.color.btn_pink_background, null))); + main.mainBinding.btnFollow.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor( + main, R.color.btn_pink_background))); } main.mainBinding.btnFollow.setVisibility(View.VISIBLE); if (!profileModel.isReallyPrivate()) { main.mainBinding.btnRestrict.setVisibility(View.VISIBLE); main.mainBinding.btnRestrict.setText(R.string.tagged); - main.mainBinding.btnRestrict.setBackgroundTintList(ColorStateList.valueOf(resources.getColor( - R.color.btn_blue_background, null))); + main.mainBinding.btnRestrict.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor( + main, R.color.btn_blue_background))); } } @@ -1010,7 +1012,7 @@ public final class MainHelper implements SwipeRefreshLayout.OnRefreshListener { final String cookie = Utils.settingsHelper.getString(Constants.COOKIE); final boolean isLoggedIn = !Utils.isEmpty(cookie); if (isLoggedIn) { - new iStoryStatusFetcher(profileId.split("/")[0], null, true, false, result -> { + new iStoryStatusFetcher(profileId.split("/")[0], null, true, false, false, false, result -> { main.storyModels = result; if (result != null && result.length > 0) main.mainBinding.mainLocationImage.setStoriesBorder(); }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); diff --git a/app/src/main/java/awais/instagrabber/activities/Main.java b/app/src/main/java/awais/instagrabber/activities/Main.java index 228725f0..58aace15 100755 --- a/app/src/main/java/awais/instagrabber/activities/Main.java +++ b/app/src/main/java/awais/instagrabber/activities/Main.java @@ -15,6 +15,7 @@ import android.view.MenuItem; import android.view.View; import android.widget.ArrayAdapter; import android.widget.AutoCompleteTextView; +import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -33,6 +34,7 @@ import awais.instagrabber.adapters.HighlightsAdapter; import awais.instagrabber.adapters.SuggestionsAdapter; import awais.instagrabber.asyncs.SuggestionsFetcher; import awais.instagrabber.asyncs.UsernameFetcher; +import awais.instagrabber.asyncs.i.iStoryStatusFetcher; import awais.instagrabber.customviews.MouseDrawer; import awais.instagrabber.databinding.ActivityMainBinding; import awais.instagrabber.dialogs.AboutDialog; @@ -76,10 +78,16 @@ public final class Main extends BaseLanguageActivity { final Object tag = v.getTag(); if (tag instanceof HighlightModel) { final HighlightModel highlightModel = (HighlightModel) tag; - startActivity(new Intent(Main.this, StoryViewer.class) - .putExtra(Constants.EXTRAS_USERNAME, userQuery) - .putExtra(Constants.EXTRAS_HIGHLIGHT, highlightModel.getTitle()) - .putExtra(Constants.EXTRAS_STORIES, highlightModel.getStoryModels())); + new iStoryStatusFetcher(highlightModel.getId(), null, false, false, + (!mainHelper.isLoggedIn && Utils.settingsHelper.getBoolean(Constants.STORIESIG)), true, result -> { + if (result != null && result.length > 0) + startActivity(new Intent(Main.this, StoryViewer.class) + .putExtra(Constants.EXTRAS_USERNAME, userQuery) + .putExtra(Constants.EXTRAS_HIGHLIGHT, highlightModel.getTitle()) + .putExtra(Constants.EXTRAS_STORIES, result) + ); + else Toast.makeText(Main.this, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); + }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } } }); @@ -349,7 +357,7 @@ public final class Main extends BaseLanguageActivity { public boolean onMenuItemActionCollapse(MenuItem item) { menu.findItem(R.id.action_about).setVisible(true); menu.findItem(R.id.action_settings).setVisible(true); - menu.findItem(R.id.action_dms).setVisible(true); + menu.findItem(R.id.action_dms).setVisible(!Utils.isEmpty(Utils.settingsHelper.getString(Constants.COOKIE))); menu.findItem(R.id.action_quickaccess).setVisible(true); menu.findItem(R.id.action_notif).setVisible(true); return true; diff --git a/app/src/main/java/awais/instagrabber/activities/StoryViewer.java b/app/src/main/java/awais/instagrabber/activities/StoryViewer.java index f31e2cad..65ffd1dd 100755 --- a/app/src/main/java/awais/instagrabber/activities/StoryViewer.java +++ b/app/src/main/java/awais/instagrabber/activities/StoryViewer.java @@ -104,6 +104,7 @@ public final class StoryViewer extends BaseLanguageActivity { private StoryModel currentStory; private String url, username; private int slidePos = 0, lastSlidePos = 0; + private final String cookie = settingsHelper.getString(Constants.COOKIE); @Override protected void onCreate(@Nullable final Bundle savedInstanceState) { @@ -160,7 +161,7 @@ public final class StoryViewer extends BaseLanguageActivity { Toast.makeText(getApplicationContext(), R.string.be_patient, Toast.LENGTH_SHORT).show(); } else { fetching = true; - new iStoryStatusFetcher(feedStoryModel.getStoryMediaId(), null, false, false, result -> { + new iStoryStatusFetcher(feedStoryModel.getStoryMediaId(), null, false, false, false, false, result -> { if (result != null && result.length > 0) { final Intent newIntent = new Intent(getApplicationContext(), StoryViewer.class) .putExtra(Constants.EXTRAS_STORIES, result) @@ -251,7 +252,7 @@ public final class StoryViewer extends BaseLanguageActivity { poll.getLeftChoice() + " (" + poll.getLeftCount() + ")", poll.getRightChoice() + " (" + poll.getRightCount() + ")" }), (d, w) -> { - new VoteAction().execute(w); + if (!Utils.isEmpty(cookie)) new VoteAction().execute(w); }) .setPositiveButton(R.string.cancel, null) .show(); @@ -285,7 +286,7 @@ public final class StoryViewer extends BaseLanguageActivity { } new AlertDialog.Builder(this).setTitle(quiz.getMyChoice() > -1 ? getString(R.string.story_quizzed) : quiz.getQuestion()) .setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, choices), (d,w) -> { - if (quiz.getMyChoice() == -1) new QuizAction().execute(w); + if (quiz.getMyChoice() == -1 && !Utils.isEmpty(cookie)) new QuizAction().execute(w); }) .setPositiveButton(R.string.cancel, null) .show(); @@ -320,14 +321,14 @@ public final class StoryViewer extends BaseLanguageActivity { @Override public void onLoadCompleted(final int windowIndex, @Nullable final MediaSource.MediaPeriodId mediaPeriodId, final LoadEventInfo loadEventInfo, final MediaLoadData mediaLoadData) { if (menuDownload != null) menuDownload.setVisible(true); - if (currentStory.canReply() && menuDm != null) menuDm.setVisible(true); + if (currentStory.canReply() && menuDm != null && !Utils.isEmpty(cookie)) menuDm.setVisible(true); storyViewerBinding.progressView.setVisibility(View.GONE); } @Override public void onLoadStarted(final int windowIndex, @Nullable final MediaSource.MediaPeriodId mediaPeriodId, final LoadEventInfo loadEventInfo, final MediaLoadData mediaLoadData) { if (menuDownload != null) menuDownload.setVisible(true); - if (currentStory.canReply() && menuDm != null) menuDm.setVisible(true); + if (currentStory.canReply() && menuDm != null && !Utils.isEmpty(cookie)) menuDm.setVisible(true); storyViewerBinding.progressView.setVisibility(View.VISIBLE); } @@ -373,7 +374,7 @@ public final class StoryViewer extends BaseLanguageActivity { @Override public boolean onResourceReady(final Drawable resource, final Object model, final Target target, final DataSource dataSource, final boolean isFirstResource) { if (menuDownload != null) menuDownload.setVisible(true); - if (currentStory.canReply() && menuDm != null) menuDm.setVisible(true); + if (currentStory.canReply() && menuDm != null && !Utils.isEmpty(cookie)) menuDm.setVisible(true); storyViewerBinding.progressView.setVisibility(View.GONE); return false; } @@ -497,7 +498,7 @@ public final class StoryViewer extends BaseLanguageActivity { storyViewerBinding.poll.setTag(poll); question = currentStory.getQuestion(); - storyViewerBinding.answer.setVisibility(question != null ? View.VISIBLE : View.GONE); + storyViewerBinding.answer.setVisibility((question != null && !Utils.isEmpty(cookie)) ? View.VISIBLE : View.GONE); storyViewerBinding.answer.setTag(question); mentions = currentStory.getMentions(); @@ -508,8 +509,6 @@ public final class StoryViewer extends BaseLanguageActivity { storyViewerBinding.quiz.setVisibility(quiz != null ? View.VISIBLE : View.GONE); storyViewerBinding.quiz.setTag(quiz); - storyViewerBinding.toolbar.toolbar.setSubtitle(Utils.datetimeParser.format(new Date(currentStory.getTimestamp() * 1000L))); - releasePlayer(); final Intent intent = getIntent(); if (intent.getBooleanExtra(Constants.EXTRAS_HASHTAG, false)) { @@ -521,6 +520,9 @@ public final class StoryViewer extends BaseLanguageActivity { if (itemType == MediaItemType.MEDIA_TYPE_VIDEO) setupVideo(); else setupImage(); + if (!intent.hasExtra(Constants.EXTRAS_HIGHLIGHT)) + storyViewerBinding.toolbar.toolbar.setSubtitle(Utils.datetimeParser.format(new Date(currentStory.getTimestamp() * 1000L))); + if (settingsHelper.getBoolean(MARK_AS_SEEN)) new SeenAction().execute(); } @@ -565,8 +567,7 @@ public final class StoryViewer extends BaseLanguageActivity { urlConnection.setRequestMethod("POST"); urlConnection.setUseCaches(false); urlConnection.setRequestProperty("User-Agent", Constants.USER_AGENT); - urlConnection.setRequestProperty("x-csrftoken", - settingsHelper.getString(Constants.COOKIE).split("csrftoken=")[1].split(";")[0]); + urlConnection.setRequestProperty("x-csrftoken", cookie.split("csrftoken=")[1].split(";")[0]); urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); urlConnection.setRequestProperty("Content-Length", "6"); urlConnection.setDoOutput(true); @@ -601,8 +602,7 @@ public final class StoryViewer extends BaseLanguageActivity { protected Void doInBackground(Integer... rawchoice) { int choice = rawchoice[0]; - final String cookie = settingsHelper.getString(Constants.COOKIE); - final String url = "https://i.instagram.com/api/v1/media/"+currentStory.getStoryMediaId().split("_")[0]+"/"+quiz.getId()+"/story_quiz_answer/"; +final String url = "https://i.instagram.com/api/v1/media/"+currentStory.getStoryMediaId().split("_")[0]+"/"+quiz.getId()+"/story_quiz_answer/"; try { JSONObject ogbody = new JSONObject("{\"client_context\":\"" + UUID.randomUUID().toString() +"\",\"mutation_token\":\"" + UUID.randomUUID().toString() @@ -650,7 +650,6 @@ public final class StoryViewer extends BaseLanguageActivity { String action; protected Void doInBackground(String... rawchoice) { - final String cookie = settingsHelper.getString(Constants.COOKIE); final String url = "https://i.instagram.com/api/v1/media/" +currentStory.getStoryMediaId().split("_")[0]+"/"+question.getId()+"/story_question_response/"; try { @@ -696,8 +695,7 @@ public final class StoryViewer extends BaseLanguageActivity { class SeenAction extends AsyncTask { protected Void doInBackground(Void... lmao) { - final String cookie = settingsHelper.getString(Constants.COOKIE); - final String url = "https://www.instagram.com/stories/reel/seen"; +final String url = "https://www.instagram.com/stories/reel/seen"; try { String urlParameters = "reelMediaId="+currentStory.getStoryMediaId().split("_")[0] +"&reelMediaOwnerId="+currentStory.getUserId() @@ -707,8 +705,7 @@ public final class StoryViewer extends BaseLanguageActivity { final HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection(); urlConnection.setRequestMethod("POST"); urlConnection.setUseCaches(false); - urlConnection.setRequestProperty("x-csrftoken", - settingsHelper.getString(Constants.COOKIE).split("csrftoken=")[1].split(";")[0]); + urlConnection.setRequestProperty("x-csrftoken", cookie.split("csrftoken=")[1].split(";")[0]); urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); urlConnection.setRequestProperty("Content-Length", Integer.toString(urlParameters.getBytes().length)); urlConnection.setDoOutput(true); @@ -731,8 +728,7 @@ public final class StoryViewer extends BaseLanguageActivity { protected Void doInBackground(String... rawAction) { final String action = rawAction[0]; final String url = "https://i.instagram.com/api/v1/direct_v2/create_group_thread/"; - final String cookie = settingsHelper.getString(Constants.COOKIE); - try { +try { final HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection(); urlConnection.setRequestMethod("POST"); urlConnection.setRequestProperty("User-Agent", Constants.I_USER_AGENT); @@ -780,7 +776,6 @@ public final class StoryViewer extends BaseLanguageActivity { wr2.flush(); wr2.close(); urlConnection2.connect(); - Log.d("austin_debug", urlConnection2.getResponseCode() + " " + urlParameters2 + " " + cookie); if (urlConnection2.getResponseCode() == HttpURLConnection.HTTP_OK) { ok = true; } diff --git a/app/src/main/java/awais/instagrabber/asyncs/HighlightsFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/HighlightsFetcher.java index b2e756e8..0c5e081b 100755 --- a/app/src/main/java/awais/instagrabber/asyncs/HighlightsFetcher.java +++ b/app/src/main/java/awais/instagrabber/asyncs/HighlightsFetcher.java @@ -17,58 +17,43 @@ import awais.instagrabber.utils.Utils; public final class HighlightsFetcher extends AsyncTask { private final String id; + private final boolean storiesig; private final FetchListener fetchListener; - public HighlightsFetcher(final String id, final FetchListener fetchListener) { + public HighlightsFetcher(final String id, final boolean storiesig, final FetchListener fetchListener) { this.id = id; + this.storiesig = storiesig; this.fetchListener = fetchListener; } @Override protected HighlightModel[] doInBackground(final Void... voids) { HighlightModel[] result = null; - String url = "https://www.instagram.com/graphql/query/?query_hash=7c16654f22c819fb63d1183034a5162f&variables=" + - "{\"user_id\":\"" + id + "\",\"include_chaining\":false,\"include_reel\":true,\"include_suggested_users\":false," + - "\"include_logged_out_extras\":false,\"include_highlight_reels\":true}"; + String url = "https://" + (storiesig ? "storiesig" : "i.instagram") + ".com/api/v1/highlights/" + id + "/highlights_tray/"; try { HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection(); conn.setInstanceFollowRedirects(false); conn.setUseCaches(false); + conn.setRequestProperty("User-Agent", storiesig ? Constants.A_USER_AGENT : Constants.I_USER_AGENT); conn.connect(); if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) { - final JSONArray highlightsReel = new JSONObject(Utils.readFromConnection(conn)).getJSONObject("data") - .getJSONObject(Constants.EXTRAS_USER).getJSONObject("edge_highlight_reels").getJSONArray("edges"); + final JSONArray highlightsReel = new JSONObject(Utils.readFromConnection(conn)).getJSONArray("tray"); final int length = highlightsReel.length(); final HighlightModel[] highlightModels = new HighlightModel[length]; final String[] highlightIds = new String[length]; for (int i = 0; i < length; ++i) { - final JSONObject highlightNode = highlightsReel.getJSONObject(i).getJSONObject("node"); - final String id = highlightNode.getString(Constants.EXTRAS_ID); - highlightIds[i] = id; + final JSONObject highlightNode = highlightsReel.getJSONObject(i); highlightModels[i] = new HighlightModel( highlightNode.getString("title"), - highlightNode.getJSONObject("cover_media").getString("thumbnail_src") + highlightNode.getString(Constants.EXTRAS_ID), + highlightNode.getJSONObject("cover_media").getJSONObject("cropped_image_version").getString("url") ); } conn.disconnect(); - - // a22a50ce4582220909e302d6eb84d259 - // 45246d3fe16ccc6577e0bd297a5db1ab - url = "https://www.instagram.com/graphql/query/?query_hash=a22a50ce4582220909e302d6eb84d259&variables=" + - "{\"highlight_reel_ids\":" + Utils.highlightIdsMerger(highlightIds) + ",\"reel_ids\":[],\"location_ids\":[],\"precomposed_overlay\":false}"; - conn = (HttpURLConnection) new URL(url).openConnection(); - conn.setInstanceFollowRedirects(false); - conn.setUseCaches(false); - conn.connect(); - - if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) { - Utils.putHighlightModels(conn, highlightModels); - } - result = highlightModels; } diff --git a/app/src/main/java/awais/instagrabber/asyncs/ProfilePictureFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/ProfilePictureFetcher.java index d2c4331d..1e7ad664 100755 --- a/app/src/main/java/awais/instagrabber/asyncs/ProfilePictureFetcher.java +++ b/app/src/main/java/awais/instagrabber/asyncs/ProfilePictureFetcher.java @@ -55,11 +55,12 @@ public final class ProfilePictureFetcher extends AsyncTask { out = data.getJSONObject("hd_profile_pic_url_info").optString("url"); } - if (Utils.isEmpty(out)) { + if (Utils.isEmpty(out) && Utils.settingsHelper.getBoolean(Constants.INSTADP)) { final HttpURLConnection backup = (HttpURLConnection) new URL("https://instadp.com/fullsize/" + userName).openConnection(); backup.setUseCaches(false); backup.setRequestMethod("GET"); + backup.setRequestProperty("User-Agent", Constants.A_USER_AGENT); final String instadp = backup.getResponseCode() == HttpURLConnection.HTTP_OK ? Utils.readFromConnection(backup) : null; backup.disconnect(); @@ -85,7 +86,6 @@ public final class ProfilePictureFetcher extends AsyncTask { } } } - if (out == null) out = picUrl; } } catch (final Exception e) { if (logCollector != null) diff --git a/app/src/main/java/awais/instagrabber/asyncs/i/iStoryStatusFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/i/iStoryStatusFetcher.java index e2312920..ce84bc5c 100755 --- a/app/src/main/java/awais/instagrabber/asyncs/i/iStoryStatusFetcher.java +++ b/app/src/main/java/awais/instagrabber/asyncs/i/iStoryStatusFetcher.java @@ -23,37 +23,46 @@ import awaisomereport.LogCollector; import static awais.instagrabber.utils.Utils.logCollector; public final class iStoryStatusFetcher extends AsyncTask { - private final String id, username; - private final boolean isLoc, isHashtag; + private final String id; + private String username; + private final boolean isLoc, isHashtag, storiesig, highlight; private final FetchListener fetchListener; public iStoryStatusFetcher(final String id, final String username, final boolean isLoc, - final boolean isHashtag, final FetchListener fetchListener) { + final boolean isHashtag, final boolean storiesig, final boolean highlight, + final FetchListener fetchListener) { this.id = id; this.username = username; this.isLoc = isLoc; this.isHashtag = isHashtag; + this.storiesig = storiesig; + this.highlight = highlight; this.fetchListener = fetchListener; } @Override protected StoryModel[] doInBackground(final Void... voids) { StoryModel[] result = null; - final String url = "https://i.instagram.com/api/v1/" + (isLoc ? "locations/" : (isHashtag ? "tags/" : "feed/user/")) - + id + "/story/"; + final String url = "https://" + (storiesig ? "storiesig" : "i.instagram") + ".com/api/v1/" + + (isLoc ? "locations/" : (isHashtag ? "tags/" : (highlight ? "feed/reels_media?user_ids=" : "feed/user/"))) + + id + (highlight ? "" : (storiesig ? "/reel_media/" : "/story/")); try { final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection(); conn.setInstanceFollowRedirects(false); conn.setUseCaches(false); - conn.setRequestProperty("User-Agent", Constants.USER_AGENT); + conn.setRequestProperty("User-Agent", storiesig ? Constants.A_USER_AGENT : Constants.I_USER_AGENT); conn.connect(); if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) { - JSONObject data = new JSONObject(Utils.readFromConnection(conn)).getJSONObject((isLoc || isHashtag) ? "story" : "reel"); + JSONObject data = new JSONObject(Utils.readFromConnection(conn)); + if (!storiesig && !highlight) data = data.optJSONObject((isLoc || isHashtag) ? "story" : "reel"); + else if (highlight) data = data.getJSONObject("reels").optJSONObject(id); + + if (username == null && !isLoc && !isHashtag) username = data.getJSONObject("user").getString("username"); JSONArray media; - if ((media = data.optJSONArray("items")) != null && media.length() > 0 && - (data = media.optJSONObject(0)) != null) { + if (data != null && (media = data.optJSONArray("items")) != null + && media.length() > 0 && (data = media.optJSONObject(0)) != null) { final int mediaLen = media.length(); diff --git a/app/src/main/java/awais/instagrabber/dialogs/SettingsDialog.java b/app/src/main/java/awais/instagrabber/dialogs/SettingsDialog.java index 7a4a76e5..519444c1 100755 --- a/app/src/main/java/awais/instagrabber/dialogs/SettingsDialog.java +++ b/app/src/main/java/awais/instagrabber/dialogs/SettingsDialog.java @@ -6,6 +6,7 @@ import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; +import android.net.Uri; import android.os.Bundle; import android.view.View; import android.view.ViewGroup; @@ -40,16 +41,18 @@ import static awais.instagrabber.utils.Constants.BOTTOM_TOOLBAR; import static awais.instagrabber.utils.Constants.DOWNLOAD_USER_FOLDER; import static awais.instagrabber.utils.Constants.FOLDER_PATH; import static awais.instagrabber.utils.Constants.FOLDER_SAVE_TO; +import static awais.instagrabber.utils.Constants.INSTADP; import static awais.instagrabber.utils.Constants.MARK_AS_SEEN; import static awais.instagrabber.utils.Constants.MUTED_VIDEOS; import static awais.instagrabber.utils.Constants.SHOW_FEED; +import static awais.instagrabber.utils.Constants.STORIESIG; import static awais.instagrabber.utils.Utils.settingsHelper; public final class SettingsDialog extends BottomSheetDialogFragment implements View.OnClickListener, AdapterView.OnItemSelectedListener, CompoundButton.OnCheckedChangeListener { private Activity activity; private FragmentManager fragmentManager; - private View btnSaveTo, btnImportExport, btnLogin, btnLogout, btnTimeSettings, btnReport; + private View btnSaveTo, btnImportExport, btnLogin, btnLogout, btnTimeSettings, btnReport, btnPrivacy; private AppCompatTextView settingTitle; private Spinner spAppTheme, spLanguage; private boolean somethingChanged = false; @@ -94,6 +97,7 @@ public final class SettingsDialog extends BottomSheetDialogFragment implements V btnImportExport = contentView.findViewById(R.id.importExport); btnTimeSettings = contentView.findViewById(R.id.btnTimeSettings); btnReport = contentView.findViewById(R.id.btnReport); + btnPrivacy = contentView.findViewById(R.id.btnPrivacy); Utils.setTooltipText(btnImportExport, R.string.import_export); @@ -103,6 +107,7 @@ public final class SettingsDialog extends BottomSheetDialogFragment implements V btnSaveTo.setOnClickListener(this); btnImportExport.setOnClickListener(this); btnTimeSettings.setOnClickListener(this); + btnPrivacy.setOnClickListener(this); if (Utils.isEmpty(settingsHelper.getString(Constants.COOKIE))) btnLogout.setEnabled(false); @@ -123,12 +128,16 @@ public final class SettingsDialog extends BottomSheetDialogFragment implements V final AppCompatCheckBox cbAutoplayVideos = contentView.findViewById(R.id.cbAutoplayVideos); final AppCompatCheckBox cbDownloadUsername = contentView.findViewById(R.id.cbDownloadUsername); final AppCompatCheckBox cbMarkAsSeen = contentView.findViewById(R.id.cbMarkAsSeen); + final AppCompatCheckBox cbInstadp = contentView.findViewById(R.id.cbInstadp); + final AppCompatCheckBox cbStoriesig = contentView.findViewById(R.id.cbStoriesig); cbSaveTo.setChecked(settingsHelper.getBoolean(FOLDER_SAVE_TO)); cbMuteVideos.setChecked(settingsHelper.getBoolean(MUTED_VIDEOS)); cbBottomToolbar.setChecked(settingsHelper.getBoolean(BOTTOM_TOOLBAR)); cbAutoplayVideos.setChecked(settingsHelper.getBoolean(AUTOPLAY_VIDEOS)); cbMarkAsSeen.setChecked(settingsHelper.getBoolean(MARK_AS_SEEN)); + cbInstadp.setChecked(settingsHelper.getBoolean(INSTADP)); + cbStoriesig.setChecked(settingsHelper.getBoolean(STORIESIG)); cbAutoloadPosts.setChecked(settingsHelper.getBoolean(AUTOLOAD_POSTS)); cbDownloadUsername.setChecked(settingsHelper.getBoolean(DOWNLOAD_USER_FOLDER)); @@ -140,6 +149,8 @@ public final class SettingsDialog extends BottomSheetDialogFragment implements V setupListener(cbAutoplayVideos); setupListener(cbDownloadUsername); setupListener(cbMarkAsSeen); + setupListener(cbInstadp); + setupListener(cbStoriesig); btnSaveTo.setEnabled(cbSaveTo.isChecked()); @@ -183,18 +194,18 @@ public final class SettingsDialog extends BottomSheetDialogFragment implements V if (ContextCompat.checkSelfPermission(activity, Utils.PERMS[0]) == PackageManager.PERMISSION_DENIED) requestPermissions(Utils.PERMS, 6007); else Utils.showImportExportDialog(activity); - } else if (v == btnTimeSettings) { new TimeSettingsDialog().show(fragmentManager, null); - } else if (v == btnReport) { CrashReporter.get(activity.getApplication()).zipLogs().startCrashEmailIntent(activity, true); - } else if (v == btnSaveTo) { if (ContextCompat.checkSelfPermission(activity, Utils.PERMS[0]) == PackageManager.PERMISSION_DENIED) requestPermissions(Utils.PERMS, 6200); else showDirectoryChooser(); - + } else if (v == btnPrivacy) { + final Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri.parse("https://instagrabber.austinhuang.me/disclosure#for-anonymous-users")); + startActivity(intent); } else if (v instanceof ViewGroup) ((ViewGroup) v).getChildAt(0).performClick(); } @@ -208,6 +219,8 @@ public final class SettingsDialog extends BottomSheetDialogFragment implements V else if (id == R.id.cbMuteVideos) settingsHelper.putBoolean(MUTED_VIDEOS, checked); else if (id == R.id.cbAutoloadPosts) settingsHelper.putBoolean(AUTOLOAD_POSTS, checked); else if (id == R.id.cbMarkAsSeen) settingsHelper.putBoolean(MARK_AS_SEEN, checked); + else if (id == R.id.cbInstadp) settingsHelper.putBoolean(INSTADP, checked); + else if (id == R.id.cbStoriesig) settingsHelper.putBoolean(STORIESIG, checked); else if (id == R.id.cbSaveTo) { settingsHelper.putBoolean(FOLDER_SAVE_TO, checked); btnSaveTo.setEnabled(checked); diff --git a/app/src/main/java/awais/instagrabber/models/HighlightModel.java b/app/src/main/java/awais/instagrabber/models/HighlightModel.java index d7af017a..e2e20dc4 100755 --- a/app/src/main/java/awais/instagrabber/models/HighlightModel.java +++ b/app/src/main/java/awais/instagrabber/models/HighlightModel.java @@ -1,11 +1,11 @@ package awais.instagrabber.models; public final class HighlightModel { - private final String title, thumbnailUrl; - private StoryModel[] storyModels; + private final String title, id, thumbnailUrl; - public HighlightModel(final String title, final String thumbnailUrl) { + public HighlightModel(final String title, final String id, final String thumbnailUrl) { this.title = title; + this.id = id; this.thumbnailUrl = thumbnailUrl; } @@ -13,15 +13,11 @@ public final class HighlightModel { return title; } + public String getId() { + return id; + } + public String getThumbnailUrl() { return thumbnailUrl; } - - public StoryModel[] getStoryModels() { - return storyModels; - } - - public void setStoryModels(final StoryModel[] storyModels) { - this.storyModels = storyModels; - } } \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/utils/Constants.java b/app/src/main/java/awais/instagrabber/utils/Constants.java index 4da09103..595e964d 100755 --- a/app/src/main/java/awais/instagrabber/utils/Constants.java +++ b/app/src/main/java/awais/instagrabber/utils/Constants.java @@ -20,6 +20,8 @@ public final class Constants { public static final String SHOW_FEED = "show_feed"; public static final String CUSTOM_DATE_TIME_FORMAT_ENABLED = "data_time_custom_enabled"; public static final String MARK_AS_SEEN = "mark_as_seen"; + public static final String INSTADP = "instadp"; + public static final String STORIESIG = "storiesig"; // never Export public static final String COOKIE = "cookie"; public static final String SHOW_QUICK_ACCESS_DIALOG = "show_quick_dlg"; @@ -49,6 +51,7 @@ public final class Constants { "Instagram 152.0.0.25.117 Android (27/8.1.0; 320dpi; 720x1362; motorola; motorola one; deen_sprout; qcom; pt_BR; 234847224)"; public static final String I_USER_AGENT = "Instagram 152.0.0.25.117 Android (27/8.1.0; 320dpi; 720x1362; motorola; motorola one; deen_sprout; qcom; pt_BR; 234847224)"; + public static final String A_USER_AGENT = "InstaGrabber.AustinHuang.me / InstaGrabber@AustinHuang.me"; // see https://github.com/dilame/instagram-private-api/blob/master/src/core/constants.ts public static final String SUPPORTED_CAPABILITIES = "[ { \"name\": \"SUPPORTED_SDK_VERSIONS\", \"value\":" + " \"13.0,14.0,15.0,16.0,17.0,18.0,19.0,20.0,21.0,22.0,23.0,24.0,25.0,26.0,27.0,28.0,29.0,30.0,31.0," + diff --git a/app/src/main/java/awais/instagrabber/utils/SettingsHelper.java b/app/src/main/java/awais/instagrabber/utils/SettingsHelper.java index 92d8ecbc..12517f36 100755 --- a/app/src/main/java/awais/instagrabber/utils/SettingsHelper.java +++ b/app/src/main/java/awais/instagrabber/utils/SettingsHelper.java @@ -22,10 +22,12 @@ import static awais.instagrabber.utils.Constants.DEVICE_UUID; import static awais.instagrabber.utils.Constants.DOWNLOAD_USER_FOLDER; import static awais.instagrabber.utils.Constants.FOLDER_PATH; import static awais.instagrabber.utils.Constants.FOLDER_SAVE_TO; +import static awais.instagrabber.utils.Constants.INSTADP; import static awais.instagrabber.utils.Constants.MARK_AS_SEEN; import static awais.instagrabber.utils.Constants.MUTED_VIDEOS; import static awais.instagrabber.utils.Constants.PREV_INSTALL_VERSION; import static awais.instagrabber.utils.Constants.SHOW_QUICK_ACCESS_DIALOG; +import static awais.instagrabber.utils.Constants.STORIESIG; public final class SettingsHelper { private final SharedPreferences sharedPreferences; @@ -107,7 +109,7 @@ public final class SettingsHelper { public @interface StringSettings {} @StringDef({DOWNLOAD_USER_FOLDER, BOTTOM_TOOLBAR, FOLDER_SAVE_TO, AUTOPLAY_VIDEOS, SHOW_QUICK_ACCESS_DIALOG, MUTED_VIDEOS, - AUTOLOAD_POSTS, CUSTOM_DATE_TIME_FORMAT_ENABLED, MARK_AS_SEEN}) + AUTOLOAD_POSTS, CUSTOM_DATE_TIME_FORMAT_ENABLED, MARK_AS_SEEN, INSTADP, STORIESIG}) public @interface BooleanSettings {} @StringDef({APP_THEME, APP_LANGUAGE, PREV_INSTALL_VERSION}) diff --git a/app/src/main/java/awais/instagrabber/utils/Utils.java b/app/src/main/java/awais/instagrabber/utils/Utils.java index 6daec057..7555e2d3 100755 --- a/app/src/main/java/awais/instagrabber/utils/Utils.java +++ b/app/src/main/java/awais/instagrabber/utils/Utils.java @@ -1173,95 +1173,6 @@ public final class Utils { dialog[0] = new AlertDialog.Builder(context).setView(importExportBinding.getRoot()).show(); } - // taken from Arrays.toString() - @NonNull - public static String highlightIdsMerger(final String... strings) { - if (strings != null) { - int iMax = strings.length - 1; - if (iMax != -1) { - final StringBuilder builder = new StringBuilder(); - builder.append('['); - for (int i = 0; ; i++) { - builder.append('"').append(strings[i]).append('"'); - if (i == iMax) return builder.append(']').toString(); - builder.append(','); - } - } - - } - return "[]"; - } - - public static void putHighlightModels(final HttpURLConnection conn, final Object[] model) throws Exception { - final boolean isHighlightModel = model instanceof HighlightModel[]; - final boolean isFeedStoryModel = model instanceof FeedStoryModel[]; - - if (isHighlightModel || isFeedStoryModel) { - final JSONArray highlightsMediaReel = new JSONObject(Utils.readFromConnection(conn)).getJSONObject("data").getJSONArray("reels_media"); - final int mediaLength = highlightsMediaReel.length(); - - for (int i = 0; i < mediaLength; ++i) { - final JSONArray items = highlightsMediaReel.getJSONObject(i).getJSONArray("items"); - final int itemsLen = items.length(); - - final StoryModel[] storyModels = new StoryModel[itemsLen]; - for (int j = 0; j < itemsLen; ++j) { - final JSONObject data = items.getJSONObject(j); - - final boolean isVideo = data.getBoolean("is_video"); - - boolean hasTappableObjecs = data.has("tappable_objects"); - final JSONArray tappableObjects; - final int tappableLength; - if (hasTappableObjecs) { - tappableObjects = data.getJSONArray("tappable_objects"); - tappableLength = tappableObjects.length(); - hasTappableObjecs = tappableLength > 0; - } else { - tappableLength = 0; - tappableObjects = null; - } - - storyModels[j] = new StoryModel(data.getString(Constants.EXTRAS_ID), data.getString("display_url"), - isVideo ? MediaItemType.MEDIA_TYPE_VIDEO : MediaItemType.MEDIA_TYPE_IMAGE, - data.getLong("taken_at_timestamp"), data.getJSONObject("owner").getString("username"), - data.getJSONObject("owner").getString("id"), data.getBoolean("can_reply")); - - if (isVideo && data.has("video_resources")) - storyModels[j].setVideoUrl(Utils.getHighQualityPost(data.getJSONArray("video_resources"), true, false, false)); - - if (!data.isNull("story_app_attribution")) - storyModels[j].setSpotify(data.getJSONObject("story_app_attribution").optString("content_url").split("\\?")[0]); - - if (hasTappableObjecs) { - for (int k = 0; k < tappableLength; ++k) { - JSONObject jsonObject = tappableObjects.getJSONObject(k); - if (jsonObject.getString("__typename").equals("GraphTappableFeedMedia") && jsonObject.has("media")) { - storyModels[j].setTappableShortCode(jsonObject.getJSONObject("media").getString(Constants.EXTRAS_SHORTCODE)); - } - else if (jsonObject.optString("__typename").equals("GraphTappableStoryPoll") && !jsonObject.isNull("id")) { - storyModels[j].setPoll(new PollModel( - jsonObject.getString("id"), - jsonObject.getString("question"), - jsonObject.getJSONArray("tallies").getJSONObject(0).getString("text"), - jsonObject.getJSONArray("tallies").getJSONObject(0).getInt("count"), - jsonObject.getJSONArray("tallies").getJSONObject(1).getString("text"), - jsonObject.getJSONArray("tallies").getJSONObject(1).getInt("count"), - jsonObject.optInt("viewer_vote", -1) - )); - } - } - } - } - - if (isHighlightModel) - ((HighlightModel[]) model)[i].setStoryModels(storyModels); - else - ((FeedStoryModel[]) model)[i].setStoryModels(storyModels); - } - } - } - public static String sign(final String message) { try { Mac hasher = Mac.getInstance("HmacSHA256"); diff --git a/app/src/main/res/layout/dialog_main_settings.xml b/app/src/main/res/layout/dialog_main_settings.xml index e04a35e8..be0c473a 100755 --- a/app/src/main/res/layout/dialog_main_settings.xml +++ b/app/src/main/res/layout/dialog_main_settings.xml @@ -259,6 +259,17 @@ android:textSize="16sp" /> + + + + + + + + + + + + + + + + + + + + + Save to custom folder Select folder Theme Settings + Only affects logged-in users: + Only affects anonymous users: + Privacy + Use Instadp for high definition profile pictures + Use storiesig for stories and highlights from public users Import/Export Select language What to do? @@ -67,6 +72,7 @@ View Post View Post Spotify + Can\'t interact if y\'ain\'t logged in! Vote Vote successful! You have already voted! diff --git a/fastlane/metadata/android/changelogs/41.txt b/fastlane/metadata/android/changelogs/41.txt new file mode 100644 index 00000000..f0a10412 --- /dev/null +++ b/fastlane/metadata/android/changelogs/41.txt @@ -0,0 +1,4 @@ +* You can now view public stories and highlights WITHOUT logging in, but only if you enable it in Settings +* Originally, viewing HD avatars without logging in automatically uses Instadp, now you have to enable it in Settings as well + * Consent is important! A button now tells you +* Highlights now support showing sticker infos \ No newline at end of file