Merge remote-tracking branch 'origin/dm-notifications-enhancements' into dm-notifications-enhancements

This commit is contained in:
Ammar Githam 2021-01-08 21:49:48 +09:00
commit 039194d046
55 changed files with 674 additions and 901 deletions

View File

@ -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.

View File

@ -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

View File

@ -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!!!
-->

View File

@ -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"

View File

@ -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> {

View File

@ -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;
}

View File

@ -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;
// }
// }

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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());
// }
// }

View File

@ -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 {

View File

@ -1,4 +1,4 @@
package awais.instagrabber.asyncs.direct_messages;
package awais.instagrabber.asyncs;
import android.os.AsyncTask;
import android.util.Log;

View File

@ -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"),

View File

@ -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;
// }
// }
// }

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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;
}
});
}
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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) {

View File

@ -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;

View File

@ -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;
}

View File

@ -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();

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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<>();

View File

@ -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);
}

View File

@ -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,

View File

@ -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/")

View File

@ -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
}
}

View File

@ -1,4 +1,4 @@
package awais.instagrabber.models;
package awais.instagrabber.repositories.responses.discover;
import java.io.Serializable;

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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:");

View File

@ -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;

View File

@ -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;

View File

@ -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",

View File

@ -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();

View File

@ -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>() {

View File

@ -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);
}
}

View File

@ -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();

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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>

View File

@ -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"

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View 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