Ammar Githam
4 years ago
22 changed files with 952 additions and 29 deletions
-
107app/src/main/java/awais/instagrabber/adapters/GifItemsAdapter.java
-
150app/src/main/java/awais/instagrabber/dialogs/GifPickerBottomDialogFragment.java
-
31app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java
-
19app/src/main/java/awais/instagrabber/managers/ThreadManager.java
-
3app/src/main/java/awais/instagrabber/models/enums/BroadcastItemType.java
-
14app/src/main/java/awais/instagrabber/repositories/GifRepository.java
-
27app/src/main/java/awais/instagrabber/repositories/requests/directmessages/AnimatedMediaBroadcastOptions.java
-
70app/src/main/java/awais/instagrabber/repositories/responses/giphy/GiphyGif.java
-
59app/src/main/java/awais/instagrabber/repositories/responses/giphy/GiphyGifImage.java
-
40app/src/main/java/awais/instagrabber/repositories/responses/giphy/GiphyGifImages.java
-
46app/src/main/java/awais/instagrabber/repositories/responses/giphy/GiphyGifResponse.java
-
47app/src/main/java/awais/instagrabber/repositories/responses/giphy/GiphyGifResults.java
-
30app/src/main/java/awais/instagrabber/utils/DMUtils.java
-
44app/src/main/java/awais/instagrabber/utils/DirectItemFactory.java
-
5app/src/main/java/awais/instagrabber/viewmodels/DirectThreadViewModel.java
-
121app/src/main/java/awais/instagrabber/viewmodels/GifPickerViewModel.java
-
7app/src/main/java/awais/instagrabber/webservices/DirectMessagesService.java
-
33app/src/main/java/awais/instagrabber/webservices/GifService.java
-
10app/src/main/res/drawable/ic_round_gif_24.xml
-
46app/src/main/res/layout/fragment_direct_messages_thread.xml
-
70app/src/main/res/layout/layout_gif_picker.xml
-
2app/src/main/res/values/strings.xml
@ -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); |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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); |
|||
} |
@ -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; |
|||
} |
|||
} |
@ -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 + |
|||
'}'; |
|||
} |
|||
} |
@ -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 + '\'' + |
|||
'}'; |
|||
} |
|||
} |
@ -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 + |
|||
'}'; |
|||
} |
|||
} |
@ -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 + '\'' + |
|||
'}'; |
|||
} |
|||
} |
@ -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 + |
|||
'}'; |
|||
} |
|||
} |
@ -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(); |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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> |
@ -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> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue