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

Send stickers and gifs in DM

This commit is contained in:
Ammar Githam 2021-03-16 01:48:39 +09:00
parent 4bb77abb33
commit 6d73528387
22 changed files with 952 additions and 29 deletions

View File

@ -0,0 +1,107 @@
package awais.instagrabber.adapters;
import android.net.Uri;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import androidx.recyclerview.widget.RecyclerView;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.drawee.backends.pipeline.PipelineDraweeControllerBuilder;
import com.facebook.drawee.controller.BaseControllerListener;
import com.facebook.drawee.drawable.ScalingUtils;
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
import com.facebook.imagepipeline.common.ResizeOptions;
import com.facebook.imagepipeline.image.ImageInfo;
import com.facebook.imagepipeline.request.ImageRequest;
import com.facebook.imagepipeline.request.ImageRequestBuilder;
import java.util.Objects;
import awais.instagrabber.databinding.ItemMediaBinding;
import awais.instagrabber.repositories.responses.giphy.GiphyGif;
import awais.instagrabber.utils.Utils;
public class GifItemsAdapter extends ListAdapter<GiphyGif, GifItemsAdapter.GifViewHolder> {
private static final DiffUtil.ItemCallback<GiphyGif> diffCallback = new DiffUtil.ItemCallback<GiphyGif>() {
@Override
public boolean areItemsTheSame(@NonNull final GiphyGif oldItem, @NonNull final GiphyGif newItem) {
return Objects.equals(oldItem.getId(), newItem.getId());
}
@Override
public boolean areContentsTheSame(@NonNull final GiphyGif oldItem, @NonNull final GiphyGif newItem) {
return Objects.equals(oldItem.getId(), newItem.getId());
}
};
private final OnItemClickListener onItemClickListener;
public GifItemsAdapter(final OnItemClickListener onItemClickListener) {
super(diffCallback);
this.onItemClickListener = onItemClickListener;
}
@NonNull
@Override
public GifViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) {
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
final ItemMediaBinding binding = ItemMediaBinding.inflate(layoutInflater, parent, false);
return new GifViewHolder(binding, onItemClickListener);
}
@Override
public void onBindViewHolder(@NonNull final GifViewHolder holder, final int position) {
holder.bind(getItem(position));
}
public static class GifViewHolder extends RecyclerView.ViewHolder {
private static final String TAG = GifViewHolder.class.getSimpleName();
private static final int size = Utils.displayMetrics.widthPixels / 3;
private final ItemMediaBinding binding;
private final OnItemClickListener onItemClickListener;
public GifViewHolder(@NonNull final ItemMediaBinding binding,
final OnItemClickListener onItemClickListener) {
super(binding.getRoot());
this.binding = binding;
this.onItemClickListener = onItemClickListener;
binding.duration.setVisibility(View.GONE);
final GenericDraweeHierarchyBuilder builder = new GenericDraweeHierarchyBuilder(itemView.getResources());
builder.setActualImageScaleType(ScalingUtils.ScaleType.FIT_CENTER);
binding.item.setHierarchy(builder.build());
}
public void bind(final GiphyGif item) {
if (onItemClickListener != null) {
itemView.setOnClickListener(v -> onItemClickListener.onItemClick(item));
}
final BaseControllerListener<ImageInfo> controllerListener = new BaseControllerListener<ImageInfo>() {
@Override
public void onFailure(final String id, final Throwable throwable) {
Log.e(TAG, "onFailure: ", throwable);
}
};
final ImageRequest request = ImageRequestBuilder
.newBuilderWithSource(Uri.parse(item.getImages().getFixedHeight().getWebp()))
.setResizeOptions(ResizeOptions.forDimensions(size, size))
.build();
final PipelineDraweeControllerBuilder builder = Fresco.newDraweeControllerBuilder()
.setImageRequest(request)
.setAutoPlayAnimations(true)
.setControllerListener(controllerListener);
binding.item.setController(builder.build());
}
}
public interface OnItemClickListener {
void onItemClick(GiphyGif giphyGif);
}
}

View File

