mirror of
https://github.com/KokaKiwi/BarInsta
synced 2024-11-22 22:57:29 +00:00
Merge branch 'master' into bottombar_redesign
This commit is contained in:
commit
1774ce8af0
@ -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"
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
@ -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 {
|
||||||
|
@ -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;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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 {
|
||||||
|
@ -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")
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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(),
|
||||||
|
@ -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) {
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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) {
|
||||||
|
@ -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
|
||||||
);
|
);
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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>
|
@ -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>
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
include ':app'
|
include ':app'
|
||||||
rootProject.name = "InstaGrabber"
|
rootProject.name = "Barinsta"
|
||||||
|
Loading…
Reference in New Issue
Block a user