1
0
mirror of https://github.com/KokaKiwi/BarInsta synced 2024-11-22 14:47:29 +00:00

Merge branch 'master' into bottombar_redesign

This commit is contained in:
Austin Huang 2021-04-02 09:35:25 -04:00 committed by GitHub
commit 1774ce8af0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 514 additions and 139 deletions

View File

@ -105,7 +105,7 @@ dependencies {
def nav_version = '2.3.4' def nav_version = '2.3.4'
def exoplayer_version = '2.13.2' def exoplayer_version = '2.13.2'
implementation 'com.google.android.material:material:1.4.0-alpha01' implementation 'com.google.android.material:material:1.4.0-alpha02'
implementation "com.google.android.exoplayer:exoplayer-core:$exoplayer_version" implementation "com.google.android.exoplayer:exoplayer-core:$exoplayer_version"
implementation "com.google.android.exoplayer:exoplayer-dash:$exoplayer_version" implementation "com.google.android.exoplayer:exoplayer-dash:$exoplayer_version"

View File

@ -47,6 +47,7 @@ import androidx.navigation.ui.NavigationUI;
import com.google.android.material.appbar.AppBarLayout; import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.appbar.CollapsingToolbarLayout; import com.google.android.material.appbar.CollapsingToolbarLayout;
import com.google.android.material.badge.BadgeDrawable;
import com.google.android.material.behavior.HideBottomViewOnScrollBehavior; import com.google.android.material.behavior.HideBottomViewOnScrollBehavior;
import com.google.android.material.bottomnavigation.BottomNavigationView; import com.google.android.material.bottomnavigation.BottomNavigationView;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
@ -83,6 +84,7 @@ import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils; import awais.instagrabber.utils.Utils;
import awais.instagrabber.utils.emoji.EmojiParser; import awais.instagrabber.utils.emoji.EmojiParser;
import awais.instagrabber.viewmodels.AppStateViewModel; import awais.instagrabber.viewmodels.AppStateViewModel;
import awais.instagrabber.viewmodels.DirectInboxViewModel;
import awais.instagrabber.webservices.SearchService; import awais.instagrabber.webservices.SearchService;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.Callback; import retrofit2.Callback;
@ -170,6 +172,7 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
initEmojiCompat(); initEmojiCompat();
searchService = SearchService.getInstance(); searchService = SearchService.getInstance();
// initDmService(); // initDmService();
initDmUnreadCount();
} }
private void initDmService() { private void initDmService() {
@ -179,6 +182,16 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
DMSyncAlarmReceiver.setAlarm(this); DMSyncAlarmReceiver.setAlarm(this);
} }
private void initDmUnreadCount() {
if (!isLoggedIn) return;
final DirectInboxViewModel directInboxViewModel = new ViewModelProvider(this).get(DirectInboxViewModel.class);
directInboxViewModel.getUnseenCount().observe(this, unseenCountResource -> {
if (unseenCountResource == null) return;
final Integer unseenCount = unseenCountResource.data;
setNavBarDMUnreadCountBadge(unseenCount == null ? 0 : unseenCount);
});
}
@Override @Override
public boolean onCreateOptionsMenu(final Menu menu) { public boolean onCreateOptionsMenu(final Menu menu) {
getMenuInflater().inflate(R.menu.main_menu, menu); getMenuInflater().inflate(R.menu.main_menu, menu);
@ -896,4 +909,19 @@ public class MainActivity extends BaseLanguageActivity implements FragmentManage
public boolean isNavRootInCurrentTabs(@IdRes final int navRootId) { public boolean isNavRootInCurrentTabs(@IdRes final int navRootId) {
return showBottomViewDestinations.stream().anyMatch(id -> id == navRootId); return showBottomViewDestinations.stream().anyMatch(id -> id == navRootId);
} }
private void setNavBarDMUnreadCountBadge(final int unseenCount) {
final BadgeDrawable badge = binding.bottomNavView.getOrCreateBadge(R.id.direct_messages_nav_graph);
if (badge == null) return;
if (unseenCount == 0) {
badge.setVisible(false);
badge.clearNumber();
return;
}
if (badge.getVerticalOffset() != 10) {
badge.setVerticalOffset(10);
}
badge.setNumber(unseenCount);
badge.setVisible(true);
}
} }

View File

@ -17,6 +17,7 @@ import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.function.Function;
import awais.instagrabber.adapters.viewholder.directmessages.DirectItemActionLogViewHolder; import awais.instagrabber.adapters.viewholder.directmessages.DirectItemActionLogViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectItemAnimatedMediaViewHolder; import awais.instagrabber.adapters.viewholder.directmessages.DirectItemAnimatedMediaViewHolder;
@ -34,6 +35,7 @@ import awais.instagrabber.adapters.viewholder.directmessages.DirectItemTextViewH
import awais.instagrabber.adapters.viewholder.directmessages.DirectItemVideoCallEventViewHolder; import awais.instagrabber.adapters.viewholder.directmessages.DirectItemVideoCallEventViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectItemViewHolder; import awais.instagrabber.adapters.viewholder.directmessages.DirectItemViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectItemVoiceMediaViewHolder; import awais.instagrabber.adapters.viewholder.directmessages.DirectItemVoiceMediaViewHolder;
import awais.instagrabber.adapters.viewholder.directmessages.DirectItemXmaViewHolder;
import awais.instagrabber.customviews.emoji.Emoji; import awais.instagrabber.customviews.emoji.Emoji;
import awais.instagrabber.databinding.LayoutDmActionLogBinding; import awais.instagrabber.databinding.LayoutDmActionLogBinding;
import awais.instagrabber.databinding.LayoutDmAnimatedMediaBinding; import awais.instagrabber.databinding.LayoutDmAnimatedMediaBinding;
@ -207,6 +209,11 @@ public final class DirectItemsAdapter extends RecyclerView.Adapter<RecyclerView.
final LayoutDmRavenMediaBinding binding = LayoutDmRavenMediaBinding.inflate(layoutInflater, baseBinding.message, false); final LayoutDmRavenMediaBinding binding = LayoutDmRavenMediaBinding.inflate(layoutInflater, baseBinding.message, false);
return new DirectItemRavenMediaViewHolder(baseBinding, binding, currentUser, thread, callback); return new DirectItemRavenMediaViewHolder(baseBinding, binding, currentUser, thread, callback);
} }
case XMA: {
final LayoutDmAnimatedMediaBinding binding = LayoutDmAnimatedMediaBinding.inflate(layoutInflater, baseBinding.message, false);
return new DirectItemXmaViewHolder(baseBinding, binding, currentUser, thread, callback);
}
case UNKNOWN:
default: { default: {
final LayoutDmTextBinding binding = LayoutDmTextBinding.inflate(layoutInflater, baseBinding.message, false); final LayoutDmTextBinding binding = LayoutDmTextBinding.inflate(layoutInflater, baseBinding.message, false);
return new DirectItemDefaultViewHolder(baseBinding, binding, currentUser, thread, callback); return new DirectItemDefaultViewHolder(baseBinding, binding, currentUser, thread, callback);
@ -240,7 +247,11 @@ public final class DirectItemsAdapter extends RecyclerView.Adapter<RecyclerView.
if (itemOrHeader.isHeader()) { if (itemOrHeader.isHeader()) {
return -1; return -1;
} }
return itemOrHeader.item.getItemType().getId(); final DirectItemType itemType = itemOrHeader.item.getItemType();
if (itemType == null) {
return 0;
}
return itemType.getId();
} }
@Override @Override
@ -394,7 +405,7 @@ public final class DirectItemsAdapter extends RecyclerView.Adapter<RecyclerView.
void onReactionClick(DirectItem item, int position); void onReactionClick(DirectItem item, int position);
void onOptionSelect(DirectItem item, @IdRes int itemId); void onOptionSelect(DirectItem item, @IdRes int itemId, final Function<DirectItem, Void> callback);
} }
public interface DirectItemInternalLongClickListener { public interface DirectItemInternalLongClickListener {

View File

@ -8,8 +8,13 @@ import androidx.core.util.Pair;
import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.ItemTouchHelper;
import com.facebook.drawee.backends.pipeline.Fresco; import com.facebook.drawee.backends.pipeline.Fresco;
import com.google.common.collect.ImmutableList;
import java.util.List;
import awais.instagrabber.R;
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback; import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
import awais.instagrabber.customviews.DirectItemContextMenu;
import awais.instagrabber.databinding.LayoutDmAnimatedMediaBinding; import awais.instagrabber.databinding.LayoutDmAnimatedMediaBinding;
import awais.instagrabber.databinding.LayoutDmBaseBinding; import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.repositories.responses.AnimatedMediaFixedHeight; import awais.instagrabber.repositories.responses.AnimatedMediaFixedHeight;
@ -19,6 +24,7 @@ import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectItemAnimatedMedia; import awais.instagrabber.repositories.responses.directmessages.DirectItemAnimatedMedia;
import awais.instagrabber.repositories.responses.directmessages.DirectThread; import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.utils.NumberUtils; import awais.instagrabber.utils.NumberUtils;
import awais.instagrabber.utils.Utils;
public class DirectItemAnimatedMediaViewHolder extends DirectItemViewHolder { public class DirectItemAnimatedMediaViewHolder extends DirectItemViewHolder {
@ -65,4 +71,14 @@ public class DirectItemAnimatedMediaViewHolder extends DirectItemViewHolder {
public int getSwipeDirection() { public int getSwipeDirection() {
return ItemTouchHelper.ACTION_STATE_IDLE; return ItemTouchHelper.ACTION_STATE_IDLE;
} }
@Override
protected List<DirectItemContextMenu.MenuItem> getLongClickOptions() {
return ImmutableList.of(
new DirectItemContextMenu.MenuItem(R.id.detail, R.string.dms_inbox_giphy, item -> {
Utils.openURL(itemView.getContext(), "https://giphy.com/gifs/" + item.getAnimatedMedia().getId());
return null;
})
);
}
} }

View File

@ -24,6 +24,7 @@ public class DirectItemDefaultViewHolder extends DirectItemViewHolder {
final DirectItemCallback callback) { final DirectItemCallback callback) {
super(baseBinding, currentUser, thread, callback); super(baseBinding, currentUser, thread, callback);
this.binding = binding; this.binding = binding;
setItemView(binding.getRoot());
} }
@Override @Override
@ -32,6 +33,11 @@ public class DirectItemDefaultViewHolder extends DirectItemViewHolder {
binding.tvMessage.setText(context.getText(R.string.dms_inbox_raven_message_unknown)); binding.tvMessage.setText(context.getText(R.string.dms_inbox_raven_message_unknown));
} }
@Override
protected boolean showBackground() {
return true;
}
@Override @Override
protected boolean allowLongClick() { protected boolean allowLongClick() {
return false; return false;

View File

@ -5,7 +5,13 @@ import android.view.ViewGroup;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import com.google.common.collect.ImmutableList;
import java.util.List;
import awais.instagrabber.R;
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback; import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
import awais.instagrabber.customviews.DirectItemContextMenu;
import awais.instagrabber.databinding.LayoutDmBaseBinding; import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmLinkBinding; import awais.instagrabber.databinding.LayoutDmLinkBinding;
import awais.instagrabber.repositories.responses.User; import awais.instagrabber.repositories.responses.User;
@ -14,6 +20,7 @@ import awais.instagrabber.repositories.responses.directmessages.DirectItemLink;
import awais.instagrabber.repositories.responses.directmessages.DirectItemLinkContext; import awais.instagrabber.repositories.responses.directmessages.DirectItemLinkContext;
import awais.instagrabber.repositories.responses.directmessages.DirectThread; import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
public class DirectItemLinkViewHolder extends DirectItemViewHolder { public class DirectItemLinkViewHolder extends DirectItemViewHolder {
@ -80,4 +87,16 @@ public class DirectItemLinkViewHolder extends DirectItemViewHolder {
protected boolean showBackground() { protected boolean showBackground() {
return true; return true;
} }
@Override
protected List<DirectItemContextMenu.MenuItem> getLongClickOptions() {
return ImmutableList.of(
new DirectItemContextMenu.MenuItem(R.id.copy, R.string.copy, item -> {
final DirectItemLink link = item.getLink();
if (link == null || TextUtils.isEmpty(link.getText())) return null;
Utils.copyText(itemView.getContext(), link.getText());
return null;
})
);
}
} }

View File

@ -12,11 +12,14 @@ import androidx.recyclerview.widget.ItemTouchHelper;
import com.facebook.drawee.drawable.ScalingUtils; import com.facebook.drawee.drawable.ScalingUtils;
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder; import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
import com.facebook.drawee.generic.RoundingParams; import com.facebook.drawee.generic.RoundingParams;
import com.google.common.collect.ImmutableList;
import java.util.List;
import java.util.Objects; import java.util.Objects;
import awais.instagrabber.R; import awais.instagrabber.R;
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback; import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
import awais.instagrabber.customviews.DirectItemContextMenu;
import awais.instagrabber.databinding.LayoutDmBaseBinding; import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmMediaShareBinding; import awais.instagrabber.databinding.LayoutDmMediaShareBinding;
import awais.instagrabber.models.enums.DirectItemType; import awais.instagrabber.models.enums.DirectItemType;
@ -30,6 +33,7 @@ import awais.instagrabber.repositories.responses.directmessages.DirectItemFelixS
import awais.instagrabber.repositories.responses.directmessages.DirectThread; import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.utils.NumberUtils; import awais.instagrabber.utils.NumberUtils;
import awais.instagrabber.utils.ResponseBodyUtils; import awais.instagrabber.utils.ResponseBodyUtils;
import awais.instagrabber.utils.Utils;
public class DirectItemMediaShareViewHolder extends DirectItemViewHolder { public class DirectItemMediaShareViewHolder extends DirectItemViewHolder {
private static final String TAG = DirectItemMediaShareViewHolder.class.getSimpleName(); private static final String TAG = DirectItemMediaShareViewHolder.class.getSimpleName();
@ -38,6 +42,7 @@ public class DirectItemMediaShareViewHolder extends DirectItemViewHolder {
private final RoundingParams incomingRoundingParams; private final RoundingParams incomingRoundingParams;
private final RoundingParams outgoingRoundingParams; private final RoundingParams outgoingRoundingParams;
private DirectItemType itemType; private DirectItemType itemType;
private Caption caption;
public DirectItemMediaShareViewHolder(@NonNull final LayoutDmBaseBinding baseBinding, public DirectItemMediaShareViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmMediaShareBinding binding, @NonNull final LayoutDmMediaShareBinding binding,
@ -113,7 +118,7 @@ public class DirectItemMediaShareViewHolder extends DirectItemViewHolder {
} }
private void setupCaption(@NonNull final Media media) { private void setupCaption(@NonNull final Media media) {
final Caption caption = media.getCaption(); caption = media.getCaption();
if (caption != null) { if (caption != null) {
binding.caption.setVisibility(View.VISIBLE); binding.caption.setVisibility(View.VISIBLE);
binding.caption.setText(caption.getText()); binding.caption.setText(caption.getText());
@ -177,4 +182,16 @@ public class DirectItemMediaShareViewHolder extends DirectItemViewHolder {
} }
return super.getSwipeDirection(); return super.getSwipeDirection();
} }
@Override
protected List<DirectItemContextMenu.MenuItem> getLongClickOptions() {
final ImmutableList.Builder<DirectItemContextMenu.MenuItem> builder = ImmutableList.builder();
if (caption != null && !TextUtils.isEmpty(caption.getText())) {
builder.add(new DirectItemContextMenu.MenuItem(R.id.copy, R.string.copy_caption, item -> {
Utils.copyText(itemView.getContext(), caption.getText());
return null;
}));
}
return builder.build();
}
} }

View File

@ -8,13 +8,16 @@ import androidx.constraintlayout.widget.ConstraintLayout;
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder; import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
import com.facebook.drawee.generic.RoundingParams; import com.facebook.drawee.generic.RoundingParams;
import com.google.common.collect.ImmutableList;
import java.util.List;
import awais.instagrabber.R; import awais.instagrabber.R;
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback; import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
import awais.instagrabber.customviews.DirectItemContextMenu;
import awais.instagrabber.databinding.LayoutDmBaseBinding; import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmReelShareBinding; import awais.instagrabber.databinding.LayoutDmReelShareBinding;
import awais.instagrabber.models.enums.MediaItemType; import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.repositories.responses.ImageVersions2;
import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.User; import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItem; import awais.instagrabber.repositories.responses.directmessages.DirectItem;
@ -22,10 +25,12 @@ import awais.instagrabber.repositories.responses.directmessages.DirectItemReelSh
import awais.instagrabber.repositories.responses.directmessages.DirectThread; import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.utils.ResponseBodyUtils; import awais.instagrabber.utils.ResponseBodyUtils;
import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
public class DirectItemReelShareViewHolder extends DirectItemViewHolder { public class DirectItemReelShareViewHolder extends DirectItemViewHolder {
private final LayoutDmReelShareBinding binding; private final LayoutDmReelShareBinding binding;
private String type;
public DirectItemReelShareViewHolder(@NonNull final LayoutDmBaseBinding baseBinding, public DirectItemReelShareViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmReelShareBinding binding, @NonNull final LayoutDmReelShareBinding binding,
@ -40,7 +45,7 @@ public class DirectItemReelShareViewHolder extends DirectItemViewHolder {
@Override @Override
public void bindItem(final DirectItem item, final MessageDirection messageDirection) { public void bindItem(final DirectItem item, final MessageDirection messageDirection) {
final DirectItemReelShare reelShare = item.getReelShare(); final DirectItemReelShare reelShare = item.getReelShare();
final String type = reelShare.getType(); type = reelShare.getType();
if (type == null) return; if (type == null) return;
final boolean isSelf = isSelf(item); final boolean isSelf = isSelf(item);
final Media media = reelShare.getMedia(); final Media media = reelShare.getMedia();
@ -170,4 +175,20 @@ public class DirectItemReelShareViewHolder extends DirectItemViewHolder {
protected boolean canForward() { protected boolean canForward() {
return false; return false;
} }
@Override
protected List<DirectItemContextMenu.MenuItem> getLongClickOptions() {
final ImmutableList.Builder<DirectItemContextMenu.MenuItem> builder = ImmutableList.builder();
if (type != null && type.equals("reply")) {
builder.add(new DirectItemContextMenu.MenuItem(R.id.copy, R.string.copy_reply, item -> {
final DirectItemReelShare reelShare = item.getReelShare();
if (reelShare == null) return null;
final String text = reelShare.getText();
if (TextUtils.isEmpty(text)) return null;
Utils.copyText(itemView.getContext(), text);
return null;
}));
}
return builder.build();
}
} }

View File

@ -2,12 +2,20 @@ package awais.instagrabber.adapters.viewholder.directmessages;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import com.google.common.collect.ImmutableList;
import java.util.List;
import awais.instagrabber.R;
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback; import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
import awais.instagrabber.customviews.DirectItemContextMenu;
import awais.instagrabber.databinding.LayoutDmBaseBinding; import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmTextBinding; import awais.instagrabber.databinding.LayoutDmTextBinding;
import awais.instagrabber.repositories.responses.User; import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItem; import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectThread; import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
public class DirectItemTextViewHolder extends DirectItemViewHolder { public class DirectItemTextViewHolder extends DirectItemViewHolder {
@ -35,4 +43,15 @@ public class DirectItemTextViewHolder extends DirectItemViewHolder {
protected boolean showBackground() { protected boolean showBackground() {
return true; return true;
} }
@Override
protected List<DirectItemContextMenu.MenuItem> getLongClickOptions() {
return ImmutableList.of(
new DirectItemContextMenu.MenuItem(R.id.copy, R.string.copy, item -> {
if (TextUtils.isEmpty(item.getText())) return null;
Utils.copyText(itemView.getContext(), item.getText());
return null;
})
);
}
} }

View File

@ -134,7 +134,7 @@ public abstract class DirectItemViewHolder extends RecyclerView.ViewHolder imple
containerLayoutParams.setMarginStart(0); containerLayoutParams.setMarginStart(0);
containerLayoutParams.setMarginEnd(0); containerLayoutParams.setMarginEnd(0);
} }
if (itemType == DirectItemType.TEXT || itemType == DirectItemType.LINK) { if (itemType == DirectItemType.TEXT || itemType == DirectItemType.LINK || itemType == DirectItemType.UNKNOWN) {
binding.messageInfo.setPadding(0, 0, dmRadius, dmRadiusSmall); binding.messageInfo.setPadding(0, 0, dmRadius, dmRadiusSmall);
} else { } else {
if (showMessageInfo()) { if (showMessageInfo()) {
@ -543,22 +543,13 @@ public abstract class DirectItemViewHolder extends RecyclerView.ViewHolder imple
if (thread.getInputMode() != 1 && messageDirection == MessageDirection.OUTGOING) { if (thread.getInputMode() != 1 && messageDirection == MessageDirection.OUTGOING) {
builder.add(new DirectItemContextMenu.MenuItem(R.id.unsend, R.string.dms_inbox_unsend)); builder.add(new DirectItemContextMenu.MenuItem(R.id.unsend, R.string.dms_inbox_unsend));
} }
final DirectItemType itemType = item.getItemType();
switch (itemType) {
case ANIMATED_MEDIA:
builder.add(new DirectItemContextMenu.MenuItem(R.id.detail, R.string.dms_inbox_giphy));
break;
case VOICE_MEDIA:
builder.add(new DirectItemContextMenu.MenuItem(R.id.detail, R.string.action_download));
break;
}
final boolean showReactions = thread.getInputMode() != 1 && allowReaction(); final boolean showReactions = thread.getInputMode() != 1 && allowReaction();
final ImmutableList<DirectItemContextMenu.MenuItem> menuItems = builder.build(); final ImmutableList<DirectItemContextMenu.MenuItem> menuItems = builder.build();
if (!showReactions && menuItems.isEmpty()) return; if (!showReactions && menuItems.isEmpty()) return;
final DirectItemContextMenu menu = new DirectItemContextMenu(itemView.getContext(), showReactions, menuItems); final DirectItemContextMenu menu = new DirectItemContextMenu(itemView.getContext(), showReactions, menuItems);
menu.setOnDismissListener(() -> setSelected(false)); menu.setOnDismissListener(() -> setSelected(false));
menu.setOnReactionClickListener(emoji -> callback.onReaction(item, emoji)); menu.setOnReactionClickListener(emoji -> callback.onReaction(item, emoji));
menu.setOnOptionSelectListener(itemId -> callback.onOptionSelect(item, itemId)); menu.setOnOptionSelectListener((itemId, cb) -> callback.onOptionSelect(item, itemId, cb));
menu.show(itemView, location); menu.show(itemView, location);
} }

View File

@ -12,12 +12,14 @@ import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.source.ProgressiveMediaSource; import com.google.android.exoplayer2.source.ProgressiveMediaSource;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import com.google.common.collect.ImmutableList;
import com.google.common.primitives.Floats; import com.google.common.primitives.Floats;
import java.util.List; import java.util.List;
import awais.instagrabber.R; import awais.instagrabber.R;
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback; import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
import awais.instagrabber.customviews.DirectItemContextMenu;
import awais.instagrabber.databinding.LayoutDmBaseBinding; import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.databinding.LayoutDmVoiceMediaBinding; import awais.instagrabber.databinding.LayoutDmVoiceMediaBinding;
import awais.instagrabber.repositories.responses.Audio; import awais.instagrabber.repositories.responses.Audio;
@ -174,6 +176,13 @@ public class DirectItemVoiceMediaViewHolder extends DirectItemViewHolder {
return false; return false;
} }
@Override
protected List<DirectItemContextMenu.MenuItem> getLongClickOptions() {
return ImmutableList.of(
new DirectItemContextMenu.MenuItem(R.id.download, R.string.action_download)
);
}
private static class AudioItemState { private static class AudioItemState {
private boolean prepared; private boolean prepared;

View File

@ -0,0 +1,69 @@
package awais.instagrabber.adapters.viewholder.directmessages;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.core.util.Pair;
import androidx.recyclerview.widget.ItemTouchHelper;
import com.facebook.drawee.backends.pipeline.Fresco;
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
import awais.instagrabber.databinding.LayoutDmAnimatedMediaBinding;
import awais.instagrabber.databinding.LayoutDmBaseBinding;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectItemXma;
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
import awais.instagrabber.utils.NumberUtils;
public class DirectItemXmaViewHolder extends DirectItemViewHolder {
private final LayoutDmAnimatedMediaBinding binding;
public DirectItemXmaViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
@NonNull final LayoutDmAnimatedMediaBinding binding,
final User currentUser,
final DirectThread thread,
final DirectItemCallback callback) {
super(baseBinding, currentUser, thread, callback);
this.binding = binding;
setItemView(binding.getRoot());
}
@Override
public void bindItem(final DirectItem item, final MessageDirection messageDirection) {
final DirectItemXma xma = item.getXma();
final DirectItemXma.XmaUrlInfo playableUrlInfo = xma.getPlayableUrlInfo();
final DirectItemXma.XmaUrlInfo previewUrlInfo = xma.getPreviewUrlInfo();
if (playableUrlInfo == null && previewUrlInfo == null) {
binding.ivAnimatedMessage.setController(null);
return;
}
final DirectItemXma.XmaUrlInfo urlInfo = playableUrlInfo != null ? playableUrlInfo : previewUrlInfo;
final String url = urlInfo.getUrl();
final Pair<Integer, Integer> widthHeight = NumberUtils.calculateWidthHeight(
urlInfo.getHeight(),
urlInfo.getWidth(),
mediaImageMaxHeight,
mediaImageMaxWidth
);
binding.ivAnimatedMessage.setVisibility(View.VISIBLE);
final ViewGroup.LayoutParams layoutParams = binding.ivAnimatedMessage.getLayoutParams();
final int width = widthHeight.first != null ? widthHeight.first : 0;
final int height = widthHeight.second != null ? widthHeight.second : 0;
layoutParams.width = width;
layoutParams.height = height;
binding.ivAnimatedMessage.requestLayout();
binding.ivAnimatedMessage.setController(Fresco.newDraweeControllerBuilder()
.setUri(url)
.setAutoPlayAnimations(true)
.build());
}
@Override
public int getSwipeDirection() {
return ItemTouchHelper.ACTION_STATE_IDLE;
}
}

View File

@ -29,12 +29,14 @@ import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.util.Pair; import androidx.core.util.Pair;
import java.util.List; import java.util.List;
import java.util.function.Function;
import awais.instagrabber.R; import awais.instagrabber.R;
import awais.instagrabber.animations.RoundedRectRevealOutlineProvider; import awais.instagrabber.animations.RoundedRectRevealOutlineProvider;
import awais.instagrabber.customviews.emoji.Emoji; import awais.instagrabber.customviews.emoji.Emoji;
import awais.instagrabber.customviews.emoji.ReactionsManager; import awais.instagrabber.customviews.emoji.ReactionsManager;
import awais.instagrabber.databinding.LayoutDirectItemOptionsBinding; import awais.instagrabber.databinding.LayoutDirectItemOptionsBinding;
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import static android.view.View.MeasureSpec.makeMeasureSpec; import static android.view.View.MeasureSpec.makeMeasureSpec;
@ -345,7 +347,7 @@ public class DirectItemContextMenu extends PopupWindow {
textView.setText(context.getString(menuItem.getTitleRes())); textView.setText(context.getString(menuItem.getTitleRes()));
textView.setOnClickListener(v -> { textView.setOnClickListener(v -> {
if (onOptionSelectListener != null) { if (onOptionSelectListener != null) {
onOptionSelectListener.onSelect(menuItem.getItemId()); onOptionSelectListener.onSelect(menuItem.getItemId(), menuItem.getCallback());
} }
dismiss(); dismiss();
}); });
@ -397,9 +399,19 @@ public class DirectItemContextMenu extends PopupWindow {
@StringRes @StringRes
private final int titleRes; private final int titleRes;
/**
* Callback function
*/
private final Function<DirectItem, Void> callback;
public MenuItem(@IdRes final int itemId, @StringRes final int titleRes) { public MenuItem(@IdRes final int itemId, @StringRes final int titleRes) {
this(itemId, titleRes, null);
}
public MenuItem(@IdRes final int itemId, @StringRes final int titleRes, @Nullable final Function<DirectItem, Void> callback) {
this.itemId = itemId; this.itemId = itemId;
this.titleRes = titleRes; this.titleRes = titleRes;
this.callback = callback;
} }
public int getItemId() { public int getItemId() {
@ -409,10 +421,14 @@ public class DirectItemContextMenu extends PopupWindow {
public int getTitleRes() { public int getTitleRes() {
return titleRes; return titleRes;
} }
public Function<DirectItem, Void> getCallback() {
return callback;
}
} }
public interface OnOptionSelectListener { public interface OnOptionSelectListener {
void onSelect(int itemId); void onSelect(int itemId, @Nullable Function<DirectItem, Void> callback);
} }
public interface OnReactionClickListener { public interface OnReactionClickListener {

View File

@ -22,6 +22,7 @@ import androidx.work.WorkManager;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -74,11 +75,10 @@ public class PostsRecyclerView extends RecyclerView {
} }
final List<Media> models = mediaViewModel.getList().getValue(); final List<Media> models = mediaViewModel.getList().getValue();
final List<Media> modelsCopy = models == null ? new ArrayList<>() : new ArrayList<>(models); final List<Media> modelsCopy = models == null ? new ArrayList<>() : new ArrayList<>(models);
if (settingsHelper.getBoolean(Constants.TOGGLE_KEYWORD_FILTER)){ if (settingsHelper.getBoolean(Constants.TOGGLE_KEYWORD_FILTER)) {
final ArrayList<String> items = new ArrayList<>(settingsHelper.getStringSet(Constants.KEYWORD_FILTERS)); final ArrayList<String> items = new ArrayList<>(settingsHelper.getStringSet(Constants.KEYWORD_FILTERS));
modelsCopy.addAll(new KeywordsFilterUtils(items).filter(result)); modelsCopy.addAll(new KeywordsFilterUtils(items).filter(result));
} } else {
else {
modelsCopy.addAll(result); modelsCopy.addAll(result);
} }
mediaViewModel.getList().postValue(modelsCopy); mediaViewModel.getList().postValue(modelsCopy);
@ -194,7 +194,9 @@ public class PostsRecyclerView extends RecyclerView {
private void initSelf() { private void initSelf() {
mediaViewModel = new ViewModelProvider(viewModelStoreOwner).get(MediaViewModel.class); mediaViewModel = new ViewModelProvider(viewModelStoreOwner).get(MediaViewModel.class);
mediaViewModel.getList().observe(lifeCycleOwner, list -> { mediaViewModel.getList().observe(lifeCycleOwner, list -> {
if (list.size() > 0) feedAdapter.submitList(list, () -> { if (list.size() <= 0) return;
feedAdapter.submitList(list, () -> {
// postDelayed(this::fetchMoreIfPossible, 1000);
if (!shouldScrollToTop) return; if (!shouldScrollToTop) return;
smoothScrollToPosition(0); smoothScrollToPosition(0);
shouldScrollToTop = false; shouldScrollToTop = false;
@ -217,6 +219,20 @@ public class PostsRecyclerView extends RecyclerView {
dispatchFetchStatus(); dispatchFetchStatus();
} }
private void fetchMoreIfPossible() {
if (!postFetcher.hasMore()) return;
if (feedAdapter.getItemCount() == 0) return;
final LayoutManager layoutManager = getLayoutManager();
if (!(layoutManager instanceof StaggeredGridLayoutManager)) return;
final int[] itemPositions = ((StaggeredGridLayoutManager) layoutManager).findLastCompletelyVisibleItemPositions(null);
final boolean allNoPosition = Arrays.stream(itemPositions).allMatch(position -> position == RecyclerView.NO_POSITION);
if (allNoPosition) return;
final boolean match = Arrays.stream(itemPositions).anyMatch(position -> position == feedAdapter.getItemCount() - 1);
if (!match) return;
postFetcher.fetch();
dispatchFetchStatus();
}
private void initDownloadWorkerListener() { private void initDownloadWorkerListener() {
WorkManager.getInstance(getContext()) WorkManager.getInstance(getContext())
.getWorkInfosByTagLiveData("download") .getWorkInfosByTagLiveData("download")

View File

@ -20,8 +20,6 @@ import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.lifecycle.Observer; import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelStoreOwner;
import androidx.navigation.NavController;
import androidx.navigation.NavDirections; import androidx.navigation.NavDirections;
import androidx.navigation.fragment.NavHostFragment; import androidx.navigation.fragment.NavHostFragment;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
@ -29,7 +27,6 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.google.android.material.badge.BadgeDrawable; import com.google.android.material.badge.BadgeDrawable;
import com.google.android.material.badge.BadgeUtils; import com.google.android.material.badge.BadgeUtils;
import com.google.android.material.bottomnavigation.BottomNavigationView;
import com.google.android.material.snackbar.Snackbar; import com.google.android.material.snackbar.Snackbar;
import java.util.List; import java.util.List;
@ -66,9 +63,7 @@ public class DirectMessageInboxFragment extends Fragment implements SwipeRefresh
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
fragmentActivity = (MainActivity) getActivity(); fragmentActivity = (MainActivity) getActivity();
if (fragmentActivity != null) { if (fragmentActivity != null) {
final NavController navController = NavHostFragment.findNavController(this); viewModel = new ViewModelProvider(fragmentActivity).get(DirectInboxViewModel.class);
final ViewModelStoreOwner viewModelStoreOwner = navController.getViewModelStoreOwner(R.id.direct_messages_nav_graph);
viewModel = new ViewModelProvider(viewModelStoreOwner).get(DirectInboxViewModel.class);
} }
setHasOptionsMenu(true); setHasOptionsMenu(true);
} }
@ -101,6 +96,7 @@ public class DirectMessageInboxFragment extends Fragment implements SwipeRefresh
} }
} }
@SuppressLint("UnsafeExperimentalUsageError")
@Override @Override
public void onPause() { public void onPause() {
super.onPause(); super.onPause();
@ -201,11 +197,6 @@ public class DirectMessageInboxFragment extends Fragment implements SwipeRefresh
break; break;
} }
}); });
viewModel.getUnseenCount().observe(getViewLifecycleOwner(), unseenCountResource -> {
if (unseenCountResource == null) return;
final Integer unseenCount = unseenCountResource.data;
setBottomNavBarBadge(unseenCount == null ? 0 : unseenCount);
});
viewModel.getPendingRequestsTotal().observe(getViewLifecycleOwner(), this::attachPendingRequestsBadge); viewModel.getPendingRequestsTotal().observe(getViewLifecycleOwner(), this::attachPendingRequestsBadge);
} }
@ -213,12 +204,7 @@ public class DirectMessageInboxFragment extends Fragment implements SwipeRefresh
private void attachPendingRequestsBadge(@Nullable final Integer count) { private void attachPendingRequestsBadge(@Nullable final Integer count) {
if (pendingRequestsMenuItem == null) { if (pendingRequestsMenuItem == null) {
final Handler handler = new Handler(); final Handler handler = new Handler();
handler.postDelayed(new Runnable() { handler.postDelayed(() -> attachPendingRequestsBadge(count), 500);
@Override
public void run() {
attachPendingRequestsBadge(count);
}
}, 500);
return; return;
} }
if (pendingRequestTotalBadgeDrawable == null) { if (pendingRequestTotalBadgeDrawable == null) {
@ -277,20 +263,4 @@ public class DirectMessageInboxFragment extends Fragment implements SwipeRefresh
}); });
binding.inboxList.addOnScrollListener(lazyLoader); binding.inboxList.addOnScrollListener(lazyLoader);
} }
private void setBottomNavBarBadge(final int unseenCount) {
final BottomNavigationView bottomNavView = fragmentActivity.getBottomNavView();
final BadgeDrawable badge = bottomNavView.getOrCreateBadge(R.id.direct_messages_nav_graph);
if (badge == null) return;
if (unseenCount == 0) {
badge.setVisible(false);
badge.clearNumber();
return;
}
if (badge.getVerticalOffset() != 10) {
badge.setVerticalOffset(10);
}
badge.setNumber(unseenCount);
badge.setVisible(true);
}
} }

View File

@ -79,11 +79,13 @@ public class DirectMessageSettingsFragment extends Fragment implements ConfirmDi
final DirectMessageSettingsFragmentArgs args = DirectMessageSettingsFragmentArgs.fromBundle(arguments); final DirectMessageSettingsFragmentArgs args = DirectMessageSettingsFragmentArgs.fromBundle(arguments);
final MainActivity fragmentActivity = (MainActivity) requireActivity(); final MainActivity fragmentActivity = (MainActivity) requireActivity();
final AppStateViewModel appStateViewModel = new ViewModelProvider(fragmentActivity).get(AppStateViewModel.class); final AppStateViewModel appStateViewModel = new ViewModelProvider(fragmentActivity).get(AppStateViewModel.class);
viewModel = new ViewModelProvider(this, new DirectSettingsViewModelFactory(fragmentActivity.getApplication(), final DirectSettingsViewModelFactory viewModelFactory = new DirectSettingsViewModelFactory(
args.getThreadId(), fragmentActivity.getApplication(),
args.getPending(), args.getThreadId(),
appStateViewModel.getCurrentUser())) args.getPending(),
.get(DirectSettingsViewModel.class); appStateViewModel.getCurrentUser()
);
viewModel = new ViewModelProvider(this, viewModelFactory).get(DirectSettingsViewModel.class);
} }
@NonNull @NonNull

View File

@ -58,6 +58,7 @@ import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.function.Function;
import awais.instagrabber.ProfileNavGraphDirections; import awais.instagrabber.ProfileNavGraphDirections;
import awais.instagrabber.R; import awais.instagrabber.R;
@ -258,7 +259,7 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
} }
@Override @Override
public void onOptionSelect(final DirectItem item, final int itemId) { public void onOptionSelect(final DirectItem item, final int itemId, final Function<DirectItem, Void> cb) {
if (itemId == R.id.unsend) { if (itemId == R.id.unsend) {
handleSentMessage(viewModel.unsend(item)); handleSentMessage(viewModel.unsend(item));
return; return;
@ -275,21 +276,17 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
final NavController navController = NavHostFragment.findNavController(DirectMessageThreadFragment.this); final NavController navController = NavHostFragment.findNavController(DirectMessageThreadFragment.this);
navController.navigate(actionGlobalUserSearch); navController.navigate(actionGlobalUserSearch);
} }
if (itemId == R.id.detail) { if (itemId == R.id.download) {
final Context context = getContext(); downloadItem(item);
if (context == null) return; return;
final DirectItemType itemType = item.getItemType(); }
switch (itemType) { // otherwise call callback if present
case ANIMATED_MEDIA: if (cb != null) {
Utils.openURL(context, "https://giphy.com/gifs/" + item.getAnimatedMedia().getId()); cb.apply(item);
break;
case VOICE_MEDIA:
downloadItem(item.getVoiceMedia() == null ? null : item.getVoiceMedia().getMedia(), context);
break;
}
} }
} }
}; };
private final DirectItemLongClickListener directItemLongClickListener = position -> { private final DirectItemLongClickListener directItemLongClickListener = position -> {
// viewModel.setSelectedPosition(position); // viewModel.setSelectedPosition(position);
}; };
@ -319,6 +316,7 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
}; };
private final MutableLiveData<Integer> inputLength = new MutableLiveData<>(0); private final MutableLiveData<Integer> inputLength = new MutableLiveData<>(0);
private MenuItem markAsSeenMenuItem; private MenuItem markAsSeenMenuItem;
private Media tempMedia;
@Override @Override
public void onCreate(@Nullable final Bundle savedInstanceState) { public void onCreate(@Nullable final Bundle savedInstanceState) {
@ -329,11 +327,13 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
final Bundle arguments = getArguments(); final Bundle arguments = getArguments();
if (arguments == null) return; if (arguments == null) return;
final DirectMessageThreadFragmentArgs fragmentArgs = DirectMessageThreadFragmentArgs.fromBundle(arguments); final DirectMessageThreadFragmentArgs fragmentArgs = DirectMessageThreadFragmentArgs.fromBundle(arguments);
viewModel = new ViewModelProvider(this, new DirectThreadViewModelFactory(fragmentActivity.getApplication(), final DirectThreadViewModelFactory viewModelFactory = new DirectThreadViewModelFactory(
fragmentArgs.getThreadId(), fragmentActivity.getApplication(),
fragmentArgs.getPending(), fragmentArgs.getThreadId(),
appStateViewModel.getCurrentUser())) fragmentArgs.getPending(),
.get(DirectThreadViewModel.class); appStateViewModel.getCurrentUser()
);
viewModel = new ViewModelProvider(this, viewModelFactory).get(DirectThreadViewModel.class);
setHasOptionsMenu(true); setHasOptionsMenu(true);
} }
@ -454,7 +454,9 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
final Context context = getContext(); final Context context = getContext();
if (context == null) return; if (context == null) return;
if (requestCode == STORAGE_PERM_REQUEST_CODE && grantResults[0] == PackageManager.PERMISSION_GRANTED) { if (requestCode == STORAGE_PERM_REQUEST_CODE && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// downloadItem(context); if (tempMedia == null) return;
downloadItem(context, tempMedia);
return;
} }
if (requestCode == AUDIO_RECORD_PERM_REQUEST_CODE) { if (requestCode == AUDIO_RECORD_PERM_REQUEST_CODE) {
if (PermissionUtils.hasAudioRecordPerms(context)) { if (PermissionUtils.hasAudioRecordPerms(context)) {
@ -1317,18 +1319,31 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
appExecutors.mainThread().execute(prevTitleRunnable, 1000); appExecutors.mainThread().execute(prevTitleRunnable, 1000);
} }
private void downloadItem(final DirectItem item) {
final Context context = getContext();
if (context == null) return;
final DirectItemType itemType = item.getItemType();
//noinspection SwitchStatementWithTooFewBranches
switch (itemType) {
case VOICE_MEDIA:
downloadItem(context, item.getVoiceMedia() == null ? null : item.getVoiceMedia().getMedia());
break;
}
}
// currently ONLY for voice // currently ONLY for voice
private void downloadItem(final Media media, final Context context) { private void downloadItem(@NonNull final Context context, final Media media) {
if (media == null) { if (media == null) {
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
} else { return;
if (ContextCompat.checkSelfPermission(context, DownloadUtils.PERMS[0]) == PackageManager.PERMISSION_GRANTED) {
DownloadUtils.download(context, media);
} else {
requestPermissions(DownloadUtils.PERMS, STORAGE_PERM_REQUEST_CODE);
}
Toast.makeText(context, R.string.downloader_downloading_media, Toast.LENGTH_SHORT).show();
} }
if (ContextCompat.checkSelfPermission(context, DownloadUtils.PERMS[0]) == PackageManager.PERMISSION_GRANTED) {
DownloadUtils.download(context, media);
Toast.makeText(context, R.string.downloader_downloading_media, Toast.LENGTH_SHORT).show();
return;
}
tempMedia = media;
requestPermissions(DownloadUtils.PERMS, STORAGE_PERM_REQUEST_CODE);
} }
@NonNull @NonNull

View File

@ -13,8 +13,6 @@ import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.lifecycle.Observer; import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelStoreOwner;
import androidx.navigation.NavController;
import androidx.navigation.fragment.NavHostFragment; import androidx.navigation.fragment.NavHostFragment;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
@ -24,7 +22,6 @@ import com.google.android.material.snackbar.Snackbar;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import awais.instagrabber.R;
import awais.instagrabber.activities.MainActivity; import awais.instagrabber.activities.MainActivity;
import awais.instagrabber.adapters.DirectMessageInboxAdapter; import awais.instagrabber.adapters.DirectMessageInboxAdapter;
import awais.instagrabber.customviews.helpers.RecyclerLazyLoaderAtEdge; import awais.instagrabber.customviews.helpers.RecyclerLazyLoaderAtEdge;
@ -51,9 +48,7 @@ public class DirectPendingInboxFragment extends Fragment implements SwipeRefresh
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
fragmentActivity = (MainActivity) getActivity(); fragmentActivity = (MainActivity) getActivity();
if (fragmentActivity != null) { if (fragmentActivity != null) {
final NavController navController = NavHostFragment.findNavController(this); viewModel = new ViewModelProvider(fragmentActivity).get(DirectPendingInboxViewModel.class);
final ViewModelStoreOwner viewModelStoreOwner = navController.getViewModelStoreOwner(R.id.direct_messages_nav_graph);
viewModel = new ViewModelProvider(viewModelStoreOwner).get(DirectPendingInboxViewModel.class);
} }
} }

View File

@ -12,7 +12,6 @@ import android.text.style.RelativeSizeSpan;
import android.text.style.StyleSpan; import android.text.style.StyleSpan;
import android.util.Log; import android.util.Log;
import android.view.ActionMode; import android.view.ActionMode;
import android.view.Gravity;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
@ -608,11 +607,16 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
usernameTemp = usernameTemp.substring(1); usernameTemp = usernameTemp.substring(1);
} }
if (TextUtils.isEmpty(usernameTemp)) { if (TextUtils.isEmpty(usernameTemp)) {
profileModel = appStateViewModel.getCurrentUser(); appStateViewModel.getCurrentUserLiveData().observe(getViewLifecycleOwner(), user -> {
username = profileModel.getUsername(); if (user == null) return;
setUsernameDelayed(); profileModel = user;
setProfileDetails(); username = profileModel.getUsername();
} else if (isLoggedIn) { setUsernameDelayed();
setProfileDetails();
});
return;
}
if (isLoggedIn) {
userService.getUsernameInfo(usernameTemp, new ServiceCallback<User>() { userService.getUsernameInfo(usernameTemp, new ServiceCallback<User>() {
@Override @Override
public void onSuccess(final User user) { public void onSuccess(final User user) {
@ -646,25 +650,25 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
} catch (final Throwable ignored) {} } catch (final Throwable ignored) {}
} }
}); });
} else { return;
graphQLService.fetchUser(usernameTemp, new ServiceCallback<User>() {
@Override
public void onSuccess(final User user) {
profileModel = user;
setProfileDetails();
}
@Override
public void onFailure(final Throwable t) {
Log.e(TAG, "Error fetching profile", t);
final Context context = getContext();
try {
if (t == null) Toast.makeText(context, R.string.error_loading_profile, Toast.LENGTH_LONG).show();
else Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
} catch (final Throwable ignored) {}
}
});
} }
graphQLService.fetchUser(usernameTemp, new ServiceCallback<User>() {
@Override
public void onSuccess(final User user) {
profileModel = user;
setProfileDetails();
}
@Override
public void onFailure(final Throwable t) {
Log.e(TAG, "Error fetching profile", t);
final Context context = getContext();
try {
if (t == null) Toast.makeText(context, R.string.error_loading_profile, Toast.LENGTH_LONG).show();
else Toast.makeText(context, t.getMessage(), Toast.LENGTH_SHORT).show();
} catch (final Throwable ignored) {}
}
});
} }
private void setProfileDetails() { private void setProfileDetails() {
@ -989,6 +993,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
} }
private void updateAccountInfo() { private void updateAccountInfo() {
if (profileModel == null) return;
accountRepository.insertOrUpdateAccount( accountRepository.insertOrUpdateAccount(
profileModel.getPk(), profileModel.getPk(),
profileModel.getUsername(), profileModel.getUsername(),

View File

@ -15,7 +15,7 @@ public class NotificationsPreferencesFragment extends BasePreferencesFragment {
final Context context = getContext(); final Context context = getContext();
if (context == null) return; if (context == null) return;
screen.addPreference(getActivityNotificationsPreference(context)); screen.addPreference(getActivityNotificationsPreference(context));
screen.addPreference(getDMNotificationsPreference(context)); // screen.addPreference(getDMNotificationsPreference(context));
} }
private Preference getActivityNotificationsPreference(@NonNull final Context context) { private Preference getActivityNotificationsPreference(@NonNull final Context context) {

View File

@ -7,6 +7,7 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
public enum DirectItemType implements Serializable { public enum DirectItemType implements Serializable {
UNKNOWN(0),
@SerializedName("text") @SerializedName("text")
TEXT(1), TEXT(1),
@SerializedName("like") @SerializedName("like")
@ -40,7 +41,9 @@ public enum DirectItemType implements Serializable {
@SerializedName("felix_share") @SerializedName("felix_share")
FELIX_SHARE(16), // media_share but igtv FELIX_SHARE(16), // media_share but igtv
@SerializedName("location") @SerializedName("location")
LOCATION(17); LOCATION(17),
@SerializedName("xma")
XMA(18); // self avatar stickers
private final int id; private final int id;
private static final Map<Integer, DirectItemType> map = new HashMap<>(); private static final Map<Integer, DirectItemType> map = new HashMap<>();
@ -60,6 +63,7 @@ public enum DirectItemType implements Serializable {
} }
public static DirectItemType valueOf(final int id) { public static DirectItemType valueOf(final int id) {
if (!map.containsKey(id)) return DirectItemType.UNKNOWN;
return map.get(id); return map.get(id);
} }

View File

@ -41,6 +41,7 @@ public class DirectItem implements Cloneable, Serializable {
private final DirectItem repliedToMessage; private final DirectItem repliedToMessage;
private final DirectItemVoiceMedia voiceMedia; private final DirectItemVoiceMedia voiceMedia;
private final Location location; private final Location location;
private final DirectItemXma xma;
private final int hideInThread; private final int hideInThread;
private Date date; private Date date;
private boolean isPending; private boolean isPending;
@ -72,6 +73,7 @@ public class DirectItem implements Cloneable, Serializable {
final DirectItem repliedToMessage, final DirectItem repliedToMessage,
final DirectItemVoiceMedia voiceMedia, final DirectItemVoiceMedia voiceMedia,
final Location location, final Location location,
final DirectItemXma xma,
final int hideInThread, final int hideInThread,
final boolean showForwardAttribution) { final boolean showForwardAttribution) {
this.itemId = itemId; this.itemId = itemId;
@ -99,6 +101,7 @@ public class DirectItem implements Cloneable, Serializable {
this.repliedToMessage = repliedToMessage; this.repliedToMessage = repliedToMessage;
this.voiceMedia = voiceMedia; this.voiceMedia = voiceMedia;
this.location = location; this.location = location;
this.xma = xma;
this.hideInThread = hideInThread; this.hideInThread = hideInThread;
this.showForwardAttribution = showForwardAttribution; this.showForwardAttribution = showForwardAttribution;
} }
@ -208,6 +211,10 @@ public class DirectItem implements Cloneable, Serializable {
return location; return location;
} }
public DirectItemXma getXma() {
return xma;
}
public int getHideInThread() { public int getHideInThread() {
return hideInThread; return hideInThread;
} }
@ -286,6 +293,7 @@ public class DirectItem implements Cloneable, Serializable {
Objects.equals(repliedToMessage, that.repliedToMessage) && Objects.equals(repliedToMessage, that.repliedToMessage) &&
Objects.equals(voiceMedia, that.voiceMedia) && Objects.equals(voiceMedia, that.voiceMedia) &&
Objects.equals(location, that.location) && Objects.equals(location, that.location) &&
Objects.equals(xma, that.xma) &&
Objects.equals(date, that.date); Objects.equals(date, that.date);
} }
@ -294,6 +302,6 @@ public class DirectItem implements Cloneable, Serializable {
return Objects return Objects
.hash(itemId, userId, timestamp, itemType, text, like, link, clientContext, reelShare, storyShare, mediaShare, profile, placeholder, .hash(itemId, userId, timestamp, itemType, text, like, link, clientContext, reelShare, storyShare, mediaShare, profile, placeholder,
media, previewMedias, actionLog, videoCallEvent, clip, felixShare, visualMedia, animatedMedia, reactions, repliedToMessage, media, previewMedias, actionLog, videoCallEvent, clip, felixShare, visualMedia, animatedMedia, reactions, repliedToMessage,
voiceMedia, location, hideInThread, date, isPending, showForwardAttribution); voiceMedia, location, xma, hideInThread, date, isPending, showForwardAttribution);
} }
} }

View File

@ -0,0 +1,104 @@
package awais.instagrabber.repositories.responses.directmessages;
import androidx.annotation.NonNull;
import java.io.Serializable;
import java.util.Objects;
public class DirectItemXma {
private final XmaUrlInfo previewUrlInfo;
private final XmaUrlInfo playableUrlInfo;
public DirectItemXma(final XmaUrlInfo previewUrlInfo, final XmaUrlInfo playableUrlInfo) {
this.previewUrlInfo = previewUrlInfo;
this.playableUrlInfo = playableUrlInfo;
}
public XmaUrlInfo getPreviewUrlInfo() {
return previewUrlInfo;
}
public XmaUrlInfo getPlayableUrlInfo() {
return playableUrlInfo;
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final DirectItemXma that = (DirectItemXma) o;
return Objects.equals(previewUrlInfo, that.previewUrlInfo) &&
Objects.equals(playableUrlInfo, that.playableUrlInfo);
}
@Override
public int hashCode() {
return Objects.hash(previewUrlInfo, playableUrlInfo);
}
@NonNull
@Override
public String toString() {
return "DirectItemXma{" +
"previewUrlInfo=" + previewUrlInfo +
", playableUrlInfo=" + playableUrlInfo +
'}';
}
public static class XmaUrlInfo implements Serializable {
private final String url;
private final long urlExpirationTimestampUs;
private final int width;
private final int height;
public XmaUrlInfo(final String url, final long urlExpirationTimestampUs, final int width, final int height) {
this.url = url;
this.urlExpirationTimestampUs = urlExpirationTimestampUs;
this.width = width;
this.height = height;
}
public String getUrl() {
return url;
}
public long getUrlExpirationTimestampUs() {
return urlExpirationTimestampUs;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final XmaUrlInfo that = (XmaUrlInfo) o;
return urlExpirationTimestampUs == that.urlExpirationTimestampUs &&
width == that.width &&
height == that.height &&
Objects.equals(url, that.url);
}
@Override
public int hashCode() {
return Objects.hash(url, urlExpirationTimestampUs, width, height);
}
@NonNull
@Override
public String toString() {
return "XmaUrlInfo{" +
"url='" + url + '\'' +
", urlExpirationTimestampUs=" + urlExpirationTimestampUs +
", width=" + width +
", height=" + height +
'}';
}
}
}

View File

@ -44,9 +44,9 @@ public final class DMUtils {
public static boolean isRead(@NonNull final DirectThread thread) { public static boolean isRead(@NonNull final DirectThread thread) {
final boolean read; final boolean read;
// if (thread.getDirectStory() != null) { // if (thread.getDirectStory() != null) {
// return false; // return false;
// } // }
final DirectItem item = thread.getFirstDirectItem(); final DirectItem item = thread.getFirstDirectItem();
final long viewerId = thread.getViewerId(); final long viewerId = thread.getViewerId();
if (item != null && item.getUserId() == viewerId) { if (item != null && item.getUserId() == viewerId) {
@ -188,6 +188,9 @@ public final class DMUtils {
break; break;
} }
break; break;
case XMA:
subtitle = resources.getString(R.string.dms_inbox_shared_sticker, username != null ? username : "");
break;
default: default:
message = resources.getString(R.string.dms_inbox_raven_message_unknown); message = resources.getString(R.string.dms_inbox_raven_message_unknown);
} }
@ -213,7 +216,14 @@ public final class DMUtils {
.filter(Objects::nonNull) .filter(Objects::nonNull)
.filter(user -> user.getPk() == userId) .filter(user -> user.getPk() == userId)
.findFirst(); .findFirst();
return senderOptional.map(User::getUsername).orElse(null); return senderOptional.map(user -> {
// return full name for fb users
final String username = user.getUsername();
if (TextUtils.isEmpty(username)) {
return user.getFullName();
}
return username;
}).orElse(null);
} }
public static String getMediaSpecificSubtitle(final String username, final Resources resources, final MediaItemType mediaType) { public static String getMediaSpecificSubtitle(final String username, final Resources resources, final MediaItemType mediaType) {

View File

@ -51,6 +51,7 @@ public final class DirectItemFactory {
repliedToMessage, repliedToMessage,
null, null,
null, null,
null,
0, 0,
false); false);
} }
@ -132,6 +133,7 @@ public final class DirectItemFactory {
null, null,
null, null,
null, null,
null,
0, 0,
false); false);
} }
@ -213,6 +215,7 @@ public final class DirectItemFactory {
null, null,
voiceMedia, voiceMedia,
null, null,
null,
0, 0,
false); false);
} }
@ -253,6 +256,7 @@ public final class DirectItemFactory {
null, null,
null, null,
null, null,
null,
0, 0,
false false
); );

View File

@ -729,15 +729,19 @@ public final class ResponseBodyUtils {
width = dimensions.optInt("width"); width = dimensions.optInt("width");
} }
String thumbnailUrl = null; String thumbnailUrl = null;
final JSONArray displayResources = feedItem.getJSONArray("display_resources");
final List<MediaCandidate> candidates = new ArrayList<MediaCandidate>(); final List<MediaCandidate> candidates = new ArrayList<MediaCandidate>();
for (int i = 0; i < displayResources.length(); i++) { if (feedItem.has("display_resources") || feedItem.has("thumbnail_resources")) {
final JSONObject displayResource = displayResources.getJSONObject(i); final JSONArray displayResources = feedItem.has("display_resources")
candidates.add(new MediaCandidate( ? feedItem.getJSONArray("display_resources")
displayResource.getInt("config_width"), : feedItem.getJSONArray("thumbnail_resources");
displayResource.getInt("config_height"), for (int i = 0; i < displayResources.length(); i++) {
displayResource.getString("src") final JSONObject displayResource = displayResources.getJSONObject(i);
)); candidates.add(new MediaCandidate(
displayResource.getInt("config_width"),
displayResource.getInt("config_height"),
displayResource.getString("src")
));
}
} }
final ImageVersions2 imageVersions2 = new ImageVersions2(candidates); final ImageVersions2 imageVersions2 = new ImageVersions2(candidates);

View File

@ -1,9 +1,12 @@
package awais.instagrabber.viewmodels; package awais.instagrabber.viewmodels;
import android.app.Application; import android.app.Application;
import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel; import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import awais.instagrabber.db.datasources.AccountDataSource; import awais.instagrabber.db.datasources.AccountDataSource;
import awais.instagrabber.db.repositories.AccountRepository; import awais.instagrabber.db.repositories.AccountRepository;
@ -21,8 +24,8 @@ public class AppStateViewModel extends AndroidViewModel {
private final String cookie; private final String cookie;
private final boolean isLoggedIn; private final boolean isLoggedIn;
private final MutableLiveData<User> currentUser = new MutableLiveData<>();
private User currentUser;
private AccountRepository accountRepository; private AccountRepository accountRepository;
private UserService userService; private UserService userService;
@ -38,6 +41,10 @@ public class AppStateViewModel extends AndroidViewModel {
} }
public User getCurrentUser() { public User getCurrentUser() {
return currentUser.getValue();
}
public LiveData<User> getCurrentUserLiveData() {
return currentUser; return currentUser;
} }
@ -46,11 +53,13 @@ public class AppStateViewModel extends AndroidViewModel {
userService.getUserInfo(uid, new ServiceCallback<User>() { userService.getUserInfo(uid, new ServiceCallback<User>() {
@Override @Override
public void onSuccess(final User user) { public void onSuccess(final User user) {
currentUser = user; currentUser.postValue(user);
} }
@Override @Override
public void onFailure(final Throwable t) {} public void onFailure(final Throwable t) {
Log.e(TAG, "onFailure: ", t);
}
}); });
} }
} }

View File

@ -241,6 +241,7 @@ public class DirectThreadViewModel extends AndroidViewModel {
if (users != null && users.getValue() != null) { if (users != null && users.getValue() != null) {
final List<User> userList = users.getValue(); final List<User> userList = users.getValue();
match = userList.stream() match = userList.stream()
.filter(Objects::nonNull)
.filter(user -> user.getPk() == userId) .filter(user -> user.getPk() == userId)
.findFirst() .findFirst()
.orElse(null); .orElse(null);
@ -250,6 +251,7 @@ public class DirectThreadViewModel extends AndroidViewModel {
if (leftUsers != null && leftUsers.getValue() != null) { if (leftUsers != null && leftUsers.getValue() != null) {
final List<User> userList = leftUsers.getValue(); final List<User> userList = leftUsers.getValue();
match = userList.stream() match = userList.stream()
.filter(Objects::nonNull)
.filter(user -> user.getPk() == userId) .filter(user -> user.getPk() == userId)
.findFirst() .findFirst()
.orElse(null); .orElse(null);

View File

@ -162,7 +162,7 @@
<color name="barinstaColorSecondaryLight">#ffddac</color> <color name="barinstaColorSecondaryLight">#ffddac</color>
<color name="barinstaColorSecondaryDark">#a17c4f</color> <color name="barinstaColorSecondaryDark">#a17c4f</color>
<color name="barinstaPrimaryTextColor">#616161</color> <color name="barinstaPrimaryTextColor">#616161</color>
<color name="barinstaSecondaryTextColor">@color/white</color> <!-- <color name="barinstaSecondaryTextColor">@color/white</color> unused -->
<!-- Bibliogram Theme colors --> <!-- Bibliogram Theme colors -->
<color name="bibliogramColorPrimary">#d63f44</color> <color name="bibliogramColorPrimary">#d63f44</color>
@ -172,5 +172,5 @@
<color name="bibliogramColorSecondaryLight">#fff4e8</color> <color name="bibliogramColorSecondaryLight">#fff4e8</color>
<color name="bibliogramColorSecondaryDark">#8e2929</color> <color name="bibliogramColorSecondaryDark">#8e2929</color>
<color name="bibliogramPrimaryTextColor">#000000</color> <color name="bibliogramPrimaryTextColor">#000000</color>
<color name="bibliogramSecondaryTextColor">@color/white</color> <!-- <color name="bibliogramSecondaryTextColor">@color/white</color> unused -->
</resources> </resources>

View File

@ -4,4 +4,5 @@
<item name="unsend" type="id" /> <item name="unsend" type="id" />
<item name="forward" type="id" /> <item name="forward" type="id" />
<item name="detail" type="id" /> <item name="detail" type="id" />
<item name="copy" type="id" />
</resources> </resources>

View File

@ -479,4 +479,6 @@
<string name="other_tabs">Other tabs</string> <string name="other_tabs">Other tabs</string>
<string name="tab_order_start_next_launch">The tab order will be reflected on next launch</string> <string name="tab_order_start_next_launch">The tab order will be reflected on next launch</string>
<string name="dm_remove_warning">If saved, all DM related features will be disabled on next launch</string> <string name="dm_remove_warning">If saved, all DM related features will be disabled on next launch</string>
<string name="copy_caption">Copy caption</string>
<string name="copy_reply">Copy reply</string>
</resources> </resources>

View File

@ -64,6 +64,7 @@
<item name="bottomNavigationStyle">@style/Widget.BottomNavigationView.Light.Barinsta</item> <item name="bottomNavigationStyle">@style/Widget.BottomNavigationView.Light.Barinsta</item>
<item name="android:textColorPrimary">@color/barinstaPrimaryTextColor</item> <item name="android:textColorPrimary">@color/barinstaPrimaryTextColor</item>
<item name="toolbarStyle">@style/Widget.MaterialComponents.Toolbar.Light.Barinsta</item> <item name="toolbarStyle">@style/Widget.MaterialComponents.Toolbar.Light.Barinsta</item>
<item name="dmInputTextColor">@color/black</item>
</style> </style>
<style name="AppTheme.Light.Bibliogram" parent="AppTheme.Light"> <style name="AppTheme.Light.Bibliogram" parent="AppTheme.Light">
@ -79,6 +80,7 @@
<item name="bottomNavigationStyle">@style/Widget.BottomNavigationView.Light.Barinsta</item> <item name="bottomNavigationStyle">@style/Widget.BottomNavigationView.Light.Barinsta</item>
<item name="android:textColorPrimary">@color/bibliogramPrimaryTextColor</item> <item name="android:textColorPrimary">@color/bibliogramPrimaryTextColor</item>
<item name="toolbarStyle">@style/Widget.MaterialComponents.Toolbar.Light.Barinsta</item> <item name="toolbarStyle">@style/Widget.MaterialComponents.Toolbar.Light.Barinsta</item>
<item name="dmInputTextColor">@color/black</item>
</style> </style>

View File

@ -1,2 +1,2 @@
include ':app' include ':app'
rootProject.name = "InstaGrabber" rootProject.name = "Barinsta"