mirror of
https://github.com/KokaKiwi/BarInsta
synced 2024-11-22 22:57:29 +00:00
Merge remote-tracking branch 'origin/dm-notifications-enhancements' into dm-notifications-enhancements
This commit is contained in:
commit
039194d046
4
.github/ISSUE_TEMPLATE/ban_report.md
vendored
4
.github/ISSUE_TEMPLATE/ban_report.md
vendored
@ -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.
|
||||
|
12
.github/ISSUE_TEMPLATE/bug_report.md
vendored
12
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -6,10 +6,10 @@ labels: bug
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
<!-- FOLLOW THIS FORMAT -->
|
||||
<!-- If you choose not to follow this format, you should erase the entire body. -->
|
||||
|
||||
- [ ] 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).
|
||||
|
||||
<!--
|
||||
Describe the bug here. Don't stress too much, but do include the key points.
|
||||
@ -18,11 +18,9 @@ Describe the bug here. Don't stress too much, but do include the key points.
|
||||
## Steps
|
||||
|
||||
<!--
|
||||
DETAILED Steps to reproduce the behaviour.
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
1. ...
|
||||
2. ...
|
||||
3. See error...
|
||||
-->
|
||||
|
||||
## Environment
|
||||
|
6
.github/ISSUE_TEMPLATE/questions.md
vendored
6
.github/ISSUE_TEMPLATE/questions.md
vendored
@ -7,7 +7,13 @@ assignees: ''
|
||||
---
|
||||
|
||||
<!--
|
||||
|
||||
STOP!!!
|
||||
|
||||
General questions & feedback should be submitted to
|
||||
* one of our chatrooms (see README.md), or
|
||||
* r/barinsta on reddit.
|
||||
|
||||
STOP!!!
|
||||
|
||||
-->
|
||||
|
@ -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"
|
||||
|
@ -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<TopicCluster, TopicClusterViewHolder> {
|
||||
|
@ -83,7 +83,7 @@ public final class NotificationsAdapter extends ListAdapter<NotificationModel, N
|
||||
else if (o1.getType() == NotificationType.REQUEST) return -1;
|
||||
else if (o2.getType() == NotificationType.REQUEST) return 1;
|
||||
// timestamp
|
||||
return o1.getTimestamp() > o2.getTimestamp() ? -1 : (o1.getTimestamp() == o2.getTimestamp() ? 0 : 1);
|
||||
return Long.compare(o2.getTimestamp(), o1.getTimestamp());
|
||||
});
|
||||
return listCopy;
|
||||
}
|
||||
|
@ -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<PostMediaViewHolder> {
|
||||
// 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;
|
||||
// }
|
||||
// }
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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());
|
||||
// }
|
||||
// }
|
@ -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 {
|
||||
|
@ -1,4 +1,4 @@
|
||||
package awais.instagrabber.asyncs.direct_messages;
|
||||
package awais.instagrabber.asyncs;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
import android.util.Log;
|
@ -52,8 +52,7 @@ public final class LocationFetcher extends AsyncTask<Void, Void, LocationModel>
|
||||
// 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"),
|
||||
|
@ -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<BroadcastOptions, Void, DirectThreadBroadcastResponse> {
|
||||
// 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<String, String> 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<String, String> 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<String, String> 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<String, String> getFormMap() {
|
||||
// final Map<String, String> 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<String, String> getFormMap() {
|
||||
// final Map<String, String> 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<String, String> getFormMap() {
|
||||
// final Map<String, String> 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;
|
||||
// }
|
||||
// }
|
||||
// }
|
@ -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);
|
||||
}
|
||||
}
|
@ -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<List<StoryModel>>() {
|
||||
@Override
|
||||
public void onSuccess(final List<StoryModel> storyModels) {
|
||||
if (storyModels != null && !storyModels.isEmpty()) {
|
||||
hashtagDetailsBinding.mainHashtagImage.setStoriesBorder();
|
||||
hashtagDetailsBinding.mainHashtagImage.setStoriesBorder(1);
|
||||
hasStories = true;
|
||||
} else {
|
||||
hasStories = false;
|
||||
|
@ -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<Favorite>() {
|
||||
favoriteRepository.getFavorite(String.valueOf(locationId), FavoriteType.LOCATION, new RepositoryCallback<Favorite>() {
|
||||
@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<Favorite>() {
|
||||
favoriteRepository.getFavorite(String.valueOf(locationId), FavoriteType.LOCATION, new RepositoryCallback<Favorite>() {
|
||||
@Override
|
||||
public void onSuccess(final Favorite result) {
|
||||
favoriteRepository.deleteFavorite(locationId, FavoriteType.LOCATION, new RepositoryCallback<Void>() {
|
||||
favoriteRepository.deleteFavorite(String.valueOf(locationId), FavoriteType.LOCATION, new RepositoryCallback<Void>() {
|
||||
@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<List<StoryModel>>() {
|
||||
@Override
|
||||
public void onSuccess(final List<StoryModel> storyModels) {
|
||||
if (storyModels != null && !storyModels.isEmpty()) {
|
||||
locationDetailsBinding.mainLocationImage.setStoriesBorder();
|
||||
hasStories = true;
|
||||
}
|
||||
storiesFetching = false;
|
||||
}
|
||||
storiesService.getUserStory(
|
||||
StoryViewerOptions.forLocation(locationId, locationModel.getName()),
|
||||
new ServiceCallback<List<StoryModel>>() {
|
||||
@Override
|
||||
public void onSuccess(final List<StoryModel> 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;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<Media>() {
|
||||
@ -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;
|
||||
}
|
||||
|
@ -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<FeedStoryModel> result) {
|
||||
feedStoriesViewModel.getList().postValue(result);
|
||||
adapter.submitList(result);
|
||||
binding.swipeRefreshLayout.setRefreshing(false);
|
||||
}
|
||||
|
||||
|
@ -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<HighlightModel> 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<HighlightModel> 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<HighlightModel> 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<FeedStoryModel> 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<HighlightModel> 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<FeedStoryModel> 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<StoryModel>() {
|
||||
if (type == Type.STORY) {
|
||||
storiesService.fetch(options.getId(), new ServiceCallback<StoryModel>() {
|
||||
@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<List<StoryModel>> storyCallback = new ServiceCallback<List<StoryModel>>() {
|
||||
@Override
|
||||
public void onSuccess(final List<StoryModel> 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) {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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<FeedStoryModel> result) {
|
||||
feedStoriesViewModel.getList().postValue(result);
|
||||
feedStoriesAdapter.submitList(result);
|
||||
storiesFetching = false;
|
||||
if (storyListMenu != null) storyListMenu.setVisible(true);
|
||||
updateSwipeRefreshState();
|
||||
|
@ -874,7 +874,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
|
||||
@Override
|
||||
public void onSuccess(final List<StoryModel> storyModels) {
|
||||
if (storyModels != null && !storyModels.isEmpty()) {
|
||||
profileDetailsBinding.mainProfileImage.setStoriesBorder();
|
||||
profileDetailsBinding.mainProfileImage.setStoriesBorder(1);
|
||||
hasStories = true;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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<Integer, MediaItemType> map = new HashMap<>();
|
||||
|
@ -11,6 +11,6 @@ import retrofit2.http.QueryMap;
|
||||
public interface LocationRepository {
|
||||
|
||||
@GET("/api/v1/feed/location/{location}/")
|
||||
Call<LocationFeedResponse> fetchPosts(@Path("location") final String locationId,
|
||||
Call<LocationFeedResponse> fetchPosts(@Path("location") final long locationId,
|
||||
@QueryMap Map<String, String> queryParams);
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ import retrofit2.http.QueryMap;
|
||||
|
||||
public interface MediaRepository {
|
||||
@GET("/api/v1/media/{mediaId}/info/")
|
||||
Call<MediaInfoResponse> fetch(@Path("mediaId") final String mediaId);
|
||||
Call<MediaInfoResponse> fetch(@Path("mediaId") final long mediaId);
|
||||
|
||||
@GET("/api/v1/media/{mediaId}/{action}/")
|
||||
Call<LikersResponse> fetchLikes(@Path("mediaId") final String mediaId,
|
||||
|
@ -15,7 +15,7 @@ import retrofit2.http.Url;
|
||||
|
||||
public interface StoriesRepository {
|
||||
@GET("/api/v1/media/{mediaId}/info/")
|
||||
Call<String> fetch(@Path("mediaId") final String mediaId);
|
||||
Call<String> 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/")
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package awais.instagrabber.models;
|
||||
package awais.instagrabber.repositories.responses.discover;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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:");
|
||||
|
@ -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;
|
||||
|
@ -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<TopicCluster>> list;
|
||||
|
@ -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<PostsFetchResponse> callback) {
|
||||
fetch("36bd0f2bf5911908de389b8ceaa3be6d",
|
||||
|
@ -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<PostsFetchResponse> callback) {
|
||||
final ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
|
||||
|
@ -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<Media> callback) {
|
||||
final Call<MediaInfoResponse> request = repository.fetch(mediaId);
|
||||
request.enqueue(new Callback<MediaInfoResponse>() {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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<StoryModel> callback) {
|
||||
final Call<String> request = repository.fetch(mediaId);
|
||||
request.enqueue(new Callback<String>() {
|
||||
@ -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<List<StoryModel>> callback) {
|
||||
final String url = buildUrl(id, isLoc, isHashtag, highlight);
|
||||
Log.d("austin_debug", url);
|
||||
final String url = buildUrl(options);
|
||||
final Call<String> 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<String>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> 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<StoryModel> 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();
|
||||
|
@ -83,24 +83,18 @@
|
||||
app:layout_constraintTop_toBottomOf="@id/mainLocationImage"
|
||||
tools:text="OUR HOUSE" />
|
||||
|
||||
<awais.instagrabber.customviews.RamboTextView
|
||||
<awais.instagrabber.customviews.RamboTextViewV2
|
||||
android:id="@+id/locationBiography"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/locationFullName"
|
||||
android:padding="8dp"
|
||||
android:background="?android:selectableItemBackground"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingLeft="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingRight="8dp"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toTopOf="@id/locationUrl"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/locationFullName"
|
||||
tools:text="IN THE MIDDLE OF OUR STREET"
|
||||
tools:visibility="visible" />
|
||||
tools:text="IN THE MIDDLE OF OUR STREET" />
|
||||
|
||||
<awais.instagrabber.customviews.RamboTextView
|
||||
android:id="@+id/locationUrl"
|
||||
|
@ -141,6 +141,10 @@
|
||||
app:argType="string"
|
||||
app:nullable="false" />
|
||||
|
||||
<argument
|
||||
android:name="title"
|
||||
app:argType="string" />
|
||||
|
||||
<action
|
||||
android:id="@+id/action_settings_to_inbox"
|
||||
app:destination="@id/directMessagesInboxFragment"
|
||||
|
@ -96,7 +96,7 @@
|
||||
tools:layout="@layout/fragment_topic_posts">
|
||||
<argument
|
||||
android:name="topicCluster"
|
||||
app:argType="awais.instagrabber.models.TopicCluster" />
|
||||
app:argType="awais.instagrabber.repositories.responses.discover.TopicCluster" />
|
||||
|
||||
<argument
|
||||
android:name="titleColor"
|
||||
|
@ -107,34 +107,7 @@
|
||||
android:label="StoryViewerFragment"
|
||||
tools:layout="@layout/fragment_story_viewer">
|
||||
<argument
|
||||
android:name="feedStoryIndex"
|
||||
app:argType="integer"
|
||||
app:nullable="false" />
|
||||
<argument
|
||||
android:name="highlight"
|
||||
app:argType="string"
|
||||
app:nullable="true" />
|
||||
<argument
|
||||
android:name="isHashtag"
|
||||
app:argType="boolean" />
|
||||
<argument
|
||||
android:name="isLoc"
|
||||
app:argType="boolean" />
|
||||
<argument
|
||||
android:name="profileId"
|
||||
app:argType="string"
|
||||
app:nullable="true" />
|
||||
<argument
|
||||
android:name="username"
|
||||
app:argType="string"
|
||||
app:nullable="true" />
|
||||
<argument
|
||||
android:name="isArchive"
|
||||
app:argType="boolean"
|
||||
app:nullable="false" />
|
||||
<argument
|
||||
android:name="isNotification"
|
||||
app:argType="boolean"
|
||||
app:nullable="false" />
|
||||
android:name="options"
|
||||
app:argType="awais.instagrabber.repositories.requests.StoryViewerOptions" />
|
||||
</fragment>
|
||||
</navigation>
|
@ -74,35 +74,8 @@
|
||||
android:label="StoryViewerFragment"
|
||||
tools:layout="@layout/fragment_story_viewer">
|
||||
<argument
|
||||
android:name="feedStoryIndex"
|
||||
app:argType="integer"
|
||||
app:nullable="false" />
|
||||
<argument
|
||||
android:name="highlight"
|
||||
app:argType="string"
|
||||
app:nullable="true" />
|
||||
<argument
|
||||
android:name="isHashtag"
|
||||
app:argType="boolean" />
|
||||
<argument
|
||||
android:name="isLoc"
|
||||
app:argType="boolean" />
|
||||
<argument
|
||||
android:name="profileId"
|
||||
app:argType="string"
|
||||
app:nullable="true" />
|
||||
<argument
|
||||
android:name="username"
|
||||
app:argType="string"
|
||||
app:nullable="true" />
|
||||
<argument
|
||||
android:name="isArchive"
|
||||
app:argType="boolean"
|
||||
app:nullable="false" />
|
||||
<argument
|
||||
android:name="isNotification"
|
||||
app:argType="boolean"
|
||||
app:nullable="false" />
|
||||
android:name="options"
|
||||
app:argType="awais.instagrabber.repositories.requests.StoryViewerOptions" />
|
||||
</fragment>
|
||||
<action
|
||||
android:id="@+id/action_global_hashTagFragment"
|
||||
|
@ -81,34 +81,7 @@
|
||||
android:label="StoryViewerFragment"
|
||||
tools:layout="@layout/fragment_story_viewer">
|
||||
<argument
|
||||
android:name="feedStoryIndex"
|
||||
app:argType="integer"
|
||||
app:nullable="false" />
|
||||
<argument
|
||||
android:name="highlight"
|
||||
app:argType="string"
|
||||
app:nullable="true" />
|
||||
<argument
|
||||
android:name="isHashtag"
|
||||
app:argType="boolean" />
|
||||
<argument
|
||||
android:name="isLoc"
|
||||
app:argType="boolean" />
|
||||
<argument
|
||||
android:name="profileId"
|
||||
app:argType="string"
|
||||
app:nullable="true" />
|
||||
<argument
|
||||
android:name="username"
|
||||
app:argType="string"
|
||||
app:nullable="true" />
|
||||
<argument
|
||||
android:name="isArchive"
|
||||
app:argType="boolean"
|
||||
app:nullable="false" />
|
||||
<argument
|
||||
android:name="isNotification"
|
||||
app:argType="boolean"
|
||||
app:nullable="false" />
|
||||
android:name="options"
|
||||
app:argType="awais.instagrabber.repositories.requests.StoryViewerOptions" />
|
||||
</fragment>
|
||||
</navigation>
|
@ -67,34 +67,7 @@
|
||||
android:label="StoryViewerFragment"
|
||||
tools:layout="@layout/fragment_story_viewer">
|
||||
<argument
|
||||
android:name="feedStoryIndex"
|
||||
app:argType="integer"
|
||||
app:nullable="false" />
|
||||
<argument
|
||||
android:name="highlight"
|
||||
app:argType="string"
|
||||
app:nullable="true" />
|
||||
<argument
|
||||
android:name="isHashtag"
|
||||
app:argType="boolean" />
|
||||
<argument
|
||||
android:name="isLoc"
|
||||
app:argType="boolean" />
|
||||
<argument
|
||||
android:name="profileId"
|
||||
app:argType="string"
|
||||
app:nullable="true" />
|
||||
<argument
|
||||
android:name="username"
|
||||
app:argType="string"
|
||||
app:nullable="true" />
|
||||
<argument
|
||||
android:name="isArchive"
|
||||
app:argType="boolean"
|
||||
app:nullable="false" />
|
||||
<argument
|
||||
android:name="isNotification"
|
||||
app:argType="boolean"
|
||||
app:nullable="false" />
|
||||
android:name="options"
|
||||
app:argType="awais.instagrabber.repositories.requests.StoryViewerOptions" />
|
||||
</fragment>
|
||||
</navigation>
|
@ -153,61 +153,37 @@
|
||||
android:name="awais.instagrabber.fragments.StoryViewerFragment"
|
||||
android:label="StoryViewerFragment"
|
||||
tools:layout="@layout/fragment_story_viewer">
|
||||
|
||||
<argument
|
||||
android:name="feedStoryIndex"
|
||||
app:argType="integer"
|
||||
app:nullable="false" />
|
||||
<argument
|
||||
android:name="highlight"
|
||||
app:argType="string"
|
||||
app:nullable="true" />
|
||||
<argument
|
||||
android:name="isHashtag"
|
||||
app:argType="boolean" />
|
||||
<argument
|
||||
android:name="isLoc"
|
||||
app:argType="boolean" />
|
||||
<argument
|
||||
android:name="profileId"
|
||||
app:argType="long" />
|
||||
<argument
|
||||
android:name="username"
|
||||
app:argType="string"
|
||||
app:nullable="true" />
|
||||
<argument
|
||||
android:name="isArchive"
|
||||
app:argType="boolean"
|
||||
app:nullable="false" />
|
||||
<argument
|
||||
android:name="isNotification"
|
||||
app:argType="boolean"
|
||||
app:nullable="false" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/directMessagesThreadFragment"
|
||||
android:name="awais.instagrabber.fragments.directmessages.DirectMessageThreadFragment"
|
||||
android:label="DirectMessagesThreadFragment"
|
||||
tools:layout="@layout/fragment_direct_messages_thread">
|
||||
<argument
|
||||
android:name="threadId"
|
||||
app:argType="string" />
|
||||
<argument
|
||||
android:name="title"
|
||||
app:argType="string" />
|
||||
<action
|
||||
android:id="@+id/action_dMThreadFragment_to_dMSettingsFragment"
|
||||
app:destination="@id/directMessagesSettingsFragment" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/directMessagesSettingsFragment"
|
||||
android:name="awais.instagrabber.fragments.directmessages.DirectMessageSettingsFragment"
|
||||
android:label="DirectMessagesSettingsFragment"
|
||||
tools:layout="@layout/fragment_direct_messages_settings">
|
||||
<argument
|
||||
android:name="threadId"
|
||||
app:argType="string" />
|
||||
<argument
|
||||
android:name="title"
|
||||
app:argType="string" />
|
||||
android:name="options"
|
||||
app:argType="awais.instagrabber.repositories.requests.StoryViewerOptions" />
|
||||
</fragment>
|
||||
<!--<fragment-->
|
||||
<!-- android:id="@+id/directMessagesThreadFragment"-->
|
||||
<!-- android:name="awais.instagrabber.fragments.directmessages.DirectMessageThreadFragment"-->
|
||||
<!-- android:label="DirectMessagesThreadFragment"-->
|
||||
<!-- tools:layout="@layout/fragment_direct_messages_thread">-->
|
||||
<!-- <argument-->
|
||||
<!-- android:name="threadId"-->
|
||||
<!-- app:argType="string" />-->
|
||||
<!-- <argument-->
|
||||
<!-- android:name="title"-->
|
||||
<!-- app:argType="string" />-->
|
||||
<!-- <action-->
|
||||
<!-- android:id="@+id/action_dMThreadFragment_to_dMSettingsFragment"-->
|
||||
<!-- app:destination="@id/directMessagesSettingsFragment" />-->
|
||||
<!--</fragment>-->
|
||||
<!--<fragment-->
|
||||
<!-- android:id="@+id/directMessagesSettingsFragment"-->
|
||||
<!-- android:name="awais.instagrabber.fragments.directmessages.DirectMessageSettingsFragment"-->
|
||||
<!-- android:label="@string/details"-->
|
||||
<!-- tools:layout="@layout/fragment_direct_messages_settings">-->
|
||||
<!-- <argument-->
|
||||
<!-- android:name="threadId"-->
|
||||
<!-- app:argType="string"-->
|
||||
<!-- app:nullable="false"/>-->
|
||||
<!-- <argument-->
|
||||
<!-- android:name="title"-->
|
||||
<!-- app:argType="string" />-->
|
||||
<!--</fragment>-->
|
||||
</navigation>
|
@ -43,34 +43,7 @@
|
||||
android:label="StoryViewerFragment"
|
||||
tools:layout="@layout/fragment_story_viewer">
|
||||
<argument
|
||||
android:name="feedStoryIndex"
|
||||
app:argType="integer"
|
||||
app:nullable="false" />
|
||||
<argument
|
||||
android:name="highlight"
|
||||
app:argType="string"
|
||||
app:nullable="true" />
|
||||
<argument
|
||||
android:name="isHashtag"
|
||||
app:argType="boolean" />
|
||||
<argument
|
||||
android:name="isLoc"
|
||||
app:argType="boolean" />
|
||||
<argument
|
||||
android:name="profileId"
|
||||
app:argType="string"
|
||||
app:nullable="true" />
|
||||
<argument
|
||||
android:name="username"
|
||||
app:argType="string"
|
||||
app:nullable="true" />
|
||||
<argument
|
||||
android:name="isArchive"
|
||||
app:argType="boolean"
|
||||
app:nullable="false" />
|
||||
<argument
|
||||
android:name="isNotification"
|
||||
app:argType="boolean"
|
||||
app:nullable="false" />
|
||||
android:name="options"
|
||||
app:argType="awais.instagrabber.repositories.requests.StoryViewerOptions" />
|
||||
</fragment>
|
||||
</navigation>
|
@ -28,21 +28,25 @@
|
||||
|
||||
</fragment>
|
||||
|
||||
<!--<action
|
||||
android:id="@+id/action_global_user_search"
|
||||
app:destination="@id/user_search">
|
||||
<argument
|
||||
android:name="multiple"
|
||||
app:argType="boolean" />
|
||||
<!--<action-->
|
||||
<!-- android:id="@+id/action_global_user_search"-->
|
||||
<!-- app:destination="@id/user_search_nav_graph">-->
|
||||
<!-- <argument-->
|
||||
<!-- android:name="multiple"-->
|
||||
<!-- app:argType="boolean" />-->
|
||||
|
||||
<argument
|
||||
android:name="title"
|
||||
app:argType="string"
|
||||
app:nullable="true" />
|
||||
<!-- <argument-->
|
||||
<!-- android:name="title"-->
|
||||
<!-- app:argType="string"-->
|
||||
<!-- app:nullable="true" />-->
|
||||
|
||||
<argument
|
||||
android:name="action_label"
|
||||
app:argType="string"
|
||||
app:nullable="true" />
|
||||
</action>-->
|
||||
<!-- <argument-->
|
||||
<!-- android:name="action_label"-->
|
||||
<!-- app:argType="string"-->
|
||||
<!-- app:nullable="true" />-->
|
||||
|
||||
<!-- <argument-->
|
||||
<!-- android:name="hideUserIds"-->
|
||||
<!-- app:argType="long[]" />-->
|
||||
<!--</action>-->
|
||||
</navigation>
|
@ -319,7 +319,7 @@
|
||||
<string name="save_unsuccessful">Save unsuccessful</string>
|
||||
<string name="save_remove_unsuccessful">Remove unsuccessful</string>
|
||||
<string name="downloading">Downloading…</string>
|
||||
<string name="downloader_downloading_child">Download item %d of %d</string>
|
||||
<string name="downloader_downloading_child">Download item %1$d of %2$d</string>
|
||||
<string name="delete">Delete</string>
|
||||
<string name="comment">Comment</string>
|
||||
<string name="layout">Layout</string>
|
||||
|
3
fastlane/metadata/android/en-US/changelogs/57.txt
Normal file
3
fastlane/metadata/android/en-US/changelogs/57.txt
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user