diff --git a/.github/ISSUE_TEMPLATE/ban_report.md b/.github/ISSUE_TEMPLATE/ban_report.md index 99d4d33c..180ca957 100644 --- a/.github/ISSUE_TEMPLATE/ban_report.md +++ b/.github/ISSUE_TEMPLATE/ban_report.md @@ -18,8 +18,8 @@ assignees: 'austinhuang0131' ## Answer honestly. Check accordingly to your situation. - [ ] I had prior rule violations on Instagram, specifically: -- [ ] I have admitted the use of InstaGrabber on Instagram. -- [ ] I have admitted the use of InstaGrabber to a friend who uses Instagram. +- [ ] I have admitted the use of Barinsta on Instagram. +- [ ] I have admitted the use of Barinsta to a friend who uses Instagram. - [ ] I have modified the source code of the app that I use, other than what is present in this repo. Specifically: ## Describe your case, including your usage pattern, but without private information. diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 2c933b53..a3abf780 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -6,10 +6,10 @@ labels: bug assignees: '' --- - + - [ ] My app is on the latest version. I understand that any other version is not supported. -- [ ] I have read [the FAQ](https://instagrabber.austinhuang.me/faq). +- [ ] I have read [the FAQ](https://barinsta.austinhuang.me/en/latest/faq). ## Environment diff --git a/.github/ISSUE_TEMPLATE/questions.md b/.github/ISSUE_TEMPLATE/questions.md index fcaaf0d5..aff97d3e 100644 --- a/.github/ISSUE_TEMPLATE/questions.md +++ b/.github/ISSUE_TEMPLATE/questions.md @@ -7,7 +7,13 @@ assignees: '' --- diff --git a/app/build.gradle b/app/build.gradle index a54f0eaa..66aab6cf 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,8 +10,8 @@ android { minSdkVersion 21 targetSdkVersion 29 - versionCode 56 - versionName '19.0.4' + versionCode 57 + versionName '19.0.5' multiDexEnabled true @@ -60,11 +60,13 @@ dependencies { def appcompat_version = "1.2.0" def nav_version = '2.3.2' + def exoplayer_version = '2.12.0' implementation 'com.google.android.material:material:1.3.0-beta01' - implementation 'com.google.android.exoplayer:exoplayer-core:2.12.0' - implementation 'com.google.android.exoplayer:exoplayer-ui:2.12.0' + implementation "com.google.android.exoplayer:exoplayer-core:$exoplayer_version" + implementation "com.google.android.exoplayer:exoplayer-dash:$exoplayer_version" + implementation "com.google.android.exoplayer:exoplayer-ui:$exoplayer_version" implementation "androidx.appcompat:appcompat:$appcompat_version" implementation "androidx.appcompat:appcompat-resources:$appcompat_version" diff --git a/app/src/main/java/awais/instagrabber/adapters/DiscoverTopicsAdapter.java b/app/src/main/java/awais/instagrabber/adapters/DiscoverTopicsAdapter.java index ed197634..fc4e0cce 100644 --- a/app/src/main/java/awais/instagrabber/adapters/DiscoverTopicsAdapter.java +++ b/app/src/main/java/awais/instagrabber/adapters/DiscoverTopicsAdapter.java @@ -10,7 +10,7 @@ import androidx.recyclerview.widget.ListAdapter; import awais.instagrabber.adapters.viewholder.TopicClusterViewHolder; import awais.instagrabber.databinding.ItemDiscoverTopicBinding; -import awais.instagrabber.models.TopicCluster; +import awais.instagrabber.repositories.responses.discover.TopicCluster; import awais.instagrabber.utils.ResponseBodyUtils; public class DiscoverTopicsAdapter extends ListAdapter { diff --git a/app/src/main/java/awais/instagrabber/adapters/NotificationsAdapter.java b/app/src/main/java/awais/instagrabber/adapters/NotificationsAdapter.java index 124abe73..e912878f 100644 --- a/app/src/main/java/awais/instagrabber/adapters/NotificationsAdapter.java +++ b/app/src/main/java/awais/instagrabber/adapters/NotificationsAdapter.java @@ -83,7 +83,7 @@ public final class NotificationsAdapter extends ListAdapter o2.getTimestamp() ? -1 : (o1.getTimestamp() == o2.getTimestamp() ? 0 : 1); + return Long.compare(o2.getTimestamp(), o1.getTimestamp()); }); return listCopy; } diff --git a/app/src/main/java/awais/instagrabber/adapters/PostsMediaAdapter.java b/app/src/main/java/awais/instagrabber/adapters/PostsMediaAdapter.java deleted file mode 100755 index 86fef229..00000000 --- a/app/src/main/java/awais/instagrabber/adapters/PostsMediaAdapter.java +++ /dev/null @@ -1,57 +0,0 @@ -// package awais.instagrabber.adapters; -// -// import android.view.LayoutInflater; -// import android.view.View; -// import android.view.ViewGroup; -// -// import androidx.annotation.NonNull; -// import androidx.recyclerview.widget.RecyclerView; -// -// import awais.instagrabber.R; -// import awais.instagrabber.adapters.viewholder.PostMediaViewHolder; -// import awais.instagrabber.databinding.ItemChildPostBinding; -// import awais.instagrabber.models.BasePostModel; -// import awais.instagrabber.models.ViewerPostModel; -// -// public final class PostsMediaAdapter extends RecyclerView.Adapter { -// private final View.OnClickListener clickListener; -// private ViewerPostModel[] postModels; -// -// public PostsMediaAdapter(final ViewerPostModel[] postModels, final View.OnClickListener clickListener) { -// this.postModels = postModels; -// this.clickListener = clickListener; -// } -// -// @NonNull -// @Override -// public PostMediaViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) { -// final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext()); -// layoutInflater.inflate(R.layout.item_child_post, parent, false); -// final ItemChildPostBinding binding = ItemChildPostBinding.inflate(layoutInflater, parent, false); -// return new PostMediaViewHolder(binding); -// } -// -// @Override -// public void onBindViewHolder(@NonNull final PostMediaViewHolder holder, final int position) { -// final ViewerPostModel postModel = postModels[position]; -// holder.bind(postModel, position, clickListener); -// } -// -// public void setData(final ViewerPostModel[] postModels) { -// this.postModels = postModels; -// notifyDataSetChanged(); -// } -// -// public ViewerPostModel getItemAt(final int position) { -// return postModels == null ? null : postModels[position]; -// } -// -// @Override -// public int getItemCount() { -// return postModels == null ? 0 : postModels.length; -// } -// -// public BasePostModel[] getPostModels() { -// return postModels; -// } -// } \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/FeedStoryViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/FeedStoryViewHolder.java index efc16e02..7e8b526b 100644 --- a/app/src/main/java/awais/instagrabber/adapters/viewholder/FeedStoryViewHolder.java +++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/FeedStoryViewHolder.java @@ -33,5 +33,9 @@ public final class FeedStoryViewHolder extends RecyclerView.ViewHolder { binding.title.setAlpha(model.isFullyRead() ? 0.5F : 1.0F); binding.icon.setImageURI(profileModel.getProfilePicUrl()); binding.icon.setAlpha(model.isFullyRead() ? 0.5F : 1.0F); + + if (model.isLive()) binding.icon.setStoriesBorder(2); + else if (model.isBestie()) binding.icon.setStoriesBorder(1); + else binding.icon.setStoriesBorder(0); } } \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/NotificationViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/NotificationViewHolder.java index 1c49dbee..8d56a246 100644 --- a/app/src/main/java/awais/instagrabber/adapters/viewholder/NotificationViewHolder.java +++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/NotificationViewHolder.java @@ -1,9 +1,7 @@ package awais.instagrabber.adapters.viewholder; -import android.text.Spannable; import android.text.TextUtils; import android.view.View; -import android.widget.TextView; import androidx.recyclerview.widget.RecyclerView; @@ -54,7 +52,7 @@ public final class NotificationViewHolder extends RecyclerView.ViewHolder { subtext = model.getText(); break; case AYML: - subtext = model.getPostId(); + subtext = model.getPreviewPic(); break; } binding.tvSubComment.setText(model.getType() == NotificationType.AYML ? model.getText() : subtext); @@ -62,8 +60,7 @@ public final class NotificationViewHolder extends RecyclerView.ViewHolder { binding.tvComment.setText(subtext); binding.tvComment.setVisibility(TextUtils.isEmpty(subtext) ? View.GONE : View.VISIBLE); binding.tvSubComment.setVisibility(model.getType() == NotificationType.AYML ? View.VISIBLE : View.GONE); - } - else if (text != -1) { + } else if (text != -1) { binding.tvComment.setText(text); binding.tvSubComment.setVisibility(subtext == null ? View.GONE : View.VISIBLE); } @@ -81,8 +78,7 @@ public final class NotificationViewHolder extends RecyclerView.ViewHolder { if (model.getType() == NotificationType.AYML) { binding.ivPreviewPic.setVisibility(View.GONE); - } - else if (TextUtils.isEmpty(model.getPreviewPic())) { + } else if (TextUtils.isEmpty(model.getPreviewPic())) { binding.ivPreviewPic.setVisibility(View.INVISIBLE); } else { binding.ivPreviewPic.setVisibility(View.VISIBLE); diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/PostMediaViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/PostMediaViewHolder.java deleted file mode 100755 index d719e299..00000000 --- a/app/src/main/java/awais/instagrabber/adapters/viewholder/PostMediaViewHolder.java +++ /dev/null @@ -1,29 +0,0 @@ -// package awais.instagrabber.adapters.viewholder; -// -// import android.view.View; -// -// import androidx.annotation.NonNull; -// import androidx.recyclerview.widget.RecyclerView; -// -// import awais.instagrabber.databinding.ItemChildPostBinding; -// import awais.instagrabber.models.ViewerPostModel; -// -// public final class PostMediaViewHolder extends RecyclerView.ViewHolder { -// -// private final ItemChildPostBinding binding; -// -// public PostMediaViewHolder(@NonNull final ItemChildPostBinding binding) { -// super(binding.getRoot()); -// this.binding = binding; -// } -// -// public void bind(final ViewerPostModel model, final int position, final View.OnClickListener clickListener) { -// if (model == null) return; -// // model.setPosition(position); -// itemView.setTag(model); -// itemView.setOnClickListener(clickListener); -// binding.selectedView.setVisibility(model.isCurrentSlide() ? View.VISIBLE : View.GONE); -// binding.isDownloaded.setVisibility(model.isDownloaded() ? View.VISIBLE : View.GONE); -// binding.icon.setImageURI(model.getDisplayUrl()); -// } -// } diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/TopicClusterViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/TopicClusterViewHolder.java index b0fda576..e4526eea 100644 --- a/app/src/main/java/awais/instagrabber/adapters/viewholder/TopicClusterViewHolder.java +++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/TopicClusterViewHolder.java @@ -26,7 +26,7 @@ import java.util.concurrent.atomic.AtomicInteger; import awais.instagrabber.R; import awais.instagrabber.adapters.DiscoverTopicsAdapter; import awais.instagrabber.databinding.ItemDiscoverTopicBinding; -import awais.instagrabber.models.TopicCluster; +import awais.instagrabber.repositories.responses.discover.TopicCluster; import awais.instagrabber.utils.ResponseBodyUtils; public class TopicClusterViewHolder extends RecyclerView.ViewHolder { diff --git a/app/src/main/java/awais/instagrabber/asyncs/direct_messages/CreateThreadAction.java b/app/src/main/java/awais/instagrabber/asyncs/CreateThreadAction.java similarity index 98% rename from app/src/main/java/awais/instagrabber/asyncs/direct_messages/CreateThreadAction.java rename to app/src/main/java/awais/instagrabber/asyncs/CreateThreadAction.java index 821ca206..74a24496 100644 --- a/app/src/main/java/awais/instagrabber/asyncs/direct_messages/CreateThreadAction.java +++ b/app/src/main/java/awais/instagrabber/asyncs/CreateThreadAction.java @@ -1,4 +1,4 @@ -package awais.instagrabber.asyncs.direct_messages; +package awais.instagrabber.asyncs; import android.os.AsyncTask; import android.util.Log; diff --git a/app/src/main/java/awais/instagrabber/asyncs/LocationFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/LocationFetcher.java index 99c0c948..8ccd47ce 100644 --- a/app/src/main/java/awais/instagrabber/asyncs/LocationFetcher.java +++ b/app/src/main/java/awais/instagrabber/asyncs/LocationFetcher.java @@ -52,8 +52,7 @@ public final class LocationFetcher extends AsyncTask // final JSONArray edges = timelineMedia.getJSONArray("edges"); // } result = new LocationModel( - location.getString(Constants.EXTRAS_ID), - location.getString("slug"), + location.getLong(Constants.EXTRAS_ID), location.getString("name"), location.getString("blurb"), location.getString("website"), diff --git a/app/src/main/java/awais/instagrabber/asyncs/direct_messages/DirectThreadBroadcaster.java b/app/src/main/java/awais/instagrabber/asyncs/direct_messages/DirectThreadBroadcaster.java deleted file mode 100644 index 6a99a190..00000000 --- a/app/src/main/java/awais/instagrabber/asyncs/direct_messages/DirectThreadBroadcaster.java +++ /dev/null @@ -1,252 +0,0 @@ -// package awais.instagrabber.asyncs.direct_messages; -// -// import android.os.AsyncTask; -// import android.util.Log; -// -// import org.json.JSONObject; -// -// import java.io.BufferedReader; -// import java.io.DataOutputStream; -// import java.io.IOException; -// import java.io.InputStreamReader; -// import java.net.HttpURLConnection; -// import java.net.URL; -// import java.util.HashMap; -// import java.util.Map; -// import java.util.UUID; -// -// import awais.instagrabber.repositories.requests.directmessages.BroadcastOptions; -// import awais.instagrabber.repositories.responses.directmessages.DirectThreadBroadcastResponse; -// import awais.instagrabber.utils.Constants; -// import awais.instagrabber.utils.CookieUtils; -// import awais.instagrabber.utils.Utils; -// -// import static awais.instagrabber.utils.Utils.settingsHelper; -// -// public class DirectThreadBroadcaster extends AsyncTask { -// private static final String TAG = "DirectThreadBroadcaster"; -// -// private final String threadId; -// -// private OnBroadcastCompleteListener listener; -// -// public DirectThreadBroadcaster(String threadId) { -// this.threadId = threadId; -// } -// -// @Override -// protected DirectThreadBroadcastResponse doInBackground(final BroadcastOptions... broadcastOptionsArray) { -// if (broadcastOptionsArray == null || broadcastOptionsArray.length == 0 || broadcastOptionsArray[0] == null) { -// return null; -// } -// final BroadcastOptions broadcastOptions = broadcastOptionsArray[0]; -// final String cookie = settingsHelper.getString(Constants.COOKIE); -// final String cc = UUID.randomUUID().toString(); -// final Map form = new HashMap<>(); -// form.put("_csrftoken", CookieUtils.getCsrfTokenFromCookie(cookie)); -// form.put("_uid", CookieUtils.getUserIdFromCookie(cookie)); -// form.put("__uuid", settingsHelper.getString(Constants.DEVICE_UUID)); -// form.put("client_context", cc); -// form.put("mutation_token", cc); -// form.putAll(broadcastOptions.getFormMap()); -// form.put("thread_id", threadId); -// form.put("action", "send_item"); -// final String message = new JSONObject(form).toString(); -// final String content = Utils.sign(message); -// final String url = "https://i.instagram.com/api/v1/direct_v2/threads/broadcast/" + broadcastOptions.getItemType().getValue() + "/"; -// HttpURLConnection connection = null; -// DataOutputStream outputStream = null; -// BufferedReader r = null; -// try { -// connection = (HttpURLConnection) new URL(url).openConnection(); -// connection.setRequestMethod("POST"); -// connection.setRequestProperty("User-Agent", Constants.I_USER_AGENT); -// connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); -// if (content != null) { -// connection.setRequestProperty("Content-Length", "" + content.getBytes().length); -// } -// connection.setUseCaches(false); -// connection.setDoOutput(true); -// outputStream = new DataOutputStream(connection.getOutputStream()); -// outputStream.writeBytes(content); -// outputStream.flush(); -// final int responseCode = connection.getResponseCode(); -// if (responseCode != HttpURLConnection.HTTP_OK) { -// Log.d(TAG, responseCode + ": " + content + ": " + cookie); -// return new DirectThreadBroadcastResponse(responseCode, null); -// } -// r = new BufferedReader(new InputStreamReader(connection.getInputStream())); -// final StringBuilder builder = new StringBuilder(); -// for (String line = r.readLine(); line != null; line = r.readLine()) { -// if (builder.length() != 0) { -// builder.append("\n"); -// } -// builder.append(line); -// } -// return new DirectThreadBroadcastResponse(responseCode, new JSONObject(builder.toString())); -// } catch (Exception e) { -// Log.e(TAG, "Error", e); -// } finally { -// if (r != null) { -// try { -// r.close(); -// } catch (IOException ignored) { -// } -// } -// if (outputStream != null) { -// try { -// outputStream.close(); -// } catch (IOException ignored) { -// } -// } -// if (connection != null) { -// connection.disconnect(); -// } -// } -// return null; -// } -// -// @Override -// protected void onPostExecute(final DirectThreadBroadcastResponse result) { -// if (listener != null) { -// listener.onTaskComplete(result); -// } -// } -// -// public void setOnTaskCompleteListener(final OnBroadcastCompleteListener listener) { -// if (listener != null) { -// this.listener = listener; -// } -// } -// -// public interface OnBroadcastCompleteListener { -// void onTaskComplete(DirectThreadBroadcastResponse response); -// } -// -// public enum ItemType { -// TEXT("text"), -// REACTION("reaction"), -// REELSHARE("reel_share"), -// IMAGE("configure_photo"); -// -// private final String value; -// -// ItemType(final String value) { -// this.value = value; -// } -// -// public String getValue() { -// return value; -// } -// } -// -// public static abstract class BroadcastOptions { -// private final ItemType itemType; -// -// public BroadcastOptions(final ItemType itemType) { -// this.itemType = itemType; -// } -// -// public ItemType getItemType() { -// return itemType; -// } -// -// abstract Map getFormMap(); -// } -// -// public static class TextBroadcastOptions extends BroadcastOptions { -// private final String text; -// -// public TextBroadcastOptions(String text) throws UnsupportedEncodingException { -// super(ItemType.TEXT); -// this.text = URLEncoder.encode(text, "UTF-8") -// .replaceAll("\\+", "%20").replaceAll("%21", "!").replaceAll("%27", "'").replaceAll("%22", "\\\"") -// .replaceAll("%28", "(").replaceAll("%29", ")").replaceAll("%7E", "~").replaceAll("%0A", "\n"); -// } -// -// @Override -// Map getFormMap() { -// return Collections.singletonMap("text", text); -// } -// } -// -// public static class ReactionBroadcastOptions extends BroadcastOptions { -// private final String itemId; -// private final boolean delete; -// -// public ReactionBroadcastOptions(String itemId, boolean delete) { -// super(ItemType.REACTION); -// this.itemId = itemId; -// this.delete = delete; -// } -// -// @Override -// Map getFormMap() { -// final Map form = new HashMap<>(); -// form.put("item_id", itemId); -// form.put("reaction_status", delete ? "deleted" : "created"); -// form.put("reaction_type", "like"); -// return form; -// } -// } -// -// public static class StoryReplyBroadcastOptions extends BroadcastOptions { -// private final String text, mediaId, reelId; -// -// public StoryReplyBroadcastOptions(String text, String mediaId, String reelId) throws UnsupportedEncodingException { -// super(ItemType.REELSHARE); -// this.text = URLEncoder.encode(text, "UTF-8") -// .replaceAll("\\+", "%20").replaceAll("%21", "!").replaceAll("%27", "'") -// .replaceAll("%28", "(").replaceAll("%29", ")").replaceAll("%7E", "~").replaceAll("%0A", "\n"); -// this.mediaId = mediaId; -// this.reelId = reelId; // or user id, usually same -// } -// -// @Override -// Map getFormMap() { -// final Map form = new HashMap<>(); -// form.put("text", text); -// form.put("media_id", mediaId); -// form.put("reel_id", reelId); -// form.put("entry", "reel"); -// return form; -// } -// } -// -// public static class ImageBroadcastOptions extends BroadcastOptions { -// final boolean allowFullAspectRatio; -// final String uploadId; -// -// public ImageBroadcastOptions(final boolean allowFullAspectRatio, final String uploadId) { -// super(ItemType.IMAGE); -// this.allowFullAspectRatio = allowFullAspectRatio; -// this.uploadId = uploadId; -// } -// -// @Override -// Map getFormMap() { -// final Map form = new HashMap<>(); -// form.put("allow_full_aspect_ratio", String.valueOf(allowFullAspectRatio)); -// form.put("upload_id", uploadId); -// return form; -// } -// } -// -// public static class DirectThreadBroadcastResponse { -// private final int responseCode; -// private final JSONObject response; -// -// public DirectThreadBroadcastResponse(int responseCode, JSONObject response) { -// this.responseCode = responseCode; -// this.response = response; -// } -// -// public int getResponseCode() { -// return responseCode; -// } -// -// public JSONObject getResponse() { -// return response; -// } -// } -// } diff --git a/app/src/main/java/awais/instagrabber/customviews/CircularImageView.java b/app/src/main/java/awais/instagrabber/customviews/CircularImageView.java index 29828709..2f687868 100755 --- a/app/src/main/java/awais/instagrabber/customviews/CircularImageView.java +++ b/app/src/main/java/awais/instagrabber/customviews/CircularImageView.java @@ -49,14 +49,15 @@ public class CircularImageView extends SimpleDraweeView { setBackgroundResource(R.drawable.shape_oval_light); } - public void setStoriesBorder() { + /* types: 0 clear, 1 green (feed bestie / has story), 2 red (live) */ + public void setStoriesBorder(final int type) { // private final int borderSize = 8; - final int color = Color.GREEN; + final int color = type == 2 ? Color.RED : Color.GREEN; RoundingParams roundingParams = getHierarchy().getRoundingParams(); if (roundingParams == null) { roundingParams = RoundingParams.asCircle().setRoundingMethod(RoundingParams.RoundingMethod.BITMAP_ONLY); } - roundingParams.setBorder(color, 5.0f); + roundingParams.setBorder(color, type == 0 ? 0f : 5.0f); getHierarchy().setRoundingParams(roundingParams); } } \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java b/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java index 8f37df9f..366eaf35 100644 --- a/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java @@ -59,6 +59,7 @@ import awais.instagrabber.models.HashtagModel; import awais.instagrabber.models.PostsLayoutPreferences; import awais.instagrabber.models.StoryModel; import awais.instagrabber.models.enums.FavoriteType; +import awais.instagrabber.repositories.requests.StoryViewerOptions; import awais.instagrabber.repositories.responses.Location; import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.utils.Constants; @@ -559,7 +560,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe if (!hasStories) return; // show stories final NavDirections action = HashTagFragmentDirections - .actionHashtagFragmentToStoryViewerFragment(-1, null, true, false, hashtagModel.getName(), hashtagModel.getName(), false, false); + .actionHashtagFragmentToStoryViewerFragment(StoryViewerOptions.forHashtag(hashtagModel.getName())); NavHostFragment.findNavController(this).navigate(action); }); } @@ -576,16 +577,12 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe if (!isLoggedIn) return; storiesFetching = true; storiesService.getUserStory( - hashtagModel.getName(), - null, - false, - true, - false, + StoryViewerOptions.forHashtag(hashtagModel.getName()), new ServiceCallback>() { @Override public void onSuccess(final List storyModels) { if (storyModels != null && !storyModels.isEmpty()) { - hashtagDetailsBinding.mainHashtagImage.setStoriesBorder(); + hashtagDetailsBinding.mainHashtagImage.setStoriesBorder(1); hasStories = true; } else { hasStories = false; diff --git a/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java b/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java index 9e1b3121..67b7e5d7 100644 --- a/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java @@ -19,7 +19,6 @@ import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.widget.TextView; import android.widget.Toast; import androidx.activity.OnBackPressedCallback; @@ -61,6 +60,7 @@ import awais.instagrabber.models.LocationModel; import awais.instagrabber.models.PostsLayoutPreferences; import awais.instagrabber.models.StoryModel; import awais.instagrabber.models.enums.FavoriteType; +import awais.instagrabber.repositories.requests.StoryViewerOptions; import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.CookieUtils; @@ -72,6 +72,7 @@ import awais.instagrabber.webservices.StoriesService; import awaisomereport.LogCollector; import static androidx.core.content.PermissionChecker.checkSelfPermission; +import static awais.instagrabber.fragments.HashTagFragment.ARG_HASHTAG; import static awais.instagrabber.utils.DownloadUtils.WRITE_PERMISSION; import static awais.instagrabber.utils.Utils.logCollector; import static awais.instagrabber.utils.Utils.settingsHelper; @@ -396,7 +397,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR } private void setupLocationDetails() { - final String locationId = locationModel.getId(); + final long locationId = locationModel.getId(); // binding.swipeRefreshLayout.setRefreshing(true); locationDetailsBinding.mainLocationImage.setImageURI(locationModel.getSdProfilePic()); final String postCount = String.valueOf(locationModel.getPostCount()); @@ -416,15 +417,29 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR if (TextUtils.isEmpty(biography)) { locationDetailsBinding.locationBiography.setVisibility(View.GONE); - } else if (TextUtils.hasMentions(biography)) { - locationDetailsBinding.locationBiography.setVisibility(View.VISIBLE); - biography = TextUtils.getMentionText(biography); - locationDetailsBinding.locationBiography.setText(biography, TextView.BufferType.SPANNABLE); - // binding.locationBiography.setMentionClickListener(mentionClickListener); } else { locationDetailsBinding.locationBiography.setVisibility(View.VISIBLE); locationDetailsBinding.locationBiography.setText(biography); - locationDetailsBinding.locationBiography.setMentionClickListener(null); + locationDetailsBinding.locationBiography.addOnHashtagListener(autoLinkItem -> { + final NavController navController = NavHostFragment.findNavController(this); + final Bundle bundle = new Bundle(); + final String originalText = autoLinkItem.getOriginalText().trim(); + bundle.putString(ARG_HASHTAG, originalText); + navController.navigate(R.id.action_global_hashTagFragment, bundle); + }); + locationDetailsBinding.locationBiography.addOnMentionClickListener(autoLinkItem -> { + final String originalText = autoLinkItem.getOriginalText().trim(); + navigateToProfile(originalText); + }); + locationDetailsBinding.locationBiography.addOnEmailClickListener(autoLinkItem -> Utils.openEmailAddress(getContext(), + autoLinkItem.getOriginalText() + .trim())); + locationDetailsBinding.locationBiography + .addOnURLClickListener(autoLinkItem -> Utils.openURL(getContext(), autoLinkItem.getOriginalText().trim())); + locationDetailsBinding.locationBiography.setOnLongClickListener(v -> { + Utils.copyText(getContext(), biography); + return true; + }); } if (!locationModel.getGeo().startsWith("geo:0.0,0.0?z=17")) { @@ -452,7 +467,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR final FavoriteDataSource dataSource = FavoriteDataSource.getInstance(getContext()); final FavoriteRepository favoriteRepository = FavoriteRepository.getInstance(dataSource); locationDetailsBinding.favChip.setVisibility(View.VISIBLE); - favoriteRepository.getFavorite(locationId, FavoriteType.LOCATION, new RepositoryCallback() { + favoriteRepository.getFavorite(String.valueOf(locationId), FavoriteType.LOCATION, new RepositoryCallback() { @Override public void onSuccess(final Favorite result) { locationDetailsBinding.favChip.setChipIconResource(R.drawable.ic_star_check_24); @@ -460,7 +475,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR locationDetailsBinding.favChip.setText(R.string.favorite_short); favoriteRepository.insertOrUpdateFavorite(new Favorite( result.getId(), - locationId, + String.valueOf(locationId), FavoriteType.LOCATION, locationModel.getName(), locationModel.getSdProfilePic(), @@ -482,10 +497,10 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR } }); locationDetailsBinding.favChip.setOnClickListener(v -> { - favoriteRepository.getFavorite(locationId, FavoriteType.LOCATION, new RepositoryCallback() { + favoriteRepository.getFavorite(String.valueOf(locationId), FavoriteType.LOCATION, new RepositoryCallback() { @Override public void onSuccess(final Favorite result) { - favoriteRepository.deleteFavorite(locationId, FavoriteType.LOCATION, new RepositoryCallback() { + favoriteRepository.deleteFavorite(String.valueOf(locationId), FavoriteType.LOCATION, new RepositoryCallback() { @Override public void onSuccess(final Void result) { locationDetailsBinding.favChip.setText(R.string.add_to_favorites); @@ -502,7 +517,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR public void onDataNotAvailable() { favoriteRepository.insertOrUpdateFavorite(new Favorite( 0, - locationId, + String.valueOf(locationId), FavoriteType.LOCATION, locationModel.getName(), locationModel.getSdProfilePic(), @@ -525,7 +540,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR if (hasStories) { // show stories final NavDirections action = LocationFragmentDirections - .actionLocationFragmentToStoryViewerFragment(-1, null, false, true, locationId, locationModel.getName(), false, false); + .actionLocationFragmentToStoryViewerFragment(StoryViewerOptions.forLocation(locationId, locationModel.getName())); NavHostFragment.findNavController(this).navigate(action); } }); @@ -542,27 +557,24 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR private void fetchStories() { if (isLoggedIn) { storiesFetching = true; - storiesService.getUserStory(String.valueOf(locationId), - null, - true, - false, - false, - new ServiceCallback>() { - @Override - public void onSuccess(final List storyModels) { - if (storyModels != null && !storyModels.isEmpty()) { - locationDetailsBinding.mainLocationImage.setStoriesBorder(); - hasStories = true; - } - storiesFetching = false; - } + storiesService.getUserStory( + StoryViewerOptions.forLocation(locationId, locationModel.getName()), + new ServiceCallback>() { + @Override + public void onSuccess(final List storyModels) { + if (storyModels != null && !storyModels.isEmpty()) { + locationDetailsBinding.mainLocationImage.setStoriesBorder(1); + hasStories = true; + } + storiesFetching = false; + } - @Override - public void onFailure(final Throwable t) { - Log.e(TAG, "Error", t); - storiesFetching = false; - } - }); + @Override + public void onFailure(final Throwable t) { + Log.e(TAG, "Error", t); + storiesFetching = false; + } + }); } } diff --git a/app/src/main/java/awais/instagrabber/fragments/NotificationsViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/NotificationsViewerFragment.java index 27b43b79..d0b745b6 100644 --- a/app/src/main/java/awais/instagrabber/fragments/NotificationsViewerFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/NotificationsViewerFragment.java @@ -36,6 +36,7 @@ import awais.instagrabber.interfaces.FetchListener; import awais.instagrabber.interfaces.MentionClickListener; import awais.instagrabber.models.NotificationModel; import awais.instagrabber.models.enums.NotificationType; +import awais.instagrabber.repositories.requests.StoryViewerOptions; import awais.instagrabber.repositories.responses.FriendshipChangeResponse; import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.utils.Constants; @@ -73,8 +74,9 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe @Override public void onPreviewClick(final NotificationModel model) { if (model.getType() == NotificationType.RESPONDED_STORY) { - final NavDirections action = NotificationsViewerFragmentDirections.actionNotificationsViewerFragmentToStoryViewerFragment( - -1, null, false, false, model.getPostId(), model.getUsername(), false, true); + final NavDirections action = NotificationsViewerFragmentDirections + .actionNotificationsViewerFragmentToStoryViewerFragment(StoryViewerOptions.forStory(model.getPostId(), + model.getUsername())); NavHostFragment.findNavController(NotificationsViewerFragment.this).navigate(action); } else { mediaService.fetch(model.getPostId(), new ServiceCallback() { @@ -110,7 +112,7 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe getString(R.string.open_profile), getString(R.string.view_story) }; - } else if (model.getPostId() != null) { + } else if (model.getPostId() > 0) { commentDialogList = new String[]{ getString(R.string.open_profile), getString(R.string.view_post) @@ -146,8 +148,8 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe return; } else if (model.getType() == NotificationType.RESPONDED_STORY) { final NavDirections action = NotificationsViewerFragmentDirections - .actionNotificationsViewerFragmentToStoryViewerFragment( - -1, null, false, false, model.getPostId(), model.getUsername(), false, true); + .actionNotificationsViewerFragmentToStoryViewerFragment(StoryViewerOptions.forStory(model.getPostId(), + model.getUsername())); NavHostFragment.findNavController(NotificationsViewerFragment.this).navigate(action); return; } diff --git a/app/src/main/java/awais/instagrabber/fragments/StoryListViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/StoryListViewerFragment.java index 95dbdce0..c712c058 100644 --- a/app/src/main/java/awais/instagrabber/fragments/StoryListViewerFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/StoryListViewerFragment.java @@ -32,6 +32,7 @@ import awais.instagrabber.databinding.FragmentStoryListViewerBinding; import awais.instagrabber.fragments.settings.MorePreferencesFragmentDirections; import awais.instagrabber.models.FeedStoryModel; import awais.instagrabber.models.HighlightModel; +import awais.instagrabber.repositories.requests.StoryViewerOptions; import awais.instagrabber.utils.TextUtils; import awais.instagrabber.viewmodels.ArchivesViewModel; import awais.instagrabber.viewmodels.FeedStoriesViewModel; @@ -58,7 +59,7 @@ public final class StoryListViewerFragment extends Fragment implements SwipeRefr public void onFeedStoryClick(final FeedStoryModel model, final int position) { if (model == null) return; final NavDirections action = StoryListViewerFragmentDirections - .actionStoryListFragmentToStoryViewerFragment(position, null, false, false, null, null, false, false); + .actionStoryListFragmentToStoryViewerFragment(StoryViewerOptions.forFeedStoryPosition(position)); NavHostFragment.findNavController(StoryListViewerFragment.this).navigate(action); } @@ -72,8 +73,8 @@ public final class StoryListViewerFragment extends Fragment implements SwipeRefr @Override public void onHighlightClick(final HighlightModel model, final int position) { if (model == null) return; - final NavDirections action = StoryListViewerFragmentDirections.actionStoryListFragmentToStoryViewerFragment( - position, getString(R.string.action_archive), false, false, null, null, true, false); + final NavDirections action = StoryListViewerFragmentDirections + .actionStoryListFragmentToStoryViewerFragment(StoryViewerOptions.forStoryArchive(position)); NavHostFragment.findNavController(StoryListViewerFragment.this).navigate(action); } @@ -188,6 +189,7 @@ public final class StoryListViewerFragment extends Fragment implements SwipeRefr @Override public void onSuccess(final List result) { feedStoriesViewModel.getList().postValue(result); + adapter.submitList(result); binding.swipeRefreshLayout.setRefreshing(false); } diff --git a/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java index 82ddb5f3..accf2b06 100644 --- a/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java @@ -55,6 +55,7 @@ import com.google.android.exoplayer2.source.MediaLoadData; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSourceEventListener; import com.google.android.exoplayer2.source.ProgressiveMediaSource; +import com.google.android.exoplayer2.source.dash.DashMediaSource; import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; import java.io.IOException; @@ -68,9 +69,9 @@ import java.util.List; import awais.instagrabber.BuildConfig; import awais.instagrabber.R; import awais.instagrabber.adapters.StoriesAdapter; +import awais.instagrabber.asyncs.CreateThreadAction; import awais.instagrabber.asyncs.PostFetcher; import awais.instagrabber.asyncs.SeenAction; -import awais.instagrabber.asyncs.direct_messages.CreateThreadAction; import awais.instagrabber.customviews.helpers.SwipeGestureListener; import awais.instagrabber.databinding.FragmentStoryViewerBinding; import awais.instagrabber.fragments.main.ProfileFragmentDirections; @@ -84,6 +85,8 @@ import awais.instagrabber.models.stickers.QuestionModel; import awais.instagrabber.models.stickers.QuizModel; import awais.instagrabber.models.stickers.SliderModel; import awais.instagrabber.models.stickers.SwipeUpModel; +import awais.instagrabber.repositories.requests.StoryViewerOptions; +import awais.instagrabber.repositories.requests.StoryViewerOptions.Type; import awais.instagrabber.repositories.requests.directmessages.BroadcastOptions; import awais.instagrabber.repositories.responses.StoryStickerResponse; import awais.instagrabber.repositories.responses.directmessages.DirectThreadBroadcastResponse; @@ -133,27 +136,32 @@ public class StoryViewerFragment extends Fragment { private MenuItem menuDownload; private MenuItem menuDm; private SimpleExoPlayer player; - private boolean isHashtag, isLoc; - private String highlight; + // private boolean isHashtag; + // private boolean isLoc; + // private String highlight; + private String actionBarTitle; private boolean fetching = false, sticking = false, shouldRefresh = true; private int currentFeedStoryIndex; private double sliderValue; private StoriesViewModel storiesViewModel; - private StoryViewerFragmentArgs fragmentArgs; private ViewModel viewModel; - private boolean isHighlight, isArchive, isNotification; + // private boolean isHighlight; + // private boolean isArchive; + // private boolean isNotification; private DirectMessagesService directMessagesService; private final String cookie = settingsHelper.getString(Constants.COOKIE); private final String csrfToken = CookieUtils.getCsrfTokenFromCookie(cookie); private final long userIdFromCookie = CookieUtils.getUserIdFromCookie(cookie); private final String deviceId = settingsHelper.getString(Constants.DEVICE_UUID); + private StoryViewerOptions options; @Override public void onCreate(@Nullable final Bundle savedInstanceState) { super.onCreate(savedInstanceState); fragmentActivity = (AppCompatActivity) requireActivity(); storiesService = StoriesService.getInstance(); + if (csrfToken == null) return; directMessagesService = DirectMessagesService.getInstance(csrfToken, userIdFromCookie, deviceId); setHasOptionsMenu(true); } @@ -256,6 +264,15 @@ public class StoryViewerFragment extends Fragment { releasePlayer(); } + @Override + public void onResume() { + super.onResume(); + final ActionBar actionBar = fragmentActivity.getSupportActionBar(); + if (actionBar != null) { + actionBar.setTitle(actionBarTitle); + } + } + @Override public void onDestroy() { releasePlayer(); @@ -269,21 +286,21 @@ public class StoryViewerFragment extends Fragment { private void init() { if (getArguments() == null) return; - fragmentArgs = StoryViewerFragmentArgs.fromBundle(getArguments()); - currentFeedStoryIndex = fragmentArgs.getFeedStoryIndex(); - highlight = fragmentArgs.getHighlight(); - isHighlight = !TextUtils.isEmpty(highlight); - isArchive = fragmentArgs.getIsArchive(); - isNotification = fragmentArgs.getIsNotification(); + final StoryViewerFragmentArgs fragmentArgs = StoryViewerFragmentArgs.fromBundle(getArguments()); + options = fragmentArgs.getOptions(); + currentFeedStoryIndex = options.getCurrentFeedStoryIndex(); + // highlight = fragmentArgs.getHighlight(); + // isHighlight = !TextUtils.isEmpty(highlight); + // isArchive = fragmentArgs.getIsArchive(); + // isNotification = fragmentArgs.getIsNotification(); + final Type type = options.getType(); if (currentFeedStoryIndex >= 0) { - viewModel = isHighlight - ? isArchive + viewModel = type == Type.HIGHLIGHT + ? type == Type.STORY_ARCHIVE ? new ViewModelProvider(fragmentActivity).get(ArchivesViewModel.class) : new ViewModelProvider(fragmentActivity).get(HighlightsViewModel.class) : new ViewModelProvider(fragmentActivity).get(FeedStoriesViewModel.class); } - // feedStoryModels = feedStoriesViewModel.getList().getValue(); - // feedStoryModels == null || feedStoryModels.isEmpty() || setupStories(); } @@ -308,21 +325,20 @@ public class StoryViewerFragment extends Fragment { final boolean hasFeedStories; List models = null; if (currentFeedStoryIndex >= 0) { - if (isArchive) { - final ArchivesViewModel archivesViewModel = (ArchivesViewModel) viewModel; - models = archivesViewModel.getList().getValue(); - } else if (isHighlight) { - final HighlightsViewModel highlightsViewModel = (HighlightsViewModel) viewModel; - models = highlightsViewModel.getList().getValue(); - // final HighlightModel model = models.get(currentFeedStoryIndex); - // currentStoryMediaId = model.getId(); - // currentStoryUsername = model.getTitle(); - } else { - final FeedStoriesViewModel feedStoriesViewModel = (FeedStoriesViewModel) viewModel; - models = feedStoriesViewModel.getList().getValue(); - // final FeedStoryModel model = models.get(currentFeedStoryIndex); - // currentStoryMediaId = model.getStoryMediaId(); - // currentStoryUsername = model.getProfileModel().getUsername(); + final Type type = options.getType(); + switch (type) { + case HIGHLIGHT: + final HighlightsViewModel highlightsViewModel = (HighlightsViewModel) viewModel; + models = highlightsViewModel.getList().getValue(); + break; + case FEED_STORY_POSITION: + final FeedStoriesViewModel feedStoriesViewModel = (FeedStoriesViewModel) viewModel; + models = feedStoriesViewModel.getList().getValue(); + break; + case STORY_ARCHIVE: + final ArchivesViewModel archivesViewModel = (ArchivesViewModel) viewModel; + models = archivesViewModel.getList().getValue(); + break; } } hasFeedStories = models != null && !models.isEmpty(); @@ -646,6 +662,7 @@ public class StoryViewerFragment extends Fragment { private void resetView() { final Context context = getContext(); + StoryModel live = null; slidePos = 0; lastSlidePos = 0; if (menuDownload != null) menuDownload.setVisible(false); @@ -653,58 +670,60 @@ public class StoryViewerFragment extends Fragment { binding.imageViewer.setController(null); releasePlayer(); String currentStoryMediaId = null; + final Type type = options.getType(); + StoryViewerOptions fetchOptions = null; if (currentFeedStoryIndex >= 0) { - if (isArchive) { - final ArchivesViewModel archivesViewModel = (ArchivesViewModel) viewModel; - final List models = archivesViewModel.getList().getValue(); - if (models == null || models.isEmpty() || currentFeedStoryIndex >= models.size()) { - Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); - return; + switch (type) { + case HIGHLIGHT: { + final HighlightsViewModel highlightsViewModel = (HighlightsViewModel) viewModel; + final List models = highlightsViewModel.getList().getValue(); + if (models == null || models.isEmpty() || currentFeedStoryIndex >= models.size()) { + Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); + return; + } + final HighlightModel model = models.get(currentFeedStoryIndex); + currentStoryMediaId = model.getId(); + fetchOptions = StoryViewerOptions.forHighlight(model.getId()); + currentStoryUsername = model.getTitle(); + break; } - final HighlightModel model = models.get(currentFeedStoryIndex); - currentStoryMediaId = model.getId(); - currentStoryUsername = model.getTitle(); - } else if (isHighlight) { - final HighlightsViewModel highlightsViewModel = (HighlightsViewModel) viewModel; - final List models = highlightsViewModel.getList().getValue(); - if (models == null || models.isEmpty() || currentFeedStoryIndex >= models.size()) { - Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); - return; + case FEED_STORY_POSITION: { + final FeedStoriesViewModel feedStoriesViewModel = (FeedStoriesViewModel) viewModel; + final List models = feedStoriesViewModel.getList().getValue(); + if (models == null) return; + final FeedStoryModel model = models.get(currentFeedStoryIndex); + currentStoryMediaId = model.getStoryMediaId(); + currentStoryUsername = model.getProfileModel().getUsername(); + fetchOptions = StoryViewerOptions.forUser(Long.parseLong(currentStoryMediaId), currentStoryUsername); + if (model.isLive()) { + live = model.getFirstStoryModel(); + } + break; + } + case STORY_ARCHIVE: { + final ArchivesViewModel archivesViewModel = (ArchivesViewModel) viewModel; + final List models = archivesViewModel.getList().getValue(); + if (models == null || models.isEmpty() || currentFeedStoryIndex >= models.size()) { + Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); + return; + } + final HighlightModel model = models.get(currentFeedStoryIndex); + currentStoryMediaId = model.getId(); + currentStoryUsername = model.getTitle(); + fetchOptions = StoryViewerOptions.forUser(Long.parseLong(currentStoryMediaId), currentStoryUsername); + break; } - final HighlightModel model = models.get(currentFeedStoryIndex); - currentStoryMediaId = model.getId(); - currentStoryUsername = model.getTitle(); - } else { - final FeedStoriesViewModel feedStoriesViewModel = (FeedStoriesViewModel) viewModel; - final List models = feedStoriesViewModel.getList().getValue(); - if (models == null) return; - final FeedStoryModel model = models.get(currentFeedStoryIndex); - currentStoryMediaId = model.getStoryMediaId(); - currentStoryUsername = model.getProfileModel().getUsername(); - } - } else if (fragmentArgs.getProfileId() > 0 && !TextUtils.isEmpty(fragmentArgs.getUsername())) { - currentStoryMediaId = String.valueOf(fragmentArgs.getProfileId()); - currentStoryUsername = fragmentArgs.getUsername(); - } - isHashtag = fragmentArgs.getIsHashtag(); - isLoc = fragmentArgs.getIsLoc(); - final boolean hasUsername = !TextUtils.isEmpty(currentStoryUsername); - if (isHighlight) { - final ActionBar actionBar = fragmentActivity.getSupportActionBar(); - if (actionBar != null) { - actionBar.setTitle(highlight); - } - } else if (hasUsername) { - currentStoryUsername = currentStoryUsername.replace("@", ""); - final ActionBar actionBar = fragmentActivity.getSupportActionBar(); - if (actionBar != null) { - actionBar.setTitle(currentStoryUsername); } } + if (type == Type.USER) { + currentStoryMediaId = String.valueOf(options.getId()); + currentStoryUsername = options.getName(); + fetchOptions = StoryViewerOptions.forUser(options.getId(), currentStoryUsername); + } + setTitle(type); storiesViewModel.getList().setValue(Collections.emptyList()); - if (currentStoryMediaId == null) return; - if (isNotification) { - storiesService.fetch(currentStoryMediaId, new ServiceCallback() { + if (type == Type.STORY) { + storiesService.fetch(options.getId(), new ServiceCallback() { @Override public void onSuccess(final StoryModel storyModel) { fetching = false; @@ -728,6 +747,7 @@ public class StoryViewerFragment extends Fragment { }); return; } + if (currentStoryMediaId == null) return; final ServiceCallback> storyCallback = new ServiceCallback>() { @Override public void onSuccess(final List storyModels) { @@ -739,8 +759,10 @@ public class StoryViewerFragment extends Fragment { return; } binding.storiesList.setVisibility((storyModels.size() == 1 && currentFeedStoryIndex == -1) ? View.GONE : View.VISIBLE); - binding.btnBackward.setVisibility(currentFeedStoryIndex == -1 ? View.GONE : View.VISIBLE); - binding.btnForward.setVisibility(currentFeedStoryIndex == -1 ? View.GONE : View.VISIBLE); + if (currentFeedStoryIndex == -1) { + binding.btnBackward.setVisibility(View.GONE); + binding.btnForward.setVisibility(View.GONE); + } storiesViewModel.getList().setValue(storyModels); currentStory = storyModels.get(0); refreshStory(); @@ -753,12 +775,29 @@ public class StoryViewerFragment extends Fragment { Log.e(TAG, "Error", t); } }; - storiesService.getUserStory(currentStoryMediaId, - currentStoryUsername, - isLoc, - isHashtag, - isHighlight, - storyCallback); + if (live != null) { + storyCallback.onSuccess(Collections.singletonList(live)); + return; + } + storiesService.getUserStory(fetchOptions, storyCallback); + } + + private void setTitle(final Type type) { + final boolean hasUsername = !TextUtils.isEmpty(currentStoryUsername); + if (type == Type.HIGHLIGHT) { + final ActionBar actionBar = fragmentActivity.getSupportActionBar(); + if (actionBar != null) { + actionBarTitle = options.getName(); + actionBar.setTitle(options.getName()); + } + } else if (hasUsername) { + currentStoryUsername = currentStoryUsername.replace("@", ""); + final ActionBar actionBar = fragmentActivity.getSupportActionBar(); + if (actionBar != null) { + actionBarTitle = currentStoryUsername; + actionBar.setTitle(currentStoryUsername); + } + } } private void refreshStory() { @@ -782,51 +821,56 @@ public class StoryViewerFragment extends Fragment { final MediaItemType itemType = currentStory.getItemType(); if (menuDownload != null) menuDownload.setVisible(false); - url = itemType == MediaItemType.MEDIA_TYPE_VIDEO ? currentStory.getVideoUrl() : currentStory.getStoryUrl(); + url = itemType == MediaItemType.MEDIA_TYPE_IMAGE ? currentStory.getStoryUrl() : currentStory.getVideoUrl(); - final String shortCode = currentStory.getTappableShortCode(); - binding.viewStoryPost.setVisibility(shortCode != null ? View.VISIBLE : View.GONE); - binding.viewStoryPost.setTag(shortCode); + if (itemType != MediaItemType.MEDIA_TYPE_LIVE) { + final String shortCode = currentStory.getTappableShortCode(); + binding.viewStoryPost.setVisibility(shortCode != null ? View.VISIBLE : View.GONE); + binding.viewStoryPost.setTag(shortCode); - final String spotify = currentStory.getSpotify(); - binding.spotify.setVisibility(spotify != null ? View.VISIBLE : View.GONE); - binding.spotify.setTag(spotify); + final String spotify = currentStory.getSpotify(); + binding.spotify.setVisibility(spotify != null ? View.VISIBLE : View.GONE); + binding.spotify.setTag(spotify); - poll = currentStory.getPoll(); - binding.poll.setVisibility(poll != null ? View.VISIBLE : View.GONE); - binding.poll.setTag(poll); + poll = currentStory.getPoll(); + binding.poll.setVisibility(poll != null ? View.VISIBLE : View.GONE); + binding.poll.setTag(poll); - question = currentStory.getQuestion(); - binding.answer.setVisibility((question != null && !TextUtils.isEmpty(cookie)) ? View.VISIBLE : View.GONE); - binding.answer.setTag(question); + question = currentStory.getQuestion(); + binding.answer.setVisibility((question != null && !TextUtils.isEmpty(cookie)) ? View.VISIBLE : View.GONE); + binding.answer.setTag(question); - mentions = currentStory.getMentions(); - binding.mention.setVisibility((mentions != null && mentions.length > 0) ? View.VISIBLE : View.GONE); - binding.mention.setTag(mentions); + mentions = currentStory.getMentions(); + binding.mention.setVisibility((mentions != null && mentions.length > 0) ? View.VISIBLE : View.GONE); + binding.mention.setTag(mentions); - quiz = currentStory.getQuiz(); - binding.quiz.setVisibility(quiz != null ? View.VISIBLE : View.GONE); - binding.quiz.setTag(quiz); + quiz = currentStory.getQuiz(); + binding.quiz.setVisibility(quiz != null ? View.VISIBLE : View.GONE); + binding.quiz.setTag(quiz); - slider = currentStory.getSlider(); - binding.slider.setVisibility(slider != null ? View.VISIBLE : View.GONE); - binding.slider.setTag(slider); + slider = currentStory.getSlider(); + binding.slider.setVisibility(slider != null ? View.VISIBLE : View.GONE); + binding.slider.setTag(slider); - final SwipeUpModel swipeUp = currentStory.getSwipeUp(); - if (swipeUp != null) { - binding.swipeUp.setVisibility(View.VISIBLE); - binding.swipeUp.setText(swipeUp.getText()); - binding.swipeUp.setTag(swipeUp.getUrl()); + final SwipeUpModel swipeUp = currentStory.getSwipeUp(); + if (swipeUp != null) { + binding.swipeUp.setVisibility(View.VISIBLE); + binding.swipeUp.setText(swipeUp.getText()); + binding.swipeUp.setTag(swipeUp.getUrl()); + } else binding.swipeUp.setVisibility(View.GONE); } releasePlayer(); - if (isHashtag || isLoc) { + final Type type = options.getType(); + if (type == Type.HASHTAG || type == Type.LOCATION) { final ActionBar actionBar = fragmentActivity.getSupportActionBar(); if (actionBar != null) { + actionBarTitle = currentStory.getUsername(); actionBar.setTitle(currentStory.getUsername()); } } if (itemType == MediaItemType.MEDIA_TYPE_VIDEO) setupVideo(); + else if (itemType == MediaItemType.MEDIA_TYPE_LIVE) setupLive(); else setupImage(); final ActionBar actionBar = fragmentActivity.getSupportActionBar(); @@ -952,6 +996,72 @@ public class StoryViewerFragment extends Fragment { }); } + private void setupLive() { + binding.playerView.setVisibility(View.VISIBLE); + binding.progressView.setVisibility(View.GONE); + binding.imageViewer.setVisibility(View.GONE); + binding.imageViewer.setController(null); + + if (menuDownload != null) menuDownload.setVisible(false); + if (menuDm != null) menuDm.setVisible(false); + + final Context context = getContext(); + if (context == null) return; + player = new SimpleExoPlayer.Builder(context).build(); + binding.playerView.setPlayer(player); + player.setPlayWhenReady(settingsHelper.getBoolean(Constants.AUTOPLAY_VIDEOS)); + + final Uri uri = Uri.parse(url); + final MediaItem mediaItem = MediaItem.fromUri(uri); + final DashMediaSource mediaSource = new DashMediaSource.Factory(new DefaultDataSourceFactory(context, "instagram")) + .createMediaSource(mediaItem); + mediaSource.addEventListener(new Handler(), new MediaSourceEventListener() { + @Override + public void onLoadCompleted(final int windowIndex, + @Nullable final MediaSource.MediaPeriodId mediaPeriodId, + @NonNull final LoadEventInfo loadEventInfo, + @NonNull final MediaLoadData mediaLoadData) { + binding.progressView.setVisibility(View.GONE); + } + + @Override + public void onLoadStarted(final int windowIndex, + @Nullable final MediaSource.MediaPeriodId mediaPeriodId, + @NonNull final LoadEventInfo loadEventInfo, + @NonNull final MediaLoadData mediaLoadData) { + binding.progressView.setVisibility(View.VISIBLE); + } + + @Override + public void onLoadCanceled(final int windowIndex, + @Nullable final MediaSource.MediaPeriodId mediaPeriodId, + @NonNull final LoadEventInfo loadEventInfo, + @NonNull final MediaLoadData mediaLoadData) { + binding.progressView.setVisibility(View.GONE); + } + + @Override + public void onLoadError(final int windowIndex, + @Nullable final MediaSource.MediaPeriodId mediaPeriodId, + @NonNull final LoadEventInfo loadEventInfo, + @NonNull final MediaLoadData mediaLoadData, + @NonNull final IOException error, + final boolean wasCanceled) { + binding.progressView.setVisibility(View.GONE); + } + }); + player.setMediaSource(mediaSource); + player.prepare(); + + binding.playerView.setOnClickListener(v -> { + if (player != null) { + if (player.getPlaybackState() == Player.STATE_ENDED) player.seekTo(0); + player.setPlayWhenReady(player.getPlaybackState() == Player.STATE_ENDED || !player.isPlaying()); + } + }); + } + + private void openProfile(final String username) { final ActionBar actionBar = fragmentActivity.getSupportActionBar(); if (actionBar != null) { diff --git a/app/src/main/java/awais/instagrabber/fragments/TopicPostsFragment.java b/app/src/main/java/awais/instagrabber/fragments/TopicPostsFragment.java index 011ed776..9b680d64 100644 --- a/app/src/main/java/awais/instagrabber/fragments/TopicPostsFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/TopicPostsFragment.java @@ -51,7 +51,7 @@ import awais.instagrabber.databinding.FragmentTopicPostsBinding; import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment; import awais.instagrabber.fragments.main.DiscoverFragmentDirections; import awais.instagrabber.models.PostsLayoutPreferences; -import awais.instagrabber.models.TopicCluster; +import awais.instagrabber.repositories.responses.discover.TopicCluster; import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.DownloadUtils; diff --git a/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java b/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java index 07e1661a..8f68b927 100644 --- a/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java @@ -253,7 +253,7 @@ public class DirectMessageThreadFragment extends Fragment { public boolean onOptionsItemSelected(@NonNull final MenuItem item) { final int itemId = item.getItemId(); if (itemId == R.id.info) { - final NavDirections action = DirectMessageThreadFragmentDirections.actionDMThreadFragmentToDMSettingsFragment(viewModel.getThreadId()); + final NavDirections action = DirectMessageThreadFragmentDirections.actionDMThreadFragmentToDMSettingsFragment(viewModel.getThreadId(), null); NavHostFragment.findNavController(this).navigate(action); return true; } 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 6460a69b..2ad2d831 100644 --- a/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java @@ -78,6 +78,21 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre private RecyclerView storiesRecyclerView; private MenuItem storyListMenu; + private final FeedStoriesAdapter feedStoriesAdapter = new FeedStoriesAdapter( + new FeedStoriesAdapter.OnFeedStoryClickListener() { + @Override + public void onFeedStoryClick(FeedStoryModel model, int position) { + final NavDirections action = FeedFragmentDirections.actionFeedFragmentToStoryViewerFragment(position, null, false, false, null, null, false, false); + NavHostFragment.findNavController(FeedFragment.this).navigate(action); + } + + @Override + public void onFeedStoryLongClick(FeedStoryModel model, int position) { + navigateToProfile("@" + model.getProfileModel().getUsername()); + } + } + ); + private final FeedAdapterV2.FeedItemCallback feedItemCallback = new FeedAdapterV2.FeedItemCallback() { @Override public void onPostClick(final Media feedModel, final View profilePicView, final View mainPostImage) { @@ -351,21 +366,6 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre private void setupFeedStories() { if (storyListMenu != null) storyListMenu.setVisible(false); feedStoriesViewModel = new ViewModelProvider(fragmentActivity).get(FeedStoriesViewModel.class); - final FeedStoriesAdapter feedStoriesAdapter = new FeedStoriesAdapter( - new FeedStoriesAdapter.OnFeedStoryClickListener() { - @Override - public void onFeedStoryClick(FeedStoryModel model, int position) { - final NavDirections action = FeedFragmentDirections - .actionFeedFragmentToStoryViewerFragment(position, null, false, false, null, null, false, false); - NavHostFragment.findNavController(FeedFragment.this).navigate(action); - } - - @Override - public void onFeedStoryLongClick(FeedStoryModel model, int position) { - navigateToProfile("@" + model.getProfileModel().getUsername()); - } - } - ); final Context context = getContext(); if (context == null) return; storiesRecyclerView = new RecyclerView(context); @@ -389,6 +389,7 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre @Override public void onSuccess(final List result) { feedStoriesViewModel.getList().postValue(result); + feedStoriesAdapter.submitList(result); storiesFetching = false; if (storyListMenu != null) storyListMenu.setVisible(true); updateSwipeRefreshState(); diff --git a/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java b/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java index feb6d127..4dff529a 100644 --- a/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java @@ -874,7 +874,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe @Override public void onSuccess(final List storyModels) { if (storyModels != null && !storyModels.isEmpty()) { - profileDetailsBinding.mainProfileImage.setStoriesBorder(); + profileDetailsBinding.mainProfileImage.setStoriesBorder(1); hasStories = true; } } diff --git a/app/src/main/java/awais/instagrabber/models/FeedStoryModel.java b/app/src/main/java/awais/instagrabber/models/FeedStoryModel.java index 026c019c..7aa21239 100755 --- a/app/src/main/java/awais/instagrabber/models/FeedStoryModel.java +++ b/app/src/main/java/awais/instagrabber/models/FeedStoryModel.java @@ -13,17 +13,21 @@ public final class FeedStoryModel implements Serializable { private final User profileModel; private final StoryModel firstStoryModel; private Boolean fullyRead; + private final boolean isLive, isBestie; private final long timestamp; private final int mediaCount; public FeedStoryModel(final String storyMediaId, final User profileModel, final boolean fullyRead, - final long timestamp, final StoryModel firstStoryModel, final int mediaCount) { + final long timestamp, final StoryModel firstStoryModel, final int mediaCount, + final boolean isLive, final boolean isBestie) { this.storyMediaId = storyMediaId; this.profileModel = profileModel; this.fullyRead = fullyRead; this.timestamp = timestamp; this.firstStoryModel = firstStoryModel; this.mediaCount = mediaCount; + this.isLive = isLive; + this.isBestie = isBestie; } public String getStoryMediaId() { @@ -62,4 +66,12 @@ public final class FeedStoryModel implements Serializable { public void setFullyRead(final boolean fullyRead) { this.fullyRead = fullyRead; } + + public boolean isLive() { + return isLive; + } + + public boolean isBestie() { + return isBestie; + } } \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/models/LocationModel.java b/app/src/main/java/awais/instagrabber/models/LocationModel.java index b237d4e4..e0e13730 100755 --- a/app/src/main/java/awais/instagrabber/models/LocationModel.java +++ b/app/src/main/java/awais/instagrabber/models/LocationModel.java @@ -4,8 +4,7 @@ import java.io.Serializable; public final class LocationModel implements Serializable { private final long postCount; - private final String id; - private final String slug; + private final long id; private final String name; private final String bio; private final String url; @@ -13,8 +12,7 @@ public final class LocationModel implements Serializable { private final String lat; private final String lng; - public LocationModel(final String id, - final String slug, + public LocationModel(final long id, final String name, final String bio, final String url, @@ -23,7 +21,6 @@ public final class LocationModel implements Serializable { final String lat, final String lng) { this.id = id; - this.slug = slug; this.name = name; this.bio = bio; this.url = url; @@ -33,14 +30,10 @@ public final class LocationModel implements Serializable { this.lng = lng; } - public String getId() { + public long getId() { return id; } - public String getSlug() { - return slug; - } - public String getName() { return name; } diff --git a/app/src/main/java/awais/instagrabber/models/NotificationModel.java b/app/src/main/java/awais/instagrabber/models/NotificationModel.java index fde8c79e..4ca34b2d 100755 --- a/app/src/main/java/awais/instagrabber/models/NotificationModel.java +++ b/app/src/main/java/awais/instagrabber/models/NotificationModel.java @@ -5,7 +5,6 @@ import androidx.annotation.NonNull; import java.util.Date; import awais.instagrabber.models.enums.NotificationType; -import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.Utils; public final class NotificationModel { @@ -13,7 +12,7 @@ public final class NotificationModel { private final long userId; private final String username; private final String profilePicUrl; - private final String postId; + private final long postId; private final String previewUrl; private final NotificationType type; private final CharSequence text; @@ -25,11 +24,11 @@ public final class NotificationModel { final long userId, final String username, final String profilePicUrl, - final String postId, + final long postId, final String previewUrl, final NotificationType type) { this.id = id; - this.text = TextUtils.hasMentions(text) ? TextUtils.getMentionText(text) : text; + this.text = text; this.timestamp = timestamp; this.userId = userId; this.username = username; @@ -68,7 +67,7 @@ public final class NotificationModel { return profilePicUrl; } - public String getPostId() { + public long getPostId() { return postId; } diff --git a/app/src/main/java/awais/instagrabber/models/enums/MediaItemType.java b/app/src/main/java/awais/instagrabber/models/enums/MediaItemType.java index 93c6f60e..616ae58f 100755 --- a/app/src/main/java/awais/instagrabber/models/enums/MediaItemType.java +++ b/app/src/main/java/awais/instagrabber/models/enums/MediaItemType.java @@ -14,7 +14,10 @@ public enum MediaItemType implements Serializable { @SerializedName("8") MEDIA_TYPE_SLIDER(8), @SerializedName("11") - MEDIA_TYPE_VOICE(11); + MEDIA_TYPE_VOICE(11), + // 5 is arbitrary + @SerializedName("5") + MEDIA_TYPE_LIVE(5); private final int id; private static final Map map = new HashMap<>(); diff --git a/app/src/main/java/awais/instagrabber/repositories/LocationRepository.java b/app/src/main/java/awais/instagrabber/repositories/LocationRepository.java index e36c9a21..df999f0b 100644 --- a/app/src/main/java/awais/instagrabber/repositories/LocationRepository.java +++ b/app/src/main/java/awais/instagrabber/repositories/LocationRepository.java @@ -11,6 +11,6 @@ import retrofit2.http.QueryMap; public interface LocationRepository { @GET("/api/v1/feed/location/{location}/") - Call fetchPosts(@Path("location") final String locationId, + Call fetchPosts(@Path("location") final long locationId, @QueryMap Map queryParams); } diff --git a/app/src/main/java/awais/instagrabber/repositories/MediaRepository.java b/app/src/main/java/awais/instagrabber/repositories/MediaRepository.java index 1eed5398..ea7d97bf 100644 --- a/app/src/main/java/awais/instagrabber/repositories/MediaRepository.java +++ b/app/src/main/java/awais/instagrabber/repositories/MediaRepository.java @@ -15,7 +15,7 @@ import retrofit2.http.QueryMap; public interface MediaRepository { @GET("/api/v1/media/{mediaId}/info/") - Call fetch(@Path("mediaId") final String mediaId); + Call fetch(@Path("mediaId") final long mediaId); @GET("/api/v1/media/{mediaId}/{action}/") Call fetchLikes(@Path("mediaId") final String mediaId, diff --git a/app/src/main/java/awais/instagrabber/repositories/StoriesRepository.java b/app/src/main/java/awais/instagrabber/repositories/StoriesRepository.java index 7bdf31a1..17af1b86 100644 --- a/app/src/main/java/awais/instagrabber/repositories/StoriesRepository.java +++ b/app/src/main/java/awais/instagrabber/repositories/StoriesRepository.java @@ -15,7 +15,7 @@ import retrofit2.http.Url; public interface StoriesRepository { @GET("/api/v1/media/{mediaId}/info/") - Call fetch(@Path("mediaId") final String mediaId); + Call fetch(@Path("mediaId") final long mediaId); // this one is the same as MediaRepository.fetch BUT you need to make sure it's a story @GET("/api/v1/feed/reels_tray/") diff --git a/app/src/main/java/awais/instagrabber/repositories/requests/StoryViewerOptions.java b/app/src/main/java/awais/instagrabber/repositories/requests/StoryViewerOptions.java new file mode 100644 index 00000000..2981d4ec --- /dev/null +++ b/app/src/main/java/awais/instagrabber/repositories/requests/StoryViewerOptions.java @@ -0,0 +1,93 @@ +package awais.instagrabber.repositories.requests; + +import java.io.Serializable; + +public class StoryViewerOptions implements Serializable { + private final long id; + private final String name; + private final Type type; + private int currentFeedStoryIndex; + + private StoryViewerOptions(final int position, final Type type) { + id = 0; + name = null; + this.currentFeedStoryIndex = position; + this.type = type; + } + + private StoryViewerOptions(final String name, final Type type) { + this.name = name; + this.id = 0; + this.type = type; + } + + private StoryViewerOptions(final long id, final Type type) { + this.name = null; + this.id = id; + this.type = type; + } + + private StoryViewerOptions(final long id, final String name, final Type type) { + this.id = id; + this.name = name; + this.type = type; + } + + public static StoryViewerOptions forHashtag(final String name) { + return new StoryViewerOptions(name, Type.HASHTAG); + } + + public static StoryViewerOptions forLocation(final long id, final String name) { + return new StoryViewerOptions(id, name, Type.LOCATION); + } + + public static StoryViewerOptions forUser(final long id, final String name) { + return new StoryViewerOptions(id, name,Type.USER); + } + + public static StoryViewerOptions forHighlight(final String highlight) { + return new StoryViewerOptions(highlight, Type.HIGHLIGHT); + } + + public static StoryViewerOptions forStory(final long mediaId, final String username) { + return new StoryViewerOptions(mediaId, username, Type.STORY); + } + + public static StoryViewerOptions forFeedStoryPosition(final int position) { + return new StoryViewerOptions(position, Type.FEED_STORY_POSITION); + } + + public static StoryViewerOptions forStoryArchive(final int position) { + return new StoryViewerOptions(position, Type.STORY_ARCHIVE); + } + + public long getId() { + return id; + } + + public String getName() { + return name; + } + + public Type getType() { + return type; + } + + public int getCurrentFeedStoryIndex() { + return currentFeedStoryIndex; + } + + public void setCurrentFeedStoryIndex(final int index) { + this.currentFeedStoryIndex = index; + } + + public enum Type { + HASHTAG, + LOCATION, + USER, + HIGHLIGHT, + STORY, + FEED_STORY_POSITION, + STORY_ARCHIVE + } +} diff --git a/app/src/main/java/awais/instagrabber/models/TopicCluster.java b/app/src/main/java/awais/instagrabber/repositories/responses/discover/TopicCluster.java similarity index 95% rename from app/src/main/java/awais/instagrabber/models/TopicCluster.java rename to app/src/main/java/awais/instagrabber/repositories/responses/discover/TopicCluster.java index 21b3ca98..610a4835 100644 --- a/app/src/main/java/awais/instagrabber/models/TopicCluster.java +++ b/app/src/main/java/awais/instagrabber/repositories/responses/discover/TopicCluster.java @@ -1,4 +1,4 @@ -package awais.instagrabber.models; +package awais.instagrabber.repositories.responses.discover; import java.io.Serializable; diff --git a/app/src/main/java/awais/instagrabber/repositories/responses/discover/TopicalExploreFeedResponse.java b/app/src/main/java/awais/instagrabber/repositories/responses/discover/TopicalExploreFeedResponse.java index ceea714a..26aa9aa9 100644 --- a/app/src/main/java/awais/instagrabber/repositories/responses/discover/TopicalExploreFeedResponse.java +++ b/app/src/main/java/awais/instagrabber/repositories/responses/discover/TopicalExploreFeedResponse.java @@ -2,8 +2,6 @@ package awais.instagrabber.repositories.responses.discover; import java.util.List; -import awais.instagrabber.models.TopicCluster; - public class TopicalExploreFeedResponse { private final boolean moreAvailable; private final String nextMaxId; diff --git a/app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java b/app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java index c3fd2ff3..63f6fd63 100644 --- a/app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java +++ b/app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java @@ -1,5 +1,6 @@ package awais.instagrabber.utils; +import android.net.Uri; import android.util.Log; import android.util.Pair; @@ -964,7 +965,7 @@ public final class ResponseBodyUtils { public static StoryModel parseStoryItem(final JSONObject data, final boolean isLoc, final boolean isHashtag, - final String localUsername) throws JSONException { + final String username) throws JSONException { final boolean isVideo = data.has("video_duration"); final StoryModel model = new StoryModel(data.getString("id"), data.getJSONObject("image_versions2").getJSONArray("candidates").getJSONObject(0) @@ -973,7 +974,7 @@ public final class ResponseBodyUtils { data.optLong("taken_at", 0), (isLoc || isHashtag) ? data.getJSONObject("user").getString("username") - : localUsername, + : username, data.getJSONObject("user").getLong("pk"), data.optBoolean("can_reply")); @@ -1041,10 +1042,15 @@ public final class ResponseBodyUtils { } if (data.has("story_cta") && data.has("link_text")) { JSONObject tappableObject = data.getJSONArray("story_cta").getJSONObject(0).getJSONArray("links").getJSONObject(0); - String swipeUpUrl = tappableObject.getString("webUri"); - if (swipeUpUrl.startsWith("http")) { - model.setSwipeUp(new SwipeUpModel(swipeUpUrl, data.getString("link_text"))); + String swipeUpUrl = tappableObject.optString("webUri"); + final String backupSwipeUpUrl = swipeUpUrl; + if (swipeUpUrl != null && swipeUpUrl.startsWith("https://l.instagram.com/")) { + swipeUpUrl = Uri.parse(swipeUpUrl).getQueryParameter("u"); } + if (swipeUpUrl != null && swipeUpUrl.startsWith("http")) + model.setSwipeUp(new SwipeUpModel(swipeUpUrl, data.getString("link_text"))); + else if (backupSwipeUpUrl != null && backupSwipeUpUrl.startsWith("http")) + model.setSwipeUp(new SwipeUpModel(backupSwipeUpUrl, data.getString("link_text"))); } if (data.has("story_sliders")) { final JSONObject tappableObject = data.getJSONArray("story_sliders").getJSONObject(0) @@ -1137,4 +1143,17 @@ public final class ResponseBodyUtils { } return read; } + + public static StoryModel parseBroadcastItem(final JSONObject data) throws JSONException { + final StoryModel model = new StoryModel(data.getString("id"), + data.getString("cover_frame_url"), + data.getString("cover_frame_url"), + MediaItemType.MEDIA_TYPE_LIVE, + data.optLong("published_time", 0), + data.getJSONObject("user").getString("username"), + data.getJSONObject("user").getLong("pk"), + false); + model.setVideoUrl(data.getString("dash_playback_url")); + return model; + } } diff --git a/app/src/main/java/awais/instagrabber/utils/TextUtils.java b/app/src/main/java/awais/instagrabber/utils/TextUtils.java index 65fb2447..95e82599 100644 --- a/app/src/main/java/awais/instagrabber/utils/TextUtils.java +++ b/app/src/main/java/awais/instagrabber/utils/TextUtils.java @@ -24,56 +24,6 @@ import java.util.regex.Pattern; import awais.instagrabber.customviews.CommentMentionClickSpan; public final class TextUtils { - private static final Pattern URL_PATTERN = Pattern.compile( - "(^|[\\s.:;?\\-\\]<\\(])((https?://|www\\.|pic\\.)[-\\w;/?:@&=+$\\|\\_.!~*\\|'()\\[\\]%#,☺]+[\\w/#](\\(\\))?)(?=$|[\\s',\\|\\(\\).:;?\\-\\[\\]>\\)])"); - - @NonNull - public static CharSequence getMentionText(@NonNull final CharSequence text) { - final int commentLength = text.length(); - final SpannableStringBuilder stringBuilder = new SpannableStringBuilder(text, 0, commentLength); - - for (int i = 0; i < commentLength; ++i) { - char currChar = text.charAt(i); - - if (currChar == '@' || currChar == '#') { - final int startLen = i; - - do { - if (++i == commentLength) break; - currChar = text.charAt(i); - - if (currChar == '.' && i + 1 < commentLength) { - final char nextChar = text.charAt(i + 1); - if (nextChar == '.' || nextChar == ' ' || nextChar == '#' || nextChar == '@' || nextChar == '/' - || nextChar == '\r' || nextChar == '\n') { - break; - } - } else if (currChar == '.') - break; - - // for merged hashtags - if (currChar == '#') { - --i; - break; - } - } while (currChar != ' ' && currChar != '\r' && currChar != '\n' && currChar != '>' && currChar != '<' - && currChar != ':' && currChar != ';' && currChar != '\'' && currChar != '"' && currChar != '[' - && currChar != ']' && currChar != '\\' && currChar != '=' && currChar != '-' && currChar != '!' - && currChar != '$' && currChar != '%' && currChar != '^' && currChar != '&' && currChar != '*' - && currChar != '(' && currChar != ')' && currChar != '{' && currChar != '}' && currChar != '/' - && currChar != '|' && currChar != '?' && currChar != '`' && currChar != '~' - ); - - final int endLen = currChar != '#' ? i : i + 1; // for merged hashtags - stringBuilder.setSpan(new CommentMentionClickSpan(), startLen, - Math.min(commentLength, endLen), // fixed - crash when end index is greater than comment length ( @kernoeb ) - Spanned.SPAN_EXCLUSIVE_INCLUSIVE); - } - } - - return stringBuilder; - } - // extracted from String class public static int indexOfChar(@NonNull final CharSequence sequence, final int ch, final int startIndex) { final int max = sequence.length(); @@ -90,11 +40,6 @@ public final class TextUtils { return -1; } - public static boolean hasMentions(final CharSequence text) { - if (isEmpty(text)) return false; - return indexOfChar(text, '@', 0) != -1 || indexOfChar(text, '#', 0) != -1; - } - public static CharSequence getSpannableUrl(final String url) { if (isEmpty(url)) return url; final int httpIndex = url.indexOf("http:"); diff --git a/app/src/main/java/awais/instagrabber/utils/Utils.java b/app/src/main/java/awais/instagrabber/utils/Utils.java index 528bac59..e8850927 100644 --- a/app/src/main/java/awais/instagrabber/utils/Utils.java +++ b/app/src/main/java/awais/instagrabber/utils/Utils.java @@ -140,11 +140,6 @@ public final class Utils { return mimeType.toLowerCase(); } - public static void errorFinish(@NonNull final Activity activity) { - Toast.makeText(activity, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); - activity.finish(); - } - public static SimpleCache getSimpleCacheInstance(final Context context) { if (context == null) { return null; diff --git a/app/src/main/java/awais/instagrabber/viewmodels/TopicClusterViewModel.java b/app/src/main/java/awais/instagrabber/viewmodels/TopicClusterViewModel.java index 3e8a169f..74ebc7cc 100644 --- a/app/src/main/java/awais/instagrabber/viewmodels/TopicClusterViewModel.java +++ b/app/src/main/java/awais/instagrabber/viewmodels/TopicClusterViewModel.java @@ -5,7 +5,7 @@ import androidx.lifecycle.ViewModel; import java.util.List; -import awais.instagrabber.models.TopicCluster; +import awais.instagrabber.repositories.responses.discover.TopicCluster; public class TopicClusterViewModel extends ViewModel { private MutableLiveData> list; diff --git a/app/src/main/java/awais/instagrabber/webservices/GraphQLService.java b/app/src/main/java/awais/instagrabber/webservices/GraphQLService.java index d7421e53..06a7af8c 100644 --- a/app/src/main/java/awais/instagrabber/webservices/GraphQLService.java +++ b/app/src/main/java/awais/instagrabber/webservices/GraphQLService.java @@ -85,7 +85,7 @@ public class GraphQLService extends BaseService { }); } - public void fetchLocationPosts(@NonNull final String locationId, + public void fetchLocationPosts(final long locationId, final String maxId, final ServiceCallback callback) { fetch("36bd0f2bf5911908de389b8ceaa3be6d", diff --git a/app/src/main/java/awais/instagrabber/webservices/LocationService.java b/app/src/main/java/awais/instagrabber/webservices/LocationService.java index d932d19a..381ba440 100644 --- a/app/src/main/java/awais/instagrabber/webservices/LocationService.java +++ b/app/src/main/java/awais/instagrabber/webservices/LocationService.java @@ -34,7 +34,7 @@ public class LocationService extends BaseService { return instance; } - public void fetchPosts(@NonNull final String locationId, + public void fetchPosts(final long locationId, final String maxId, final ServiceCallback callback) { final ImmutableMap.Builder builder = ImmutableMap.builder(); diff --git a/app/src/main/java/awais/instagrabber/webservices/MediaService.java b/app/src/main/java/awais/instagrabber/webservices/MediaService.java index 88767b45..81354535 100644 --- a/app/src/main/java/awais/instagrabber/webservices/MediaService.java +++ b/app/src/main/java/awais/instagrabber/webservices/MediaService.java @@ -51,7 +51,7 @@ public class MediaService extends BaseService { return instance; } - public void fetch(final String mediaId, + public void fetch(final long mediaId, final ServiceCallback callback) { final Call request = repository.fetch(mediaId); request.enqueue(new Callback() { diff --git a/app/src/main/java/awais/instagrabber/webservices/NewsService.java b/app/src/main/java/awais/instagrabber/webservices/NewsService.java index 6000fd95..945b1f17 100644 --- a/app/src/main/java/awais/instagrabber/webservices/NewsService.java +++ b/app/src/main/java/awais/instagrabber/webservices/NewsService.java @@ -62,7 +62,7 @@ public class NewsService extends BaseService { try { final JSONObject jsonObject = new JSONObject(body); final JSONArray oldStories = jsonObject.getJSONArray("old_stories"), - newStories = jsonObject.getJSONArray("new_stories"); + newStories = jsonObject.getJSONArray("new_stories"); for (int j = 0; j < newStories.length(); ++j) { final NotificationModel newsItem = parseNewsItem(newStories.getJSONObject(j)); @@ -105,7 +105,7 @@ public class NewsService extends BaseService { .getJSONObject("graphql") .getJSONObject("user"); final JSONObject ewaf = page.getJSONObject("activity_feed") - .optJSONObject("edge_web_activity_feed"); + .optJSONObject("edge_web_activity_feed"); final JSONObject efr = page.optJSONObject("edge_follow_requests"); JSONObject data; JSONArray media; @@ -127,8 +127,9 @@ public class NewsService extends BaseService { user.getLong("id"), user.getString("username"), user.getString("profile_pic_url"), - !data.isNull("media") ? data.getJSONObject("media").getString("id") : null, - !data.isNull("media") ? data.getJSONObject("media").getString("thumbnail_src") : null, notificationType)); + data.has("media") ? data.getJSONObject("media").getLong("id") : 0, + data.has("media") ? data.getJSONObject("media").getString("thumbnail_src") : null, + notificationType)); } } @@ -146,7 +147,7 @@ public class NewsService extends BaseService { data.getLong(Constants.EXTRAS_ID), data.getString("username"), data.getString("profile_pic_url"), - null, + 0, null, NotificationType.REQUEST)); } } @@ -169,7 +170,7 @@ public class NewsService extends BaseService { final String type = itemJson.getString("story_type"); final NotificationType notificationType = NotificationType.valueOfType(type); if (notificationType == null) { - if (BuildConfig.DEBUG) Log.d("austin_debug", "unhandled news type: "+itemJson); + if (BuildConfig.DEBUG) Log.d("austin_debug", "unhandled news type: " + itemJson); return null; } final JSONObject data = itemJson.getJSONObject("args"); @@ -180,7 +181,7 @@ public class NewsService extends BaseService { data.getLong("profile_id"), data.getString("profile_name"), data.getString("profile_image"), - !data.isNull("media") ? data.getJSONArray("media").getJSONObject(0).getString("id") : null, + !data.isNull("media") ? data.getJSONArray("media").getJSONObject(0).getLong("id") : 0, !data.isNull("media") ? data.getJSONArray("media").getJSONObject(0).getString("image") : null, notificationType); } @@ -254,8 +255,8 @@ public class NewsService extends BaseService { data.getLong("pk"), data.getString("username"), data.getString("profile_pic_url"), + 0, data.getString("full_name"), // just borrowing this field - null, NotificationType.AYML); } } diff --git a/app/src/main/java/awais/instagrabber/webservices/StoriesService.java b/app/src/main/java/awais/instagrabber/webservices/StoriesService.java index 64835a47..e6c4e39f 100644 --- a/app/src/main/java/awais/instagrabber/webservices/StoriesService.java +++ b/app/src/main/java/awais/instagrabber/webservices/StoriesService.java @@ -3,6 +3,7 @@ package awais.instagrabber.webservices; import android.util.Log; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import org.json.JSONArray; import org.json.JSONException; @@ -20,6 +21,7 @@ import awais.instagrabber.models.FeedStoryModel; import awais.instagrabber.models.HighlightModel; import awais.instagrabber.models.StoryModel; import awais.instagrabber.repositories.StoriesRepository; +import awais.instagrabber.repositories.requests.StoryViewerOptions; import awais.instagrabber.repositories.responses.FriendshipStatus; import awais.instagrabber.repositories.responses.StoryStickerResponse; import awais.instagrabber.repositories.responses.User; @@ -34,7 +36,6 @@ import retrofit2.Retrofit; public class StoriesService extends BaseService { private static final String TAG = "StoriesService"; - private static final boolean loadFromMock = false; private final StoriesRepository repository; @@ -54,7 +55,7 @@ public class StoriesService extends BaseService { return instance; } - public void fetch(final String mediaId, + public void fetch(final long mediaId, final ServiceCallback callback) { final Call request = repository.fetch(mediaId); request.enqueue(new Callback() { @@ -156,12 +157,66 @@ public class StoriesService extends BaseService { 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").getJSONObject(0) : null; + 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, false, null); } - feedStoryModels.add(new FeedStoryModel(id, user, fullyRead, timestamp, firstStoryModel, mediaCount)); + feedStoryModels.add(new FeedStoryModel(id, user, fullyRead, timestamp, firstStoryModel, mediaCount, false, isBestie)); + } + final JSONArray broadcasts = new JSONObject(body).getJSONArray("broadcasts"); + for (int i = 0; i < broadcasts.length(); ++i) { + final JSONObject node = broadcasts.getJSONObject(i); + final JSONObject userJson = node.getJSONObject("broadcast_owner"); + // final ProfileModel profileModel = new ProfileModel(false, false, false, + // userJson.getString("pk"), + // userJson.getString("username"), + // null, null, null, + // userJson.getString("profile_pic_url"), + // null, 0, 0, 0, false, false, false, false, false); + final User user = new User(userJson.getLong("pk"), + userJson.getString("username"), + userJson.optString("full_name"), + userJson.optBoolean("is_private"), + userJson.getString("profile_pic_url"), + null, + new FriendshipStatus( + false, + false, + false, + false, + false, + false, + false, + false, + false, + false + ), + userJson.optBoolean("is_verified"), + false, + false, + false, + false, + null, + null, + 0, + 0, + 0, + 0, + null, + null, + 0, + 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; + StoryModel firstStoryModel = null; + if (itemJson != null) { + firstStoryModel = ResponseBodyUtils.parseBroadcastItem(itemJson); + } + feedStoryModels.add(new FeedStoryModel(id, user, false, timestamp, firstStoryModel, 1, true, false)); } callback.onSuccess(sort(feedStoryModels)); } catch (JSONException e) { @@ -274,20 +329,17 @@ public class StoriesService extends BaseService { }); } - public void getUserStory(final String id, - final String username, - final boolean isLoc, - final boolean isHashtag, - final boolean highlight, + public void getUserStory(final StoryViewerOptions options, final ServiceCallback> callback) { - final String url = buildUrl(id, isLoc, isHashtag, highlight); - Log.d("austin_debug", url); + final String url = buildUrl(options); final Call userStoryCall = repository.getUserStory(Constants.I_USER_AGENT, url); + final boolean isLoc = options.getType() == StoryViewerOptions.Type.LOCATION; + final boolean isHashtag = options.getType() == StoryViewerOptions.Type.HASHTAG; + final boolean isHighlight = options.getType() == StoryViewerOptions.Type.HIGHLIGHT; userStoryCall.enqueue(new Callback() { @Override public void onResponse(@NonNull final Call call, @NonNull final Response response) { JSONObject data; - String localUsername = username; try { final String body = response.body(); if (body == null) { @@ -296,15 +348,19 @@ public class StoriesService extends BaseService { } data = new JSONObject(body); - if (!highlight) + if (!isHighlight) { data = data.optJSONObject((isLoc || isHashtag) ? "story" : "reel"); - else if (highlight) data = data.getJSONObject("reels").optJSONObject(id); + } else if (isHighlight) { + data = data.getJSONObject("reels").optJSONObject(options.getName()); + } + String username = null; if (data != null - && localUsername == null + // && localUsername == null && !isLoc - && !isHashtag) - localUsername = data.getJSONObject("user").getString("username"); + && !isHashtag) { + username = data.getJSONObject("user").getString("username"); + } JSONArray media; if (data != null @@ -315,7 +371,7 @@ public class StoriesService extends BaseService { final List models = new ArrayList<>(); for (int i = 0; i < mediaLen; ++i) { data = media.getJSONObject(i); - models.add(ResponseBodyUtils.parseStoryItem(data, isLoc, isHashtag, localUsername)); + models.add(ResponseBodyUtils.parseStoryItem(data, isLoc, isHashtag, username)); } callback.onSuccess(models); } else { @@ -410,21 +466,42 @@ public class StoriesService extends BaseService { respondToSticker(storyId, stickerId, "story_slider_vote", "vote", String.valueOf(answer), userId, csrfToken, callback); } - private String buildUrl(final String id, final boolean isLoc, final boolean isHashtag, final boolean highlight) { - final String userId = id.replace(":", "%3A"); + @Nullable + private String buildUrl(@NonNull final StoryViewerOptions options) { final StringBuilder builder = new StringBuilder(); builder.append("https://i.instagram.com/api/v1/"); - if (isLoc) { - builder.append("locations/"); - } else if (isHashtag) { - builder.append("tags/"); - } else if (highlight) { - builder.append("feed/reels_media/?user_ids="); - } else { - builder.append("feed/user/"); + final StoryViewerOptions.Type type = options.getType(); + String id = null; + switch (type) { + case HASHTAG: + builder.append("tags/"); + id = options.getName(); + break; + case LOCATION: + builder.append("locations/"); + id = String.valueOf(options.getId()); + break; + case USER: + builder.append("feed/user/"); + id = String.valueOf(options.getId()); + break; + case HIGHLIGHT: + builder.append("feed/reels_media/?user_ids="); + id = options.getName(); + break; + case STORY: + break; + // case FEED_STORY_POSITION: + // break; + // case STORY_ARCHIVE: + // break; } + if (id == null) { + return null; + } + final String userId = id.replace(":", "%3A"); builder.append(userId); - if (!highlight) { + if (type != StoryViewerOptions.Type.HIGHLIGHT) { builder.append("/story/"); } return builder.toString(); diff --git a/app/src/main/res/layout/layout_location_details.xml b/app/src/main/res/layout/layout_location_details.xml index 9176519a..cee275cb 100644 --- a/app/src/main/res/layout/layout_location_details.xml +++ b/app/src/main/res/layout/layout_location_details.xml @@ -83,24 +83,18 @@ app:layout_constraintTop_toBottomOf="@id/mainLocationImage" tools:text="OUR HOUSE" /> - + tools:text="IN THE MIDDLE OF OUR STREET" /> + + + app:argType="awais.instagrabber.repositories.responses.discover.TopicCluster" /> - - - - - - - + android:name="options" + app:argType="awais.instagrabber.repositories.requests.StoryViewerOptions" /> \ No newline at end of file diff --git a/app/src/main/res/navigation/hashtag_nav_graph.xml b/app/src/main/res/navigation/hashtag_nav_graph.xml index c8a61d1e..cc449694 100644 --- a/app/src/main/res/navigation/hashtag_nav_graph.xml +++ b/app/src/main/res/navigation/hashtag_nav_graph.xml @@ -74,35 +74,8 @@ android:label="StoryViewerFragment" tools:layout="@layout/fragment_story_viewer"> - - - - - - - + android:name="options" + app:argType="awais.instagrabber.repositories.requests.StoryViewerOptions" /> - - - - - - - + android:name="options" + app:argType="awais.instagrabber.repositories.requests.StoryViewerOptions" /> \ 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 6b8dfe9a..dffb4a38 100644 --- a/app/src/main/res/navigation/notification_viewer_nav_graph.xml +++ b/app/src/main/res/navigation/notification_viewer_nav_graph.xml @@ -67,34 +67,7 @@ android:label="StoryViewerFragment" tools:layout="@layout/fragment_story_viewer"> - - - - - - - + android:name="options" + app:argType="awais.instagrabber.repositories.requests.StoryViewerOptions" /> \ 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 dd676668..cda21c30 100644 --- a/app/src/main/res/navigation/profile_nav_graph.xml +++ b/app/src/main/res/navigation/profile_nav_graph.xml @@ -153,61 +153,37 @@ android:name="awais.instagrabber.fragments.StoryViewerFragment" android:label="StoryViewerFragment" tools:layout="@layout/fragment_story_viewer"> + - - - - - - - - - - - - - - - - + android:name="options" + app:argType="awais.instagrabber.repositories.requests.StoryViewerOptions" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/story_list_nav_graph.xml b/app/src/main/res/navigation/story_list_nav_graph.xml index 35995a66..0820b7d0 100644 --- a/app/src/main/res/navigation/story_list_nav_graph.xml +++ b/app/src/main/res/navigation/story_list_nav_graph.xml @@ -43,34 +43,7 @@ android:label="StoryViewerFragment" tools:layout="@layout/fragment_story_viewer"> - - - - - - - + android:name="options" + app:argType="awais.instagrabber.repositories.requests.StoryViewerOptions" /> \ No newline at end of file diff --git a/app/src/main/res/navigation/user_search_nav_graph.xml b/app/src/main/res/navigation/user_search_nav_graph.xml index bd5eeb70..9caaea01 100644 --- a/app/src/main/res/navigation/user_search_nav_graph.xml +++ b/app/src/main/res/navigation/user_search_nav_graph.xml @@ -28,21 +28,25 @@ - + + + + + - + + + + - - --> + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 91dbdd46..5532c609 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -319,7 +319,7 @@ Save unsuccessful Remove unsuccessful Downloading… - Download item %d of %d + Download item %1$d of %2$d Delete Comment Layout diff --git a/fastlane/metadata/android/en-US/changelogs/57.txt b/fastlane/metadata/android/en-US/changelogs/57.txt new file mode 100644 index 00000000..77b86f46 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/57.txt @@ -0,0 +1,3 @@ +Preliminary support for live videos, as well as bug fixes. + +For details see https://github.com/austinhuang0131/barinsta/releases/tag/v19.0.5 \ No newline at end of file