@ -0,0 +1,150 @@
package awais.instagrabber.dialogs;
import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import android.text.Editable;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.GridLayoutManager;
import com.google.android.material.bottomsheet.BottomSheetDialog;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import com.google.android.material.snackbar.Snackbar;
import awais.instagrabber.R;
import awais.instagrabber.adapters.GifItemsAdapter;
import awais.instagrabber.customviews.helpers.TextWatcherAdapter;
import awais.instagrabber.databinding.LayoutGifPickerBinding;
import awais.instagrabber.repositories.responses.giphy.GiphyGif;
import awais.instagrabber.utils.Debouncer;
import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.viewmodels.GifPickerViewModel;
public class GifPickerBottomDialogFragment extends BottomSheetDialogFragment {
private static final String TAG = GifPickerBottomDialogFragment.class.getSimpleName();
private static final int INPUT_DEBOUNCE_INTERVAL = 500;
private static final String INPUT_KEY = "gif_search_input";
private LayoutGifPickerBinding binding;
private GifPickerViewModel viewModel;
private GifItemsAdapter gifItemsAdapter;
private OnSelectListener onSelectListener;
private Debouncer<String> inputDebouncer;
public static GifPickerBottomDialogFragment newInstance() {
final Bundle args = new Bundle();
final GifPickerBottomDialogFragment fragment = new GifPickerBottomDialogFragment();
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setStyle(DialogFragment.STYLE_NORMAL, R.style.ThemeOverlay_Rounded_BottomSheetDialog);
final Debouncer.Callback<String> callback = new Debouncer.Callback<String>() {
@Override
public void call(final String key) {
final Editable text = binding.input.getText();
if (TextUtils.isEmpty(text)) {
viewModel.search(null);
return;
}
viewModel.search(text.toString().trim());
}
@Override
public void onError(final Throwable t) {
Log.e(TAG, "onError: ", t);
}
};
inputDebouncer = new Debouncer<>(callback, INPUT_DEBOUNCE_INTERVAL);
}
@Nullable
@Override
public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) {
binding = LayoutGifPickerBinding.inflate(inflater, container, false);
viewModel = new ViewModelProvider(this).get(GifPickerViewModel.class);
return binding.getRoot();
}
@Override
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
init();
}
@Override
public void onStart() {
super.onStart();
final Dialog dialog = getDialog();
if (dialog == null) return;
final BottomSheetDialog bottomSheetDialog = (BottomSheetDialog) dialog;
final View bottomSheetInternal = bottomSheetDialog.findViewById(com.google.android.material.R.id.design_bottom_sheet);
if (bottomSheetInternal == null) return;
bottomSheetInternal.getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT;
bottomSheetInternal.requestLayout();
}
private void init() {
setupList();
setupInput();
setupObservers();
}
private void setupList() {
final Context context = getContext();
if (context == null) return;
binding.gifList.setLayoutManager(new GridLayoutManager(context, 3));
binding.gifList.setHasFixedSize(true);
gifItemsAdapter = new GifItemsAdapter(entry -> {
if (onSelectListener == null) return;
onSelectListener.onSelect(entry);
});
binding.gifList.setAdapter(gifItemsAdapter);
}
private void setupInput() {
binding.input.addTextChangedListener(new TextWatcherAdapter() {
@Override
public void onTextChanged(final CharSequence s, final int start, final int before, final int count) {
inputDebouncer.call(INPUT_KEY);
}
});
}
private void setupObservers() {
viewModel.getImages().observe(getViewLifecycleOwner(), imagesResource -> {
if (imagesResource == null) return;
switch (imagesResource.status) {
case SUCCESS:
gifItemsAdapter.submitList(imagesResource.data);
break;
case ERROR:
final Context context = getContext();
if (context != null && imagesResource.message != null) {
Snackbar.make(context, binding.getRoot(), imagesResource.message, Snackbar.LENGTH_LONG);
}
break;
case LOADING:
break;
}
});
}
public void setOnSelectListener(final OnSelectListener onSelectListener) {
this.onSelectListener = onSelectListener;
}
public interface OnSelectListener {
void onSelect(GiphyGif giphyGif);
}
}

View File

