Browse Source

wip: archive and feed stories as a list

renovate/org.robolectric-robolectric-4.x
Austin Huang 4 years ago
parent
commit
6e4fa9fdbf
No known key found for this signature in database GPG Key ID: 84C23AA04587A91F
  1. 97
      app/src/main/java/awais/instagrabber/adapters/FeedStoriesListAdapter.java
  2. 53
      app/src/main/java/awais/instagrabber/adapters/HighlightStoriesListAdapter.java
  3. 69
      app/src/main/java/awais/instagrabber/adapters/viewholder/StoryListViewHolder.java
  4. 108
      app/src/main/java/awais/instagrabber/fragments/StoryListViewerFragment.java
  5. 7
      app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java
  6. 5
      app/src/main/java/awais/instagrabber/fragments/settings/MorePreferencesFragment.java
  7. 25
      app/src/main/java/awais/instagrabber/models/FeedStoryModel.java
  8. 20
      app/src/main/java/awais/instagrabber/models/HighlightModel.java
  9. 9
      app/src/main/java/awais/instagrabber/models/StoryModel.java
  10. 3
      app/src/main/java/awais/instagrabber/repositories/StoriesRepository.java
  11. 2
      app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java
  12. 65
      app/src/main/java/awais/instagrabber/webservices/StoriesService.java
  13. 10
      app/src/main/res/drawable/ic_archive.xml
  14. 10
      app/src/main/res/drawable/ic_story_list.xml
  15. 15
      app/src/main/res/layout/fragment_story_list_viewer.xml
  16. 5
      app/src/main/res/menu/feed_menu.xml
  17. 11
      app/src/main/res/navigation/feed_nav_graph.xml
  18. 10
      app/src/main/res/navigation/more_nav_graph.xml
  19. 68
      app/src/main/res/navigation/story_list_nav_graph.xml
  20. 2
      app/src/main/res/values/strings.xml

97
app/src/main/java/awais/instagrabber/adapters/FeedStoriesListAdapter.java

@ -0,0 +1,97 @@
package awais.instagrabber.adapters;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import awais.instagrabber.adapters.viewholder.StoryListViewHolder;
import awais.instagrabber.databinding.ItemNotificationBinding;
import awais.instagrabber.models.FeedStoryModel;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
public final class FeedStoriesListAdapter extends ListAdapter<FeedStoryModel, StoryListViewHolder> {
private final OnFeedStoryClickListener listener;
private static final DiffUtil.ItemCallback<FeedStoryModel> diffCallback = new DiffUtil.ItemCallback<FeedStoryModel>() {
@Override
public boolean areItemsTheSame(@NonNull final FeedStoryModel oldItem, @NonNull final FeedStoryModel newItem) {
return oldItem.getStoryMediaId().equals(newItem.getStoryMediaId());
}
@Override
public boolean areContentsTheSame(@NonNull final FeedStoryModel oldItem, @NonNull final FeedStoryModel newItem) {
return oldItem.getStoryMediaId().equals(newItem.getStoryMediaId()) && oldItem.isFullyRead().equals(newItem.isFullyRead());
}
};
public FeedStoriesListAdapter(final OnFeedStoryClickListener listener) {
super(diffCallback);
this.listener = listener;
}
@NonNull
@Override
public StoryListViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
final ItemNotificationBinding binding = ItemNotificationBinding.inflate(layoutInflater, parent, false);
return new StoryListViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull final StoryListViewHolder holder, final int position) {
final FeedStoryModel model = getItem(position);
holder.bind(model, listener);
}
@Override
public void submitList(@Nullable final List<FeedStoryModel> list, @Nullable final Runnable commitCallback) {
if (list == null) {
super.submitList(null, commitCallback);
return;
}
super.submitList(sort(list), commitCallback);
}
@Override
public void submitList(@Nullable final List<FeedStoryModel> list) {
if (list == null) {
super.submitList(null);
return;
}
super.submitList(sort(list));
}
private List<FeedStoryModel> sort(final List<FeedStoryModel> list) {
final List<FeedStoryModel> listCopy = new ArrayList<>(list);
Collections.sort(listCopy, (o1, o2) -> {
int result;
switch (Utils.settingsHelper.getString(Constants.STORY_SORT)) {
case "1":
result = o1.getTimestamp() > o2.getTimestamp() ? -1 : (o1.getTimestamp() == o2.getTimestamp() ? 0 : 1);
break;
case "2":
result = o1.getTimestamp() > o2.getTimestamp() ? 1 : (o1.getTimestamp() == o2.getTimestamp() ? 0 : -1);
break;
default:
result = 0;
}
return result;
});
return listCopy;
}
public interface OnFeedStoryClickListener {
void onFeedStoryClick(final FeedStoryModel model);
void onProfileClick(final String username);
}
}

53
app/src/main/java/awais/instagrabber/adapters/HighlightStoriesListAdapter.java

@ -0,0 +1,53 @@
package awais.instagrabber.adapters;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import awais.instagrabber.adapters.viewholder.StoryListViewHolder;
import awais.instagrabber.databinding.ItemNotificationBinding;
import awais.instagrabber.models.HighlightModel;
public final class HighlightStoriesListAdapter extends ListAdapter<HighlightModel, StoryListViewHolder> {
private final OnHighlightStoryClickListener listener;
private static final DiffUtil.ItemCallback<HighlightModel> diffCallback = new DiffUtil.ItemCallback<HighlightModel>() {
@Override
public boolean areItemsTheSame(@NonNull final HighlightModel oldItem, @NonNull final HighlightModel newItem) {
return oldItem.getId().equals(newItem.getId());
}
@Override
public boolean areContentsTheSame(@NonNull final HighlightModel oldItem, @NonNull final HighlightModel newItem) {
return oldItem.getId().equals(newItem.getId());
}
};
public HighlightStoriesListAdapter(final OnHighlightStoryClickListener listener) {
super(diffCallback);
this.listener = listener;
}
@NonNull
@Override
public StoryListViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
final ItemNotificationBinding binding = ItemNotificationBinding.inflate(layoutInflater, parent, false);
return new StoryListViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull final StoryListViewHolder holder, final int position) {
final HighlightModel model = getItem(position);
holder.bind(model, listener);
}
public interface OnHighlightStoryClickListener {
void onHighlightClick(HighlightModel model);
void onProfileClick(String username);
}
}

69
app/src/main/java/awais/instagrabber/adapters/viewholder/StoryListViewHolder.java

@ -0,0 +1,69 @@
package awais.instagrabber.adapters.viewholder;
import android.text.TextUtils;
import android.view.View;
import androidx.recyclerview.widget.RecyclerView;
import awais.instagrabber.R;
import awais.instagrabber.adapters.FeedStoriesListAdapter.OnFeedStoryClickListener;
import awais.instagrabber.adapters.HighlightStoriesListAdapter.OnHighlightStoryClickListener;
import awais.instagrabber.databinding.ItemNotificationBinding;
import awais.instagrabber.models.FeedStoryModel;
import awais.instagrabber.models.HighlightModel;
public final class StoryListViewHolder extends RecyclerView.ViewHolder {
private final ItemNotificationBinding binding;
public StoryListViewHolder(final ItemNotificationBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
public void bind(final FeedStoryModel model,
final OnFeedStoryClickListener notificationClickListener) {
if (model == null) return;
binding.tvComment.setVisibility(View.GONE);
binding.tvSubComment.setVisibility(View.GONE);
binding.tvDate.setText(model.getDateTime());
binding.tvUsername.setText(model.getProfileModel().getUsername());
binding.ivProfilePic.setImageURI(model.getProfileModel().getSdProfilePic());
binding.ivProfilePic.setOnClickListener(v -> {
if (notificationClickListener == null) return;
notificationClickListener.onProfileClick(model.getProfileModel().getUsername());
});
binding.ivPreviewPic.setVisibility(View.VISIBLE);
binding.ivPreviewPic.setImageURI(model.getFirstStoryModel().getThumbnail());
binding.ivPreviewPic.setOnClickListener(v -> {
if (notificationClickListener == null) return;
notificationClickListener.onFeedStoryClick(model);
});
itemView.setOnClickListener(v -> {
if (notificationClickListener == null) return;
notificationClickListener.onFeedStoryClick(model);
});
}
public void bind(final HighlightModel model,
final OnHighlightStoryClickListener notificationClickListener) {
if (model == null) return;
binding.tvComment.setVisibility(View.GONE);
binding.tvSubComment.setVisibility(View.GONE);
binding.tvUsername.setText(model.getDateTime());
binding.ivProfilePic.setVisibility(View.GONE);
binding.ivPreviewPic.setVisibility(View.VISIBLE);
binding.ivPreviewPic.setImageURI(model.getThumbnailUrl());
itemView.setOnClickListener(v -> {
if (notificationClickListener == null) return;
notificationClickListener.onHighlightClick(model);
});
}
}

108
app/src/main/java/awais/instagrabber/fragments/StoryListViewerFragment.java

@ -0,0 +1,108 @@
package awais.instagrabber.fragments;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.NavDirections;
import androidx.navigation.fragment.NavHostFragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import awais.instagrabber.adapters.FeedStoriesListAdapter;
import awais.instagrabber.adapters.FeedStoriesListAdapter.OnFeedStoryClickListener;
import awais.instagrabber.databinding.FragmentStoryListViewerBinding;
import awais.instagrabber.fragments.settings.MorePreferencesFragmentDirections;
import awais.instagrabber.models.FeedStoryModel;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.viewmodels.StoriesViewModel;
import awais.instagrabber.webservices.StoriesService;
import static awais.instagrabber.utils.Utils.settingsHelper;
public final class StoryListViewerFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
private static final String TAG = "StoryListViewerFragment";
private FragmentStoryListViewerBinding binding;
private SwipeRefreshLayout root;
private boolean shouldRefresh = true;
private StoriesViewModel storiesViewModel;
private StoriesService storiesService;
private Context context;
private String type;
private final OnFeedStoryClickListener clickListener = new OnFeedStoryClickListener() {
@Override
public void onFeedStoryClick(final FeedStoryModel model) {
if (model == null) return;
// final NavDirections action = StoryListNavGraphDirections.actionStoryListFragmentToStoryViewerFragment(position, null, false, false, null, null);
// NavHostFragment.findNavController(StoryListViewerFragment.this).navigate(action);
}
@Override
public void onProfileClick(final String username) {
openProfile(username);
}
};
@Override
public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
context = getContext();
if (context == null) return;
storiesService = StoriesService.getInstance();
}
@NonNull
@Override
public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) {
if (root != null) {
shouldRefresh = false;
return root;
}
binding = FragmentStoryListViewerBinding.inflate(getLayoutInflater());
root = binding.getRoot();
return root;
}
@Override
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
if (!shouldRefresh) return;
init();
shouldRefresh = false;
}
private void init() {
final Context context = getContext();
if (getArguments() == null) return;
final StoryListViewerFragmentArgs fragmentArgs = StoryListViewerFragmentArgs.fromBundle(getArguments());
type = fragmentArgs.getType();
binding.swipeRefreshLayout.setOnRefreshListener(this);
storiesViewModel = new ViewModelProvider(this).get(StoriesViewModel.class);
// final NotificationsAdapter adapter = new NotificationsAdapter(clickListener, mentionClickListener);
binding.rvStories.setLayoutManager(new LinearLayoutManager(context));
// binding.rvStories.setAdapter(adapter);
// storiesViewModel.getList().observe(getViewLifecycleOwner(), adapter::submitList);
onRefresh();
}
@Override
public void onRefresh() {
binding.swipeRefreshLayout.setRefreshing(true);
binding.swipeRefreshLayout.setRefreshing(false);
// storiesViewModel.getList().postValue();
}
private void openProfile(final String username) {
final NavDirections action = MorePreferencesFragmentDirections
.actionGlobalProfileFragment("@" + username);
NavHostFragment.findNavController(this).navigate(action);
}
}

7
app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java

@ -279,7 +279,11 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
@Override
public boolean onOptionsItemSelected(@NonNull final MenuItem item) {
if (item.getItemId() == R.id.layout) {
if (item.getItemId() == R.id.storyList) {
final NavDirections action = FeedFragmentDirections.actionGlobalStoryListViewerFragment("feed");
NavHostFragment.findNavController(FeedFragment.this).navigate(action);
}
else if (item.getItemId() == R.id.layout) {
showPostsLayoutPreferences();
return true;
}
@ -358,7 +362,6 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
new FeedStoriesAdapter.OnFeedStoryClickListener() {
@Override
public void onFeedStoryClick(FeedStoryModel model, int position) {
Log.d("austin_debug", "read status is "+model.isFullyRead());
final NavDirections action = FeedFragmentDirections.actionFeedFragmentToStoryViewerFragment(position, null, false, false, null, null);
NavHostFragment.findNavController(FeedFragment.this).navigate(action);
}

5
app/src/main/java/awais/instagrabber/fragments/settings/MorePreferencesFragment.java

@ -137,6 +137,11 @@ public class MorePreferencesFragment extends BasePreferencesFragment {
NavHostFragment.findNavController(this).navigate(R.id.action_global_notificationsViewerFragment);
return true;
}));
screen.addPreference(getPreference(R.string.action_archive, R.drawable.ic_archive, preference -> {
final NavDirections navDirections = MorePreferencesFragmentDirections.actionGlobalStoryListViewerFragment("archive");
NavHostFragment.findNavController(this).navigate(navDirections);
return true;
}));
}
screen.addPreference(getPreference(R.string.title_favorites, R.drawable.ic_star_24, preference -> {
final NavDirections navDirections = MorePreferencesFragmentDirections.actionMorePreferencesFragmentToFavoritesFragment();

25
app/src/main/java/awais/instagrabber/models/FeedStoryModel.java

@ -2,21 +2,27 @@ package awais.instagrabber.models;
import android.util.Log;
import androidx.annotation.NonNull;
import java.io.Serializable;
import java.util.Date;
import awais.instagrabber.utils.Utils;
public final class FeedStoryModel implements Serializable {
private final String storyMediaId;
private final ProfileModel profileModel;
// private StoryModel[] storyModels;
private final StoryModel firstStoryModel;
private Boolean fullyRead;
private final long timestamp;
public FeedStoryModel(final String storyMediaId, final ProfileModel profileModel,
final boolean fullyRead, final long timestamp) {
final boolean fullyRead, final long timestamp, final StoryModel firstStoryModel) {
this.storyMediaId = storyMediaId;
this.profileModel = profileModel;
this.fullyRead = fullyRead;
this.timestamp = timestamp;
this.firstStoryModel = firstStoryModel;
}
public String getStoryMediaId() {
@ -27,17 +33,22 @@ public final class FeedStoryModel implements Serializable {
return timestamp;
}
@NonNull
public String getDateTime() {
return Utils.datetimeParser.format(new Date(timestamp * 1000L));
}
public ProfileModel getProfileModel() {
return profileModel;
}
// public void setStoryModels(final StoryModel[] storyModels) {
// this.storyModels = storyModels;
// public void setFirstStoryModel(final StoryModel firstStoryModel) {
// this.firstStoryModel = firstStoryModel;
// }
// public StoryModel[] getStoryModels() {
// return storyModels;
// }
public StoryModel getFirstStoryModel() {
return firstStoryModel;
}
public Boolean isFullyRead() {
return fullyRead;

20
app/src/main/java/awais/instagrabber/models/HighlightModel.java

@ -1,16 +1,25 @@
package awais.instagrabber.models;
import androidx.annotation.NonNull;
import java.util.Date;
import awais.instagrabber.utils.Utils;
public final class HighlightModel {
private final String title;
private final String id;
private final String thumbnailUrl;
private final long timestamp;
public HighlightModel(final String title,
final String id,
final String thumbnailUrl) {
final String thumbnailUrl,
final long timestamp) {
this.title = title;
this.id = id;
this.thumbnailUrl = thumbnailUrl;
this.timestamp = timestamp;
}
public String getTitle() {
@ -24,4 +33,13 @@ public final class HighlightModel {
public String getThumbnailUrl() {
return thumbnailUrl;
}
public long getTimestamp() {
return timestamp;
}
@NonNull
public String getDateTime() {
return Utils.datetimeParser.format(new Date(timestamp * 1000L));
}
}

9
app/src/main/java/awais/instagrabber/models/StoryModel.java

@ -11,7 +11,7 @@ import awais.instagrabber.models.stickers.SwipeUpModel;
public final class StoryModel implements Serializable {
private final String storyMediaId;
private final String storyUrl;
private final String storyUrl, thumbnail;
private final String username;
private final String userId;
private final MediaItemType itemType;
@ -30,10 +30,11 @@ public final class StoryModel implements Serializable {
private boolean isCurrentSlide = false;
private final boolean canReply;
public StoryModel(final String storyMediaId, final String storyUrl, final MediaItemType itemType,
public StoryModel(final String storyMediaId, final String storyUrl, final String thumbnail, final MediaItemType itemType,
final long timestamp, final String username, final String userId, final boolean canReply) {
this.storyMediaId = storyMediaId;
this.storyUrl = storyUrl;
this.thumbnail = thumbnail;
this.itemType = itemType;
this.timestamp = timestamp;
this.username = username;
@ -45,6 +46,10 @@ public final class StoryModel implements Serializable {
return storyUrl;
}
public String getThumbnail() {
return thumbnail;
}
public String getStoryMediaId() {
return storyMediaId;
}

3
app/src/main/java/awais/instagrabber/repositories/StoriesRepository.java

@ -26,6 +26,9 @@ public interface StoriesRepository {
@GET("/api/v1/highlights/{uid}/highlights_tray/")
Call<String> fetchHighlights(@Path("uid") final String uid);
@GET("/api/v1/archive/reel/day_shells/")
Call<String> fetchArchive(@QueryMap Map<String, String> queryParams);
@GET
Call<String> getUserStory(@Header("User-Agent") String userAgent, @Url String url);

2
app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java

@ -890,6 +890,8 @@ public final class ResponseBodyUtils {
final StoryModel model = new StoryModel(data.getString("id"),
data.getJSONObject("image_versions2").getJSONArray("candidates").getJSONObject(0)
.getString("url"),
data.getJSONObject("image_versions2").getJSONArray("candidates").getJSONObject(1)
.getString("url"),
isVideo ? MediaItemType.MEDIA_TYPE_VIDEO : MediaItemType.MEDIA_TYPE_IMAGE,
data.optLong("taken_at", 0),
(isLoc || isHashtag)

65
app/src/main/java/awais/instagrabber/webservices/StoriesService.java

@ -12,6 +12,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import java.util.UUID;
import awais.instagrabber.models.FeedStoryModel;
@ -124,7 +125,9 @@ public class StoriesService extends BaseService {
final String id = node.getString("id");
final long timestamp = node.getLong("latest_reel_media");
final boolean fullyRead = !node.isNull("seen") && node.getLong("seen") == timestamp;
feedStoryModels.add(new FeedStoryModel(id, profileModel, fullyRead, timestamp));
final JSONObject itemJson = node.getJSONArray("items").getJSONObject(0);
final StoryModel firstStoryModel = ResponseBodyUtils.parseStoryItem(itemJson, false, false, null);
feedStoryModels.add(new FeedStoryModel(id, profileModel, fullyRead, timestamp, firstStoryModel));
}
callback.onSuccess(feedStoryModels);
} catch (JSONException e) {
@ -179,6 +182,62 @@ public class StoriesService extends BaseService {
});
}
public void fetchArchive(final String maxId,
final ServiceCallback<ArchiveFetchResponse> callback) {
final Map<String, String> form = new HashMap<>();
form.put("include_suggested_highlights", "false");
form.put("is_in_archive_home", "true");
form.put("include_cover", "1");
form.put("timezone_offset", String.valueOf(TimeZone.getDefault().getRawOffset() / 1000));
if (!TextUtils.isEmpty(maxId)) {
form.put("max_id", maxId); // NOT TESTED
}
final Call<String> request = repository.fetchArchive(form);
request.enqueue(new Callback<String>() {
@Override
public void onResponse(@NonNull final Call<String> call, @NonNull final Response<String> response) {
try {
if (callback == null) {
return;
}
final String body = response.body();
if (TextUtils.isEmpty(body)) {
callback.onSuccess(null);
return;
}
final JSONObject data = new JSONObject(body);
final JSONArray highlightsReel = data.getJSONArray("items");
final int length = highlightsReel.length();
final List<HighlightModel> highlightModels = new ArrayList<>();
for (int i = 0; i < length; ++i) {
final JSONObject highlightNode = highlightsReel.getJSONObject(i);
highlightModels.add(new HighlightModel(
null,
highlightNode.getString(Constants.EXTRAS_ID),
highlightNode.getJSONObject("cover_image_version").getString("url"),
highlightNode.getLong("timestamp")
));
}
callback.onSuccess(new ArchiveFetchResponse(highlightModels,
data.getBoolean("more_available"),
data.getString("max_id")));
} catch (JSONException e) {
Log.e(TAG, "onResponse", e);
callback.onFailure(e);
}
}
@Override
public void onFailure(@NonNull final Call<String> call, @NonNull final Throwable t) {
if (callback != null) {
callback.onFailure(t);
}
}
});
}
public void getUserStory(final String id,
final String username,
final boolean isLoc,
@ -335,11 +394,11 @@ public class StoriesService extends BaseService {
}
public class ArchiveFetchResponse {
private List<HighlightModel> archives;
private final List<HighlightModel> archives;
private final boolean hasNextPage;
private final String nextCursor;
public ArchiveFetchResponse(final List<HighlightModel> highlightModels, final boolean hasNextPage, final String nextCursor) {
public ArchiveFetchResponse(final List<HighlightModel> archives, final boolean hasNextPage, final String nextCursor) {
this.archives = archives;
this.hasNextPage = hasNextPage;
this.nextCursor = nextCursor;

10
app/src/main/res/drawable/ic_archive.xml

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M20.54,5.23l-1.39,-1.68C18.88,3.21 18.47,3 18,3H6c-0.47,0 -0.88,0.21 -1.16,0.55L3.46,5.23C3.17,5.57 3,6.02 3,6.5V19c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V6.5c0,-0.48 -0.17,-0.93 -0.46,-1.27zM12,17.5L6.5,12H10v-2h4v2h3.5L12,17.5zM5.12,5l0.81,-1h12l0.94,1H5.12z"/>
</vector>

10
app/src/main/res/drawable/ic_story_list.xml

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M13.05,9.79L10,7.5v9l3.05,-2.29L16,12zM13.05,9.79L10,7.5v9l3.05,-2.29L16,12zM13.05,9.79L10,7.5v9l3.05,-2.29L16,12zM11,4.07L11,2.05c-2.01,0.2 -3.84,1 -5.32,2.21L7.1,5.69c1.11,-0.86 2.44,-1.44 3.9,-1.62zM5.69,7.1L4.26,5.68C3.05,7.16 2.25,8.99 2.05,11h2.02c0.18,-1.46 0.76,-2.79 1.62,-3.9zM4.07,13L2.05,13c0.2,2.01 1,3.84 2.21,5.32l1.43,-1.43c-0.86,-1.1 -1.44,-2.43 -1.62,-3.89zM5.68,19.74C7.16,20.95 9,21.75 11,21.95v-2.02c-1.46,-0.18 -2.79,-0.76 -3.9,-1.62l-1.42,1.43zM22,12c0,5.16 -3.92,9.42 -8.95,9.95v-2.02C16.97,19.41 20,16.05 20,12s-3.03,-7.41 -6.95,-7.93L13.05,2.05C18.08,2.58 22,6.84 22,12z"/>
</vector>

15
app/src/main/res/layout/fragment_story_list_viewer.xml

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/swipeRefreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".fragments.StoryListViewerFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvStories"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
tools:listitem="@layout/item_notification" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

5
app/src/main/res/menu/feed_menu.xml

@ -1,6 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/storyList"
android:icon="@drawable/ic_story_list"
android:title="@string/feed_stories"
app:showAsAction="always" />
<item
android:id="@+id/layout"
android:title="@string/layout"

11
app/src/main/res/navigation/feed_nav_graph.xml

@ -78,6 +78,17 @@
android:id="@+id/action_global_notificationsViewerFragment"
app:destination="@id/notification_viewer_nav_graph" />
<include app:graph="@navigation/story_list_nav_graph" />
<action
android:id="@+id/action_global_storyListViewerFragment"
app:destination="@id/story_list_nav_graph">
<argument
android:name="type"
app:argType="string"
app:nullable="false" />
</action>
<fragment
android:id="@+id/feedFragment"
android:name="awais.instagrabber.fragments.main.FeedFragment"

10
app/src/main/res/navigation/more_nav_graph.xml

@ -10,6 +10,7 @@
<include app:graph="@navigation/comments_nav_graph" />
<include app:graph="@navigation/likes_nav_graph" />
<include app:graph="@navigation/notification_viewer_nav_graph" />
<include app:graph="@navigation/story_list_nav_graph" />
<action
android:id="@+id/action_global_profileFragment"
@ -38,6 +39,15 @@
app:nullable="false" />
</action>
<action
android:id="@+id/action_global_storyListViewerFragment"
app:destination="@id/story_list_nav_graph">
<argument
android:name="type"
app:argType="string"
app:nullable="false" />
</action>
<action
android:id="@+id/action_global_notificationsViewerFragment"
app:destination="@id/notification_viewer_nav_graph" />

68
app/src/main/res/navigation/story_list_nav_graph.xml

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/story_list_nav_graph"
app:startDestination="@id/storyListViewerFragment">
<action
android:id="@+id/action_global_profileFragment"
app:destination="@id/profile_nav_graph">
<argument
android:name="username"
app:argType="string"
app:nullable="true" />
</action>
<fragment
android:id="@+id/storyListViewerFragment"
android:name="awais.instagrabber.fragments.StoryListViewerFragment"
android:label="Stories"
tools:layout="@layout/fragment_story_list_viewer">
<argument
android:name="type"
app:argType="string"
app:nullable="false" />
<action
android:id="@+id/action_storyListFragment_to_storyViewerFragment"
app:destination="@id/storyViewerFragment" />
</fragment>
<action
android:id="@+id/action_global_storyListViewerFragment"
app:destination="@id/storyListViewerFragment">
<argument
android:name="type"
app:argType="string"
app:nullable="false" />
</action>
<fragment
android:id="@+id/storyViewerFragment"
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="string"
app:nullable="true" />
<argument
android:name="username"
app:argType="string"
app:nullable="true" />
</fragment>
</navigation>

2
app/src/main/res/values/strings.xml

@ -231,6 +231,7 @@
<string name="crash_title">App crashed</string>
<string name="crash_descr">Oops.. the app crashed, but don\'t worry you can send error report to the developer to help him fix the issue. (:</string>
<string name="action_notif">Activity</string>
<string name="action_archive">Story archive</string>
<string name="license" translatable="false">Copyright (C) 2019 AWAiS\nCopyright (C) 2020 Austin Huang, Ammar Githam\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. See https://www.gnu.org/licenses/.</string>
<string name="liability" translatable="false">This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.</string>
<string name="select_picture">Select Picture</string>
@ -321,6 +322,7 @@
<string name="delete">Delete</string>
<string name="comment">Comment</string>
<string name="layout">Layout</string>
<string name="feed_stories">Feed stories</string>
<string name="opening_post">Opening post...</string>
<string name="share">Share</string>
<string name="layout_style">Layout style</string>

Loading…
Cancel
Save