diff --git a/.github/ISSUE_TEMPLATE/ban_report.md b/.github/ISSUE_TEMPLATE/ban_report.md index 99d4d33c..180ca957 100644 --- a/.github/ISSUE_TEMPLATE/ban_report.md +++ b/.github/ISSUE_TEMPLATE/ban_report.md @@ -18,8 +18,8 @@ assignees: 'austinhuang0131' ## Answer honestly. Check accordingly to your situation. - [ ] I had prior rule violations on Instagram, specifically: -- [ ] I have admitted the use of InstaGrabber on Instagram. -- [ ] I have admitted the use of InstaGrabber to a friend who uses Instagram. +- [ ] I have admitted the use of Barinsta on Instagram. +- [ ] I have admitted the use of Barinsta to a friend who uses Instagram. - [ ] I have modified the source code of the app that I use, other than what is present in this repo. Specifically: ## Describe your case, including your usage pattern, but without private information. diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 2c933b53..a3abf780 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -6,10 +6,10 @@ labels: bug assignees: '' --- - + - [ ] My app is on the latest version. I understand that any other version is not supported. -- [ ] I have read [the FAQ](https://instagrabber.austinhuang.me/faq). +- [ ] I have read [the FAQ](https://barinsta.austinhuang.me/en/latest/faq). ## Environment diff --git a/.github/ISSUE_TEMPLATE/questions.md b/.github/ISSUE_TEMPLATE/questions.md index fcaaf0d5..aff97d3e 100644 --- a/.github/ISSUE_TEMPLATE/questions.md +++ b/.github/ISSUE_TEMPLATE/questions.md @@ -7,7 +7,13 @@ assignees: '' --- diff --git a/app/build.gradle b/app/build.gradle index a54f0eaa..fbd08223 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -60,11 +60,13 @@ dependencies { def appcompat_version = "1.2.0" def nav_version = '2.3.2' + def exoplayer_version = '2.12.0' implementation 'com.google.android.material:material:1.3.0-beta01' - implementation 'com.google.android.exoplayer:exoplayer-core:2.12.0' - implementation 'com.google.android.exoplayer:exoplayer-ui:2.12.0' + implementation "com.google.android.exoplayer:exoplayer-core:$exoplayer_version" + implementation "com.google.android.exoplayer:exoplayer-dash:$exoplayer_version" + implementation "com.google.android.exoplayer:exoplayer-ui:$exoplayer_version" implementation "androidx.appcompat:appcompat:$appcompat_version" implementation "androidx.appcompat:appcompat-resources:$appcompat_version" diff --git a/app/src/main/java/awais/instagrabber/adapters/FeedAdapterV2.java b/app/src/main/java/awais/instagrabber/adapters/FeedAdapterV2.java index 8e3a5a2c..4847fe3c 100644 --- a/app/src/main/java/awais/instagrabber/adapters/FeedAdapterV2.java +++ b/app/src/main/java/awais/instagrabber/adapters/FeedAdapterV2.java @@ -25,6 +25,7 @@ import awais.instagrabber.databinding.ItemFeedVideoBinding; import awais.instagrabber.models.FeedModel; import awais.instagrabber.models.PostsLayoutPreferences; import awais.instagrabber.models.enums.MediaItemType; +import awais.instagrabber.utils.TextUtils; public final class FeedAdapterV2 extends ListAdapter { private static final String TAG = "FeedAdapterV2"; @@ -46,7 +47,13 @@ public final class FeedAdapterV2 extends ListAdapter storyModels) { if (storyModels != null && !storyModels.isEmpty()) { - hashtagDetailsBinding.mainHashtagImage.setStoriesBorder(); + hashtagDetailsBinding.mainHashtagImage.setStoriesBorder(1); hasStories = true; } else { hasStories = false; diff --git a/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java b/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java index d978a614..4612e150 100644 --- a/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/LocationFragment.java @@ -563,7 +563,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR @Override public void onSuccess(final List storyModels) { if (storyModels != null && !storyModels.isEmpty()) { - locationDetailsBinding.mainLocationImage.setStoriesBorder(); + locationDetailsBinding.mainLocationImage.setStoriesBorder(1); hasStories = true; } storiesFetching = false; diff --git a/app/src/main/java/awais/instagrabber/fragments/StoryListViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/StoryListViewerFragment.java index dd851198..2a3cf2fe 100644 --- a/app/src/main/java/awais/instagrabber/fragments/StoryListViewerFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/StoryListViewerFragment.java @@ -197,6 +197,7 @@ public final class StoryListViewerFragment extends Fragment implements SwipeRefr @Override public void onSuccess(final List result) { feedStoriesViewModel.getList().postValue(result); + adapter.submitList(result); binding.swipeRefreshLayout.setRefreshing(false); } diff --git a/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java b/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java index fed3ba75..4d8fd604 100644 --- a/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.java @@ -50,6 +50,7 @@ import com.facebook.imagepipeline.request.ImageRequestBuilder; import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.SimpleExoPlayer; +import com.google.android.exoplayer2.source.dash.DashMediaSource; import com.google.android.exoplayer2.source.LoadEventInfo; import com.google.android.exoplayer2.source.MediaLoadData; import com.google.android.exoplayer2.source.MediaSource; @@ -134,7 +135,7 @@ public class StoryViewerFragment extends Fragment { private MenuItem menuDm; private SimpleExoPlayer player; private boolean isHashtag, isLoc; - private String highlight; + private String highlight, actionBarTitle; private boolean fetching = false, sticking = false, shouldRefresh = true; private int currentFeedStoryIndex; private double sliderValue; @@ -256,6 +257,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(); @@ -646,6 +656,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); @@ -681,6 +692,7 @@ public class StoryViewerFragment extends Fragment { final FeedStoryModel model = models.get(currentFeedStoryIndex); currentStoryMediaId = model.getStoryMediaId(); currentStoryUsername = model.getProfileModel().getUsername(); + if (model.isLive()) live = model.getFirstStoryModel(); } } else if (!TextUtils.isEmpty(fragmentArgs.getProfileId()) && !TextUtils.isEmpty(fragmentArgs.getUsername())) { currentStoryMediaId = fragmentArgs.getProfileId(); @@ -692,12 +704,14 @@ public class StoryViewerFragment extends Fragment { if (isHighlight) { final ActionBar actionBar = fragmentActivity.getSupportActionBar(); if (actionBar != null) { + actionBarTitle = highlight; actionBar.setTitle(highlight); } } else if (hasUsername) { currentStoryUsername = currentStoryUsername.replace("@", ""); final ActionBar actionBar = fragmentActivity.getSupportActionBar(); if (actionBar != null) { + actionBarTitle = currentStoryUsername; actionBar.setTitle(currentStoryUsername); } } @@ -755,12 +769,14 @@ 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)); + else storiesService.getUserStory(currentStoryMediaId, + currentStoryUsername, + isLoc, + isHashtag, + isHighlight, + storyCallback); + } } private void refreshStory() { @@ -784,51 +800,55 @@ 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 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(); @@ -954,6 +974,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, + final LoadEventInfo loadEventInfo, + final MediaLoadData mediaLoadData) { + binding.progressView.setVisibility(View.GONE); + } + + @Override + public void onLoadStarted(final int windowIndex, + @Nullable final MediaSource.MediaPeriodId mediaPeriodId, + final LoadEventInfo loadEventInfo, + final MediaLoadData mediaLoadData) { + binding.progressView.setVisibility(View.VISIBLE); + } + + @Override + public void onLoadCanceled(final int windowIndex, + @Nullable final MediaSource.MediaPeriodId mediaPeriodId, + final LoadEventInfo loadEventInfo, + final MediaLoadData mediaLoadData) { + binding.progressView.setVisibility(View.GONE); + } + + @Override + public void onLoadError(final int windowIndex, + @Nullable final MediaSource.MediaPeriodId mediaPeriodId, + final LoadEventInfo loadEventInfo, + final MediaLoadData mediaLoadData, + final IOException error, + final boolean wasCanceled) { + binding.progressView.setVisibility(View.GONE); + } + }); + player.setMediaSource(mediaSource); + player.prepare(); + + binding.playerView.setOnClickListener(v -> { + if (player != null) { + if (player.getPlaybackState() == Player.STATE_ENDED) player.seekTo(0); + player.setPlayWhenReady(player.getPlaybackState() == Player.STATE_ENDED || !player.isPlaying()); + } + }); + } + + private void openProfile(final String username) { final ActionBar actionBar = fragmentActivity.getSupportActionBar(); if (actionBar != null) { diff --git a/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java b/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java index 832977c8..2cbe8d59 100644 --- a/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java @@ -79,6 +79,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 FeedModel feedModel, final View profilePicView, final View mainPostImage) { @@ -353,20 +368,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); @@ -390,6 +391,7 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre @Override public void onSuccess(final List result) { feedStoriesViewModel.getList().postValue(result); + feedStoriesAdapter.submitList(result); storiesFetching = false; if (storyListMenu != null) storyListMenu.setVisible(true); updateSwipeRefreshState(); diff --git a/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java b/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java index d5ab821d..d5a85e34 100644 --- a/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java @@ -869,7 +869,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe @Override public void onSuccess(final List storyModels) { if (storyModels != null && !storyModels.isEmpty()) { - profileDetailsBinding.mainProfileImage.setStoriesBorder(); + profileDetailsBinding.mainProfileImage.setStoriesBorder(1); hasStories = true; } } diff --git a/app/src/main/java/awais/instagrabber/models/FeedStoryModel.java b/app/src/main/java/awais/instagrabber/models/FeedStoryModel.java index 01ffac76..74bac68e 100755 --- a/app/src/main/java/awais/instagrabber/models/FeedStoryModel.java +++ b/app/src/main/java/awais/instagrabber/models/FeedStoryModel.java @@ -14,17 +14,21 @@ public final class FeedStoryModel implements Serializable { private final ProfileModel 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 ProfileModel 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() { @@ -63,4 +67,12 @@ public final class FeedStoryModel implements Serializable { public void setFullyRead(final boolean fullyRead) { this.fullyRead = fullyRead; } + + public boolean isLive() { + return isLive; + } + + public boolean isBestie() { + return isBestie; + } } \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/models/enums/MediaItemType.java b/app/src/main/java/awais/instagrabber/models/enums/MediaItemType.java index 93c6f60e..616ae58f 100755 --- a/app/src/main/java/awais/instagrabber/models/enums/MediaItemType.java +++ b/app/src/main/java/awais/instagrabber/models/enums/MediaItemType.java @@ -14,7 +14,10 @@ public enum MediaItemType implements Serializable { @SerializedName("8") MEDIA_TYPE_SLIDER(8), @SerializedName("11") - MEDIA_TYPE_VOICE(11); + MEDIA_TYPE_VOICE(11), + // 5 is arbitrary + @SerializedName("5") + MEDIA_TYPE_LIVE(5); private final int id; private static final Map map = new HashMap<>(); diff --git a/app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java b/app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java index e526ae99..577461a3 100644 --- a/app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java +++ b/app/src/main/java/awais/instagrabber/utils/ResponseBodyUtils.java @@ -1034,4 +1034,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").getString("pk"), + false); + model.setVideoUrl(data.getString("dash_playback_url")); + return model; + } } diff --git a/app/src/main/java/awais/instagrabber/utils/Utils.java b/app/src/main/java/awais/instagrabber/utils/Utils.java index 528bac59..e8850927 100644 --- a/app/src/main/java/awais/instagrabber/utils/Utils.java +++ b/app/src/main/java/awais/instagrabber/utils/Utils.java @@ -140,11 +140,6 @@ public final class Utils { return mimeType.toLowerCase(); } - public static void errorFinish(@NonNull final Activity activity) { - Toast.makeText(activity, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); - activity.finish(); - } - public static SimpleCache getSimpleCacheInstance(final Context context) { if (context == null) { return null; diff --git a/app/src/main/java/awais/instagrabber/webservices/StoriesService.java b/app/src/main/java/awais/instagrabber/webservices/StoriesService.java index cff5b2fc..815e39bc 100644 --- a/app/src/main/java/awais/instagrabber/webservices/StoriesService.java +++ b/app/src/main/java/awais/instagrabber/webservices/StoriesService.java @@ -121,12 +121,32 @@ 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, profileModel, fullyRead, timestamp, firstStoryModel, mediaCount)); + feedStoryModels.add(new FeedStoryModel(id, profileModel, 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 user = node.getJSONObject("broadcast_owner"); + final ProfileModel profileModel = new ProfileModel(false, false, false, + user.getString("pk"), + user.getString("username"), + null, null, null, + user.getString("profile_pic_url"), + null, 0, 0, 0, false, false, false, false, false); + 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, profileModel, false, timestamp, firstStoryModel, 1, true, false)); } callback.onSuccess(sort(feedStoryModels)); } catch (JSONException e) {