@ -81,6 +81,7 @@ import awais.instagrabber.customviews.helpers.SwipeAndRestoreItemTouchHelperCall
import awais.instagrabber.customviews.helpers.TextWatcherAdapter; import awais.instagrabber.customviews.helpers.TextWatcherAdapter;
import awais.instagrabber.databinding.FragmentDirectMessagesThreadBinding; import awais.instagrabber.databinding.FragmentDirectMessagesThreadBinding;
import awais.instagrabber.dialogs.DirectItemReactionDialogFragment; import awais.instagrabber.dialogs.DirectItemReactionDialogFragment;
import awais.instagrabber.dialogs.GifPickerBottomDialogFragment;
import awais.instagrabber.dialogs.MediaPickerBottomDialogFragment; import awais.instagrabber.dialogs.MediaPickerBottomDialogFragment;
import awais.instagrabber.fragments.PostViewV2Fragment; import awais.instagrabber.fragments.PostViewV2Fragment;
import awais.instagrabber.fragments.UserSearchFragment; import awais.instagrabber.fragments.UserSearchFragment;
@ -737,6 +738,7 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
private void hideInput() { private void hideInput() {
binding.emojiToggle.setVisibility(View.GONE); binding.emojiToggle.setVisibility(View.GONE);
binding.gif.setVisibility(View.GONE);
binding.camera.setVisibility(View.GONE); binding.camera.setVisibility(View.GONE);
binding.gallery.setVisibility(View.GONE); binding.gallery.setVisibility(View.GONE);
binding.input.setVisibility(View.GONE); binding.input.setVisibility(View.GONE);
@ -750,6 +752,7 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
private void showInput() { private void showInput() {
binding.emojiToggle.setVisibility(View.VISIBLE); binding.emojiToggle.setVisibility(View.VISIBLE);
binding.gif.setVisibility(View.VISIBLE);
binding.camera.setVisibility(View.VISIBLE); binding.camera.setVisibility(View.VISIBLE);
binding.gallery.setVisibility(View.VISIBLE); binding.gallery.setVisibility(View.VISIBLE);
binding.input.setVisibility(View.VISIBLE); binding.input.setVisibility(View.VISIBLE);
@ -788,16 +791,18 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
binding.send.setListenForRecord(true); binding.send.setListenForRecord(true);
startIconAnimation(); startIconAnimation();
} }
binding.gallery.setVisibility(View.VISIBLE); binding.gif.setVisibility(View.VISIBLE);
binding.camera.setVisibility(View.VISIBLE); binding.camera.setVisibility(View.VISIBLE);
binding.gallery.setVisibility(View.VISIBLE);
return; return;
} }
if (binding.send.isListenForRecord()) { if (binding.send.isListenForRecord()) {
binding.send.setListenForRecord(false); binding.send.setListenForRecord(false);
startIconAnimation(); startIconAnimation();
} }
binding.gallery.setVisibility(View.GONE); binding.gif.setVisibility(View.GONE);
binding.camera.setVisibility(View.GONE); binding.camera.setVisibility(View.GONE);
binding.gallery.setVisibility(View.GONE);
} }
private String getDirectItemPreviewText(final DirectItem item) { private String getDirectItemPreviewText(final DirectItem item) {
@ -937,8 +942,9 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
public void onStart() { public void onStart() {
isRecording = true; isRecording = true;
binding.input.setHint(null); binding.input.setHint(null);
binding.gallery.setVisibility(View.GONE); binding.gif.setVisibility(View.GONE);
binding.camera.setVisibility(View.GONE); binding.camera.setVisibility(View.GONE);
binding.gallery.setVisibility(View.GONE);
if (PermissionUtils.hasAudioRecordPerms(context)) { if (PermissionUtils.hasAudioRecordPerms(context)) {
viewModel.startRecording(); viewModel.startRecording();
return; return;
@ -958,8 +964,9 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
public void onFinish(final long recordTime) { public void onFinish(final long recordTime) {
Log.d(TAG, "onFinish"); Log.d(TAG, "onFinish");
binding.input.setHint("Message"); binding.input.setHint("Message");
binding.gallery.setVisibility(View.VISIBLE); binding.gif.setVisibility(View.VISIBLE);
binding.camera.setVisibility(View.VISIBLE); binding.camera.setVisibility(View.VISIBLE);
binding.gallery.setVisibility(View.VISIBLE);
viewModel.stopRecording(false); viewModel.stopRecording(false);
isRecording = false; isRecording = false;
} }
@ -971,16 +978,18 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
if (PermissionUtils.hasAudioRecordPerms(context)) { if (PermissionUtils.hasAudioRecordPerms(context)) {
tooltip.show(binding.send); tooltip.show(binding.send);
} }
binding.gallery.setVisibility(View.VISIBLE); binding.gif.setVisibility(View.VISIBLE);
binding.camera.setVisibility(View.VISIBLE); binding.camera.setVisibility(View.VISIBLE);
binding.gallery.setVisibility(View.VISIBLE);
viewModel.stopRecording(true); viewModel.stopRecording(true);
isRecording = false; isRecording = false;
} }
}); });
binding.recordView.setOnBasketAnimationEndListener(() -> { binding.recordView.setOnBasketAnimationEndListener(() -> {
binding.input.setHint(R.string.dms_thread_message_hint); binding.input.setHint(R.string.dms_thread_message_hint);
binding.gallery.setVisibility(View.VISIBLE); binding.gif.setVisibility(View.VISIBLE);
binding.camera.setVisibility(View.VISIBLE); binding.camera.setVisibility(View.VISIBLE);
binding.gallery.setVisibility(View.VISIBLE);
}); });
binding.input.addTextChangedListener(new TextWatcherAdapter() { binding.input.addTextChangedListener(new TextWatcherAdapter() {
// int prevLength = 0; // int prevLength = 0;
@ -1057,6 +1066,16 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
mediaPicker.show(getChildFragmentManager(), "MediaPicker"); mediaPicker.show(getChildFragmentManager(), "MediaPicker");
hideKeyboard(true); hideKeyboard(true);
}); });
binding.gif.setOnClickListener(v -> {
final GifPickerBottomDialogFragment gifPicker = GifPickerBottomDialogFragment.newInstance();
gifPicker.setOnSelectListener(giphyGif -> {
gifPicker.dismiss();
if (giphyGif == null) return;
handleSentMessage(viewModel.sendAnimatedMedia(giphyGif));
});
gifPicker.show(getChildFragmentManager(), "GifPicker");
hideKeyboard(true);
});
binding.camera.setOnClickListener(v -> { binding.camera.setOnClickListener(v -> {
final Intent intent = new Intent(context, CameraActivity.class); final Intent intent = new Intent(context, CameraActivity.class);
startActivityForResult(intent, CAMERA_REQUEST_CODE); startActivityForResult(intent, CAMERA_REQUEST_CODE);

View File

@ -53,6 +53,7 @@ import awais.instagrabber.repositories.responses.directmessages.DirectThreadDeta
import awais.instagrabber.repositories.responses.directmessages.DirectThreadFeedResponse; import awais.instagrabber.repositories.responses.directmessages.DirectThreadFeedResponse;
import awais.instagrabber.repositories.responses.directmessages.DirectThreadParticipantRequestsResponse; import awais.instagrabber.repositories.responses.directmessages.DirectThreadParticipantRequestsResponse;
import awais.instagrabber.repositories.responses.directmessages.RankedRecipient; import awais.instagrabber.repositories.responses.directmessages.RankedRecipient;
import awais.instagrabber.repositories.responses.giphy.GiphyGif;
import awais.instagrabber.utils.BitmapUtils; import awais.instagrabber.utils.BitmapUtils;
import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils; import awais.instagrabber.utils.CookieUtils;
@ -641,6 +642,24 @@ public final class ThreadManager {
return data; return data;
} }
public LiveData<Resource<Object>> sendAnimatedMedia(@NonNull final GiphyGif giphyGif) {
final MutableLiveData<Resource<Object>> data = new MutableLiveData<>();
final Long userId = getCurrentUserId(data);
if (userId == null) return data;
final String clientContext = UUID.randomUUID().toString();
final DirectItem directItem = DirectItemFactory.createAnimatedMedia(userId, clientContext, giphyGif);
directItem.setPending(true);
addItems(0, Collections.singletonList(directItem));
data.postValue(Resource.loading(directItem));
final Call<DirectThreadBroadcastResponse> request = service.broadcastAnimatedMedia(
clientContext,
threadIdOrUserIds,
giphyGif
);
enqueueRequest(request, data, directItem);
return data;
}
public void sendVoice(@NonNull final MutableLiveData<Resource<Object>> data, public void sendVoice(@NonNull final MutableLiveData<Resource<Object>> data,
@NonNull final Uri uri, @NonNull final Uri uri,
@NonNull final List<Float> waveform, @NonNull final List<Float> waveform,

View File

@ -7,7 +7,8 @@ public enum BroadcastItemType {
IMAGE("configure_photo"), IMAGE("configure_photo"),
LINK("link"), LINK("link"),
VIDEO("configure_video"), VIDEO("configure_video"),
VOICE("share_voice"); VOICE("share_voice"),
ANIMATED_MEDIA("animated_media");
private final String value; private final String value;

View File

@ -0,0 +1,14 @@
package awais.instagrabber.repositories;
import awais.instagrabber.repositories.responses.giphy.GiphyGifResponse;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Query;
public interface GifRepository {
@GET("/api/v1/creatives/story_media_search_keyed_format/")
Call<GiphyGifResponse> searchGiphyGifs(@Query("request_surface") final String requestSurface,
@Query("q") final String query,
@Query("media_types") final String mediaTypes);
}

View File

@ -0,0 +1,27 @@
package awais.instagrabber.repositories.requests.directmessages;
import java.util.HashMap;
import java.util.Map;
import awais.instagrabber.models.enums.BroadcastItemType;
import awais.instagrabber.repositories.responses.giphy.GiphyGif;
public class AnimatedMediaBroadcastOptions extends BroadcastOptions {
private final GiphyGif giphyGif;
public AnimatedMediaBroadcastOptions(final String clientContext,
final ThreadIdOrUserIds threadIdOrUserIds,
final GiphyGif giphyGif) {
super(clientContext, threadIdOrUserIds, BroadcastItemType.ANIMATED_MEDIA);
this.giphyGif = giphyGif;
}
@Override
public Map<String, String> getFormMap() {
final Map<String, String> form = new HashMap<>();
form.put("is_sticker", String.valueOf(giphyGif.isSticker()));
form.put("id", giphyGif.getId());
return form;
}
}

View File

@ -0,0 +1,70 @@
package awais.instagrabber.repositories.responses.giphy;
import androidx.annotation.NonNull;
import java.util.Objects;
public class GiphyGif {
private final String type;
private final String id;
private final String title;
private final int isSticker;
private final GiphyGifImages images;
public GiphyGif(final String type, final String id, final String title, final int isSticker, final GiphyGifImages images) {
this.type = type;
this.id = id;
this.title = title;
this.isSticker = isSticker;
this.images = images;
}
public String getType() {
return type;
}
public String getId() {
return id;
}
public String getTitle() {
return title;
}
public boolean isSticker() {
return isSticker == 1;
}
public GiphyGifImages getImages() {
return images;
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final GiphyGif giphyGif = (GiphyGif) o;
return isSticker == giphyGif.isSticker &&
Objects.equals(type, giphyGif.type) &&
Objects.equals(id, giphyGif.id) &&
Objects.equals(title, giphyGif.title) &&
Objects.equals(images, giphyGif.images);
}
@Override
public int hashCode() {
return Objects.hash(type, id, title, isSticker, images);
}
@NonNull
@Override
public String toString() {
return "GiphyGif{" +
"type='" + type + '\'' +
", id='" + id + '\'' +
", title='" + title + '\'' +
", isSticker=" + isSticker() +
", images=" + images +
'}';
}
}

View File

@ -0,0 +1,59 @@
package awais.instagrabber.repositories.responses.giphy;
import java.util.Objects;
public class GiphyGifImage {
private final int height;
private final int width;
private final long webpSize;
private final String webp;
public GiphyGifImage(final int height, final int width, final long webpSize, final String webp) {
this.height = height;
this.width = width;
this.webpSize = webpSize;
this.webp = webp;
}
public int getHeight() {
return height;
}
public int getWidth() {
return width;
}
public long getWebpSize() {
return webpSize;
}
public String getWebp() {
return webp;
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final GiphyGifImage that = (GiphyGifImage) o;
return height == that.height &&
width == that.width &&
webpSize == that.webpSize &&
Objects.equals(webp, that.webp);
}
@Override
public int hashCode() {
return Objects.hash(height, width, webpSize, webp);
}
@Override
public String toString() {
return "GiphyGifImage{" +
"height=" + height +
", width=" + width +
", webpSize=" + webpSize +
", webp='" + webp + '\'' +
'}';
}
}

View File

@ -0,0 +1,40 @@
package awais.instagrabber.repositories.responses.giphy;
import androidx.annotation.NonNull;
import java.util.Objects;
import awais.instagrabber.repositories.responses.AnimatedMediaFixedHeight;
public class GiphyGifImages {
private final AnimatedMediaFixedHeight fixedHeight;
public GiphyGifImages(final AnimatedMediaFixedHeight fixedHeight) {
this.fixedHeight = fixedHeight;
}
public AnimatedMediaFixedHeight getFixedHeight() {
return fixedHeight;
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final GiphyGifImages that = (GiphyGifImages) o;
return Objects.equals(fixedHeight, that.fixedHeight);
}
@Override
public int hashCode() {
return Objects.hash(fixedHeight);
}
@NonNull
@Override
public String toString() {
return "GiphyGifImages{" +
"fixedHeight=" + fixedHeight +
'}';
}
}

View File

@ -0,0 +1,46 @@
package awais.instagrabber.repositories.responses.giphy;
import androidx.annotation.NonNull;
import java.util.Objects;
public class GiphyGifResponse {
private final GiphyGifResults results;
private final String status;
public GiphyGifResponse(final GiphyGifResults results, final String status) {
this.results = results;
this.status = status;
}
public GiphyGifResults getResults() {
return results;
}
public String getStatus() {
return status;
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final GiphyGifResponse that = (GiphyGifResponse) o;
return Objects.equals(results, that.results) &&
Objects.equals(status, that.status);
}
@Override
public int hashCode() {
return Objects.hash(results, status);
}
@NonNull
@Override
public String toString() {
return "GiphyGifResponse{" +
"results=" + results +
", status='" + status + '\'' +
'}';
}
}

View File

@ -0,0 +1,47 @@
package awais.instagrabber.repositories.responses.giphy;
import androidx.annotation.NonNull;
import java.util.List;
import java.util.Objects;
public class GiphyGifResults {
private final List<GiphyGif> giphyGifs;
private final List<GiphyGif> giphy;
public GiphyGifResults(final List<GiphyGif> giphyGifs, final List<GiphyGif> giphy) {
this.giphyGifs = giphyGifs;
this.giphy = giphy;
}
public List<GiphyGif> getGiphyGifs() {
return giphyGifs;
}
public List<GiphyGif> getGiphy() {
return giphy;
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final GiphyGifResults that = (GiphyGifResults) o;
return Objects.equals(giphyGifs, that.giphyGifs) &&
Objects.equals(giphy, that.giphy);
}
@Override
public int hashCode() {
return Objects.hash(giphyGifs, giphy);
}
@NonNull
@Override
public String toString() {
return "GiphyGifResults{" +
"giphyGifs=" + giphyGifs +
", giphy=" + giphy +
'}';
}
}

View File

@ -15,6 +15,7 @@ import awais.instagrabber.models.enums.DirectItemType;
import awais.instagrabber.models.enums.MediaItemType; import awais.instagrabber.models.enums.MediaItemType;
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.DirectItemAnimatedMedia;
import awais.instagrabber.repositories.responses.directmessages.DirectItemReelShare; import awais.instagrabber.repositories.responses.directmessages.DirectItemReelShare;
import awais.instagrabber.repositories.responses.directmessages.DirectItemVisualMedia; import awais.instagrabber.repositories.responses.directmessages.DirectItemVisualMedia;
import awais.instagrabber.repositories.responses.directmessages.DirectThread; import awais.instagrabber.repositories.responses.directmessages.DirectThread;
@ -84,11 +85,16 @@ public final class DMUtils {
message = item.getPlaceholder().getMessage(); message = item.getPlaceholder().getMessage();
break; break;
case MEDIA_SHARE: case MEDIA_SHARE:
subtitle = resources.getString(R.string.dms_inbox_shared_post, username != null ? username : "", final User mediaShareUser = item.getMediaShare().getUser();
item.getMediaShare().getUser().getUsername()); subtitle = resources.getString(R.string.dms_inbox_shared_post,
username != null ? username : "",
mediaShareUser == null ? "" : mediaShareUser.getUsername());
break; break;
case ANIMATED_MEDIA: case ANIMATED_MEDIA:
subtitle = resources.getString(R.string.dms_inbox_shared_gif, username != null ? username : ""); final DirectItemAnimatedMedia animatedMedia = item.getAnimatedMedia();
subtitle = resources.getString(animatedMedia.isSticker() ? R.string.dms_inbox_shared_sticker
: R.string.dms_inbox_shared_gif,
username != null ? username : "");
break; break;
case PROFILE: case PROFILE:
subtitle = resources subtitle = resources
@ -111,8 +117,10 @@ public final class DMUtils {
final int format = reelType.equals("highlight_reel") final int format = reelType.equals("highlight_reel")
? R.string.dms_inbox_shared_highlight ? R.string.dms_inbox_shared_highlight
: R.string.dms_inbox_shared_story; : R.string.dms_inbox_shared_story;
subtitle = resources.getString(format, username != null ? username : "", final User storyShareMediaUser = item.getStoryShare().getMedia().getUser();
item.getStoryShare().getMedia().getUser().getUsername()); subtitle = resources.getString(format,
username != null ? username : "",
storyShareMediaUser == null ? "" : storyShareMediaUser.getUsername());
} }
break; break;
} }
@ -126,12 +134,16 @@ public final class DMUtils {
subtitle = item.getVideoCallEvent().getDescription(); subtitle = item.getVideoCallEvent().getDescription();
break; break;
case CLIP: case CLIP:
subtitle = resources.getString(R.string.dms_inbox_shared_clip, username != null ? username : "", final User clipUser = item.getClip().getClip().getUser();
item.getClip().getClip().getUser().getUsername()); subtitle = resources.getString(R.string.dms_inbox_shared_clip,
username != null ? username : "",
clipUser == null ? "" : clipUser.getUsername());
break; break;
case FELIX_SHARE: case FELIX_SHARE:
subtitle = resources.getString(R.string.dms_inbox_shared_igtv, username != null ? username : "", final User felixShareVideoUser = item.getFelixShare().getVideo().getUser();
item.getFelixShare().getVideo().getUser().getUsername()); subtitle = resources.getString(R.string.dms_inbox_shared_igtv,
username != null ? username : "",
felixShareVideoUser == null ? "" : felixShareVideoUser.getUsername());
break; break;
case RAVEN_MEDIA: case RAVEN_MEDIA:
subtitle = getRavenMediaSubtitle(item, resources, username); subtitle = getRavenMediaSubtitle(item, resources, username);

View File

@ -8,13 +8,16 @@ import java.util.UUID;
import awais.instagrabber.models.enums.DirectItemType; import awais.instagrabber.models.enums.DirectItemType;
import awais.instagrabber.models.enums.MediaItemType; import awais.instagrabber.models.enums.MediaItemType;
import awais.instagrabber.repositories.responses.AnimatedMediaImages;
import awais.instagrabber.repositories.responses.Audio; import awais.instagrabber.repositories.responses.Audio;
import awais.instagrabber.repositories.responses.ImageVersions2; import awais.instagrabber.repositories.responses.ImageVersions2;
import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.MediaCandidate; import awais.instagrabber.repositories.responses.MediaCandidate;
import awais.instagrabber.repositories.responses.VideoVersion; import awais.instagrabber.repositories.responses.VideoVersion;
import awais.instagrabber.repositories.responses.directmessages.DirectItem; import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectItemAnimatedMedia;
import awais.instagrabber.repositories.responses.directmessages.DirectItemVoiceMedia; import awais.instagrabber.repositories.responses.directmessages.DirectItemVoiceMedia;
import awais.instagrabber.repositories.responses.giphy.GiphyGif;
public final class DirectItemFactory { public final class DirectItemFactory {
@ -213,4 +216,45 @@ public final class DirectItemFactory {
0, 0,
false); false);
} }
public static DirectItem createAnimatedMedia(final long userId,
final String clientContext,
final GiphyGif giphyGif) {
final AnimatedMediaImages animatedImages = new AnimatedMediaImages(giphyGif.getImages().getFixedHeight());
final DirectItemAnimatedMedia animateMedia = new DirectItemAnimatedMedia(
giphyGif.getId(),
animatedImages,
false,
giphyGif.isSticker()
);
return new DirectItem(
UUID.randomUUID().toString(),
userId,
System.currentTimeMillis() * 1000,
DirectItemType.ANIMATED_MEDIA,
null,
null,
null,
clientContext,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
null,
animateMedia,
null,
null,
null,
null,
0,
false
);
}
} }

View File

@ -26,6 +26,7 @@ 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.repositories.responses.directmessages.RankedRecipient; import awais.instagrabber.repositories.responses.directmessages.RankedRecipient;
import awais.instagrabber.repositories.responses.giphy.GiphyGif;
import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils; import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.DirectoryUtils; import awais.instagrabber.utils.DirectoryUtils;
@ -219,6 +220,10 @@ public class DirectThreadViewModel extends AndroidViewModel {
return threadManager.unsend(item); return threadManager.unsend(item);
} }
public LiveData<Resource<Object>> sendAnimatedMedia(@NonNull final GiphyGif giphyGif) {
return threadManager.sendAnimatedMedia(giphyGif);
}
public User getCurrentUser() { public User getCurrentUser() {
return currentUser; return currentUser;
} }

View File

@ -0,0 +1,121 @@
package awais.instagrabber.viewmodels;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import awais.instagrabber.models.Resource;
import awais.instagrabber.repositories.responses.giphy.GiphyGif;
import awais.instagrabber.repositories.responses.giphy.GiphyGifResponse;
import awais.instagrabber.repositories.responses.giphy.GiphyGifResults;
import awais.instagrabber.webservices.GifService;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class GifPickerViewModel extends ViewModel {
private static final String TAG = GifPickerViewModel.class.getSimpleName();
private final MutableLiveData<Resource<List<GiphyGif>>> images = new MutableLiveData<>(Resource.success(Collections.emptyList()));
private final GifService gifService;
private Call<GiphyGifResponse> searchRequest;
public GifPickerViewModel() {
gifService = GifService.getInstance();
search(null);
}
public LiveData<Resource<List<GiphyGif>>> getImages() {
return images;
}
public void search(final String query) {
final Resource<List<GiphyGif>> currentValue = images.getValue();
if (currentValue != null && currentValue.status == Resource.Status.LOADING) {
cancelSearchRequest();
}
images.postValue(Resource.loading(getCurrentImages()));
searchRequest = gifService.searchGiphyGifs(query, query != null);
searchRequest.enqueue(new Callback<GiphyGifResponse>() {
@Override
public void onResponse(@NonNull final Call<GiphyGifResponse> call,
@NonNull final Response<GiphyGifResponse> response) {
if (response.isSuccessful()) {
parseResponse(response);
return;
}
if (response.errorBody() != null) {
try {
final String string = response.errorBody().string();
final String msg = String.format(Locale.US,
"onResponse: url: %s, responseCode: %d, errorBody: %s",
call.request().url().toString(),
response.code(),
string);
images.postValue(Resource.error(msg, getCurrentImages()));
Log.e(TAG, msg);
} catch (IOException e) {
images.postValue(Resource.error(e.getMessage(), getCurrentImages()));
Log.e(TAG, "onResponse: ", e);
}
}
images.postValue(Resource.error("request was not successful and response error body was null", getCurrentImages()));
}
@Override
public void onFailure(@NonNull final Call<GiphyGifResponse> call,
@NonNull final Throwable t) {
images.postValue(Resource.error(t.getMessage(), getCurrentImages()));
Log.e(TAG, "enqueueRequest: onFailure: ", t);
}
});
}
private void parseResponse(final Response<GiphyGifResponse> response) {
final GiphyGifResponse giphyGifResponse = response.body();
if (giphyGifResponse == null) {
images.postValue(Resource.error("Response body was null", getCurrentImages()));
return;
}
final GiphyGifResults results = giphyGifResponse.getResults();
images.postValue(Resource.success(
ImmutableList.<GiphyGif>builder()
.addAll(results.getGiphy() == null ? Collections.emptyList() : results.getGiphy())
.addAll(results.getGiphyGifs() == null ? Collections.emptyList() : results.getGiphyGifs())
.build()
));
}
// @NonNull
// private List<GiphyGifImage> getGiphyGifImages(@NonNull final List<GiphyGif> giphy) {
// return giphy.stream()
// .map(giphyGif -> {
// final GiphyGifImages images = giphyGif.getImages();
// if (images == null) return null;
// return images.getOriginal();
// })
// .filter(Objects::nonNull)
// .collect(Collectors.toList());
// }
private List<GiphyGif> getCurrentImages() {
final Resource<List<GiphyGif>> value = images.getValue();
return value == null ? Collections.emptyList() : value.data;
}
public void cancelSearchRequest() {
if (searchRequest == null) return;
searchRequest.cancel();
}
}

View File

@ -17,6 +17,7 @@ import java.util.UUID;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import awais.instagrabber.repositories.DirectMessagesRepository; import awais.instagrabber.repositories.DirectMessagesRepository;
import awais.instagrabber.repositories.requests.directmessages.AnimatedMediaBroadcastOptions;
import awais.instagrabber.repositories.requests.directmessages.BroadcastOptions; import awais.instagrabber.repositories.requests.directmessages.BroadcastOptions;
import awais.instagrabber.repositories.requests.directmessages.BroadcastOptions.ThreadIdOrUserIds; import awais.instagrabber.repositories.requests.directmessages.BroadcastOptions.ThreadIdOrUserIds;
import awais.instagrabber.repositories.requests.directmessages.LinkBroadcastOptions; import awais.instagrabber.repositories.requests.directmessages.LinkBroadcastOptions;
@ -34,6 +35,7 @@ import awais.instagrabber.repositories.responses.directmessages.DirectThreadDeta
import awais.instagrabber.repositories.responses.directmessages.DirectThreadFeedResponse; import awais.instagrabber.repositories.responses.directmessages.DirectThreadFeedResponse;
import awais.instagrabber.repositories.responses.directmessages.DirectThreadParticipantRequestsResponse; import awais.instagrabber.repositories.responses.directmessages.DirectThreadParticipantRequestsResponse;
import awais.instagrabber.repositories.responses.directmessages.RankedRecipientsResponse; import awais.instagrabber.repositories.responses.directmessages.RankedRecipientsResponse;
import awais.instagrabber.repositories.responses.giphy.GiphyGif;
import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils; import awais.instagrabber.utils.Utils;
import retrofit2.Call; import retrofit2.Call;
@ -182,6 +184,11 @@ public class DirectMessagesService extends BaseService {
return broadcast(new ReactionBroadcastOptions(clientContext, threadIdOrUserIds, itemId, emoji, delete)); return broadcast(new ReactionBroadcastOptions(clientContext, threadIdOrUserIds, itemId, emoji, delete));
} }
public Call<DirectThreadBroadcastResponse> broadcastAnimatedMedia(final String clientContext,
final ThreadIdOrUserIds threadIdOrUserIds,
final GiphyGif giphyGif) {
return broadcast(new AnimatedMediaBroadcastOptions(clientContext, threadIdOrUserIds, giphyGif));
}
private Call<DirectThreadBroadcastResponse> broadcast(@NonNull final BroadcastOptions broadcastOptions) { private Call<DirectThreadBroadcastResponse> broadcast(@NonNull final BroadcastOptions broadcastOptions) {
if (TextUtils.isEmpty(broadcastOptions.getClientContext())) { if (TextUtils.isEmpty(broadcastOptions.getClientContext())) {

View File

@ -0,0 +1,33 @@
package awais.instagrabber.webservices;
import awais.instagrabber.repositories.GifRepository;
import awais.instagrabber.repositories.responses.giphy.GiphyGifResponse;
import retrofit2.Call;
import retrofit2.Retrofit;
public class GifService extends BaseService {
private final GifRepository repository;
private static GifService instance;
private GifService() {
final Retrofit retrofit = getRetrofitBuilder()
.baseUrl("https://i.instagram.com")
.build();
repository = retrofit.create(GifRepository.class);
}
public static GifService getInstance() {
if (instance == null) {
instance = new GifService();
}
return instance;
}
public Call<GiphyGifResponse> searchGiphyGifs(final String query,
final boolean includeGifs) {
final String mediaTypes = includeGifs ? "[\"giphy_gifs\",\"giphy\"]" : "[\"giphy\"]";
return repository.searchGiphyGifs("direct", query, mediaTypes);
}
}

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M12.25,9c0.41,0 0.75,0.34 0.75,0.75v4.5c0,0.41 -0.34,0.75 -0.75,0.75s-0.75,-0.34 -0.75,-0.75v-4.5c0,-0.41 0.34,-0.75 0.75,-0.75zM10,9.75c0,-0.41 -0.34,-0.75 -0.75,-0.75L6,9c-0.6,0 -1,0.5 -1,1v4c0,0.5 0.4,1 1,1h3c0.6,0 1,-0.5 1,-1v-1.25c0,-0.41 -0.34,-0.75 -0.75,-0.75s-0.75,0.34 -0.75,0.75v0.75h-2v-3h2.75c0.41,0 0.75,-0.34 0.75,-0.75zM19,9.75c0,-0.41 -0.34,-0.75 -0.75,-0.75L15.5,9c-0.55,0 -1,0.45 -1,1v4.25c0,0.41 0.34,0.75 0.75,0.75s0.75,-0.34 0.75,-0.75L16,13h1.25c0.41,0 0.75,-0.34 0.75,-0.75s-0.34,-0.75 -0.75,-0.75L16,11.5v-1h2.25c0.41,0 0.75,-0.34 0.75,-0.75z"/>
</vector>

View File

@ -120,7 +120,8 @@
app:layout_constraintBottom_toBottomOf="@id/input" app:layout_constraintBottom_toBottomOf="@id/input"
app:layout_constraintEnd_toStartOf="@id/send" app:layout_constraintEnd_toStartOf="@id/send"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/input" /> app:layout_constraintTop_toTopOf="@id/input"
tools:visibility="visible" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/emoji_toggle" android:id="@+id/emoji_toggle"
@ -143,7 +144,8 @@
app:rippleColor="@color/grey_500" app:rippleColor="@color/grey_500"
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.App.Button.Circle" app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.App.Button.Circle"
app:strokeColor="@color/black" app:strokeColor="@color/black"
app:strokeWidth="1dp" /> app:strokeWidth="1dp"
tools:visibility="visible" />
<awais.instagrabber.customviews.KeyNotifyingEmojiEditText <awais.instagrabber.customviews.KeyNotifyingEmojiEditText
android:id="@+id/input" android:id="@+id/input"
@ -158,16 +160,32 @@
android:textColorHint="@color/grey_500" android:textColorHint="@color/grey_500"
android:visibility="gone" android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/camera" app:layout_constraintEnd_toStartOf="@id/gif"
app:layout_constraintStart_toEndOf="@id/emoji_toggle" app:layout_constraintStart_toEndOf="@id/emoji_toggle"
app:layout_constraintTop_toBottomOf="@id/reply_preview_text" app:layout_constraintTop_toBottomOf="@id/reply_preview_text"
app:layout_goneMarginBottom="4dp" app:layout_goneMarginBottom="4dp"
app:layout_goneMarginEnd="24dp" /> app:layout_goneMarginEnd="24dp"
tools:visibility="visible" />
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/gif"
android:layout_width="32dp"
android:layout_height="0dp"
android:background="@android:color/transparent"
android:scaleType="fitCenter"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/input_bg"
app:layout_constraintEnd_toStartOf="@id/camera"
app:layout_constraintStart_toEndOf="@id/input"
app:layout_constraintTop_toTopOf="@id/input"
app:srcCompat="@drawable/ic_round_gif_24"
tools:visibility="visible" />
<androidx.appcompat.widget.AppCompatImageButton <androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/camera" android:id="@+id/camera"
android:layout_width="32dp" android:layout_width="32dp"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_marginStart="4dp"
android:background="@android:color/transparent" android:background="@android:color/transparent"
android:paddingStart="4dp" android:paddingStart="4dp"
android:paddingEnd="4dp" android:paddingEnd="4dp"
@ -175,10 +193,10 @@
android:visibility="gone" android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/input_bg" app:layout_constraintBottom_toBottomOf="@id/input_bg"
app:layout_constraintEnd_toStartOf="@id/gallery" app:layout_constraintEnd_toStartOf="@id/gallery"
app:layout_constraintStart_toEndOf="@id/input" app:layout_constraintStart_toEndOf="@id/gif"
app:layout_constraintTop_toTopOf="@id/input" app:layout_constraintTop_toTopOf="@id/input"
app:srcCompat="@drawable/ic_camera_24" app:srcCompat="@drawable/ic_camera_24"
tools:visibility="gone" /> tools:visibility="visible" />
<androidx.appcompat.widget.AppCompatImageButton <androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/gallery" android:id="@+id/gallery"
@ -196,7 +214,7 @@
app:layout_constraintEnd_toStartOf="@id/send" app:layout_constraintEnd_toStartOf="@id/send"
app:layout_constraintStart_toEndOf="@id/camera" app:layout_constraintStart_toEndOf="@id/camera"
app:layout_constraintTop_toTopOf="@id/input" app:layout_constraintTop_toTopOf="@id/input"
tools:visibility="gone" /> tools:visibility="visible" />
<awais.instagrabber.customviews.RecordView <awais.instagrabber.customviews.RecordView
android:id="@+id/record_view" android:id="@+id/record_view"
@ -214,7 +232,7 @@
app:slide_to_cancel_margin_right="16dp" app:slide_to_cancel_margin_right="16dp"
app:slide_to_cancel_text="Slide To Cancel" app:slide_to_cancel_text="Slide To Cancel"
app:slide_to_cancel_text_color="@color/white" app:slide_to_cancel_text_color="@color/white"
tools:visibility="gone" /> tools:visibility="visible" />
<awais.instagrabber.customviews.RecordButton <awais.instagrabber.customviews.RecordButton
android:id="@+id/send" android:id="@+id/send"
@ -231,7 +249,8 @@
app:layout_constraintBottom_toBottomOf="@id/input_bg" app:layout_constraintBottom_toBottomOf="@id/input_bg"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/input_bg" app:layout_constraintStart_toEndOf="@id/input_bg"
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.App.Button.Circle" /> app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.App.Button.Circle"
tools:visibility="visible" />
<awais.instagrabber.customviews.emoji.EmojiPicker <awais.instagrabber.customviews.emoji.EmojiPicker
android:id="@+id/emoji_picker" android:id="@+id/emoji_picker"
@ -241,7 +260,8 @@
android:visibility="gone" android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" /> app:layout_constraintStart_toStartOf="parent"
tools:visibility="visible" />
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/accept_pending_request_question" android:id="@+id/accept_pending_request_question"
@ -255,7 +275,7 @@
android:visibility="gone" android:visibility="gone"
app:layout_constraintBottom_toTopOf="@id/decline" app:layout_constraintBottom_toTopOf="@id/decline"
app:layout_constraintTop_toBottomOf="@id/chats_barrier" app:layout_constraintTop_toBottomOf="@id/chats_barrier"
tools:visibility="visible" /> tools:visibility="gone" />
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/decline" android:id="@+id/decline"
@ -272,7 +292,7 @@
app:layout_constraintEnd_toStartOf="@id/accept" app:layout_constraintEnd_toStartOf="@id/accept"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/accept_pending_request_question" app:layout_constraintTop_toBottomOf="@id/accept_pending_request_question"
tools:visibility="visible" /> tools:visibility="gone" />
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/accept" android:id="@+id/accept"
@ -288,5 +308,5 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/decline" app:layout_constraintStart_toEndOf="@id/decline"
app:layout_constraintTop_toBottomOf="@id/accept_pending_request_question" app:layout_constraintTop_toBottomOf="@id/accept_pending_request_question"
tools:visibility="visible" /> tools:visibility="gone" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,70 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:id="@+id/input_bg"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="4dp"
android:layout_marginEnd="4dp"
android:background="@drawable/bg_input"
app:layout_constraintBottom_toBottomOf="@id/input"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/input" />
<com.google.android.material.button.MaterialButton
android:id="@+id/search_icon"
style="@style/Widget.MaterialComponents.Button.Icon.NoInsets"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="2dp"
android:background="@android:color/transparent"
android:clickable="false"
android:scrollbars="none"
app:icon="@drawable/ic_search_24"
app:iconGravity="textStart"
app:iconSize="24dp"
app:iconTint="@color/grey_700"
app:layout_constraintBottom_toBottomOf="@id/input_bg"
app:layout_constraintEnd_toStartOf="@id/input"
app:layout_constraintStart_toStartOf="@id/input_bg"
app:layout_constraintTop_toTopOf="@id/input"
app:rippleColor="@color/grey_500"
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.App.Button.Circle"
app:strokeColor="@color/black"
app:strokeWidth="1dp" />
<androidx.emoji.widget.EmojiAppCompatEditText
android:id="@+id/input"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="20dp"
android:layout_marginBottom="4dp"
android:background="@android:color/transparent"
android:hint="@string/search_giphy"
android:paddingTop="12dp"
android:paddingBottom="12dp"
android:textColor="@color/white"
android:textColorHint="@color/grey_500"
app:layout_constraintBottom_toTopOf="@id/gif_list"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/search_icon"
app:layout_constraintTop_toTopOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/gif_list"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/input" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -185,6 +185,7 @@
<string name="dms_inbox_shared_video">%s shared a video</string> <string name="dms_inbox_shared_video">%s shared a video</string>
<string name="dms_inbox_shared_message">%s sent a message</string> <string name="dms_inbox_shared_message">%s sent a message</string>
<string name="dms_inbox_shared_gif">%s shared a gif</string> <string name="dms_inbox_shared_gif">%s shared a gif</string>
<string name="dms_inbox_shared_sticker">%s shared a sticker</string>
<string name="dms_inbox_shared_profile">%s shared a profile: @%s</string> <string name="dms_inbox_shared_profile">%s shared a profile: @%s</string>
<string name="dms_inbox_shared_location">%s shared a location: %s</string> <string name="dms_inbox_shared_location">%s shared a location: %s</string>
<string name="dms_inbox_shared_highlight">%s shared a story highlight by @%s</string> <string name="dms_inbox_shared_highlight">%s shared a story highlight by @%s</string>
@ -490,4 +491,5 @@
<string name="auto_refresh_every">Auto refresh every</string> <string name="auto_refresh_every">Auto refresh every</string>
<string name="secs">secs</string> <string name="secs">secs</string>
<string name="mins">mins</string> <string name="mins">mins</string>
<string name="search_giphy">Search GIPHY</string>
</resources> </resources>