From e2e1200c8993e31cf1e9026ed97791a9d6ffdb82 Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Tue, 20 Apr 2021 09:45:40 -0300 Subject: [PATCH] Improvments to MP4 giphy fragment behaviour. --- .../securesms/giph/mp4/GiphyMp4Fragment.java | 15 +- .../giph/mp4/GiphyMp4PagedDataSource.java | 1 + .../securesms/giph/net/GiphyLoader.java | 87 -------- .../giph/net/GiphyStickerLoader.java | 24 --- .../securesms/giph/ui/GiphyActivity.java | 134 +----------- .../securesms/giph/ui/GiphyAdapter.java | 193 ------------------ .../securesms/giph/ui/GiphyFragment.java | 119 ----------- .../giph/ui/GiphyStickerFragment.java | 19 -- .../giph/util/InfiniteScrollListener.java | 51 ----- .../giph/util/RecyclerViewPositionHelper.java | 116 ----------- app/src/main/res/layout/giphy_activity.xml | 70 +++---- .../res/layout/giphy_activity_toolbar.xml | 2 +- app/src/main/res/layout/giphy_fragment.xml | 27 --- .../main/res/layout/giphy_mp4_fragment.xml | 19 +- app/src/main/res/layout/giphy_thumbnail.xml | 22 -- app/src/main/res/values/strings.xml | 2 +- 16 files changed, 68 insertions(+), 833 deletions(-) delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/giph/net/GiphyLoader.java delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/giph/net/GiphyStickerLoader.java delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyAdapter.java delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyFragment.java delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyStickerFragment.java delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/giph/util/InfiniteScrollListener.java delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/giph/util/RecyclerViewPositionHelper.java delete mode 100644 app/src/main/res/layout/giphy_fragment.xml delete mode 100644 app/src/main/res/layout/giphy_thumbnail.xml diff --git a/app/src/main/java/org/thoughtcrime/securesms/giph/mp4/GiphyMp4Fragment.java b/app/src/main/java/org/thoughtcrime/securesms/giph/mp4/GiphyMp4Fragment.java index 4bc21e2d5c..fd92bd76eb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/giph/mp4/GiphyMp4Fragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/giph/mp4/GiphyMp4Fragment.java @@ -5,12 +5,13 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; +import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.core.widget.ContentLoadingProgressBar; import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProviders; -import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.StaggeredGridLayoutManager; @@ -19,11 +20,9 @@ import com.google.android.exoplayer2.ui.AspectRatioFrameLayout; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; -import org.thoughtcrime.securesms.util.TextSecurePreferences; import java.util.ArrayList; import java.util.List; -import java.util.Objects; /** * Fragment which displays GyphyImages. @@ -51,6 +50,8 @@ public class GiphyMp4Fragment extends Fragment { boolean isForMms = requireArguments().getBoolean(IS_FOR_MMS, false); FrameLayout frameLayout = view.findViewById(R.id.giphy_parent); RecyclerView recycler = view.findViewById(R.id.giphy_recycler); + ContentLoadingProgressBar progressBar = view.findViewById(R.id.content_loading); + TextView nothingFound = view.findViewById(R.id.nothing_found); GiphyMp4ViewModel viewModel = ViewModelProviders.of(requireActivity(), new GiphyMp4ViewModel.Factory(isForMms)).get(GiphyMp4ViewModel.class); GiphyMp4MediaSourceFactory mediaSourceFactory = new GiphyMp4MediaSourceFactory(ApplicationDependencies.getOkHttpClient()); GiphyMp4Adapter adapter = new GiphyMp4Adapter(mediaSourceFactory, viewModel::saveToBlob); @@ -60,11 +61,13 @@ public class GiphyMp4Fragment extends Fragment { recycler.setLayoutManager(new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL)); recycler.setAdapter(adapter); recycler.setItemAnimator(null); + progressBar.show(); GiphyMp4AdapterPlaybackController.attach(recycler, callback, GiphyMp4PlaybackPolicy.maxSimultaneousPlaybackInSearchResults()); - - viewModel.getImages().observe(getViewLifecycleOwner(), adapter::submitList); - + viewModel.getImages().observe(getViewLifecycleOwner(), images -> { + nothingFound.setVisibility(images.isEmpty() ? View.VISIBLE : View.INVISIBLE); + adapter.submitList(images, progressBar::hide); + }); viewModel.getPagingController().observe(getViewLifecycleOwner(), adapter::setPagingController); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/giph/mp4/GiphyMp4PagedDataSource.java b/app/src/main/java/org/thoughtcrime/securesms/giph/mp4/GiphyMp4PagedDataSource.java index 7cf0c44246..057706623f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/giph/mp4/GiphyMp4PagedDataSource.java +++ b/app/src/main/java/org/thoughtcrime/securesms/giph/mp4/GiphyMp4PagedDataSource.java @@ -6,6 +6,7 @@ import android.text.TextUtils; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import org.signal.core.util.ThreadUtil; import org.signal.core.util.logging.Log; import org.signal.paging.PagedDataSource; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; diff --git a/app/src/main/java/org/thoughtcrime/securesms/giph/net/GiphyLoader.java b/app/src/main/java/org/thoughtcrime/securesms/giph/net/GiphyLoader.java deleted file mode 100644 index fc0e7c304f..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/giph/net/GiphyLoader.java +++ /dev/null @@ -1,87 +0,0 @@ -package org.thoughtcrime.securesms.giph.net; - - -import android.content.Context; -import android.net.Uri; -import android.text.TextUtils; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.annimon.stream.Stream; - -import org.signal.core.util.logging.Log; -import org.thoughtcrime.securesms.giph.model.GiphyImage; -import org.thoughtcrime.securesms.giph.model.GiphyResponse; -import org.thoughtcrime.securesms.net.ContentProxySelector; -import org.thoughtcrime.securesms.net.StandardUserAgentInterceptor; -import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess; -import org.thoughtcrime.securesms.util.AsyncLoader; -import org.thoughtcrime.securesms.util.JsonUtils; - -import java.io.IOException; -import java.util.LinkedList; -import java.util.List; - -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; - -public abstract class GiphyLoader extends AsyncLoader> { - - private static final String TAG = Log.tag(GiphyLoader.class); - - public static int PAGE_SIZE = 100; - - @Nullable private String searchString; - - private final OkHttpClient client; - - protected GiphyLoader(@NonNull Context context, @Nullable String searchString) { - super(context); - this.searchString = searchString; - this.client = new OkHttpClient.Builder() - .proxySelector(new ContentProxySelector()) - .addInterceptor(new StandardUserAgentInterceptor()) - .dns(SignalServiceNetworkAccess.DNS) - .build(); - } - - @Override - public List loadInBackground() { - return loadPage(0); - } - - public @NonNull List loadPage(int offset) { - try { - String url; - - if (TextUtils.isEmpty(searchString)) url = String.format(getTrendingUrl(), offset); - else url = String.format(getSearchUrl(), offset, Uri.encode(searchString)); - - Request request = new Request.Builder().url(url).build(); - Response response = client.newCall(request).execute(); - - if (!response.isSuccessful()) { - throw new IOException("Unexpected code " + response); - } - - GiphyResponse giphyResponse = JsonUtils.fromJson(response.body().byteStream(), GiphyResponse.class); - List results = Stream.of(giphyResponse.getData()) - .filterNot(g -> TextUtils.isEmpty(g.getGifUrl())) - .filterNot(g -> TextUtils.isEmpty(g.getGifMmsUrl())) - .filterNot(g -> TextUtils.isEmpty(g.getStillUrl())) - .toList(); - - if (results == null) return new LinkedList<>(); - else return results; - - } catch (IOException e) { - Log.w(TAG, e); - return new LinkedList<>(); - } - } - - protected abstract String getTrendingUrl(); - protected abstract String getSearchUrl(); -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/giph/net/GiphyStickerLoader.java b/app/src/main/java/org/thoughtcrime/securesms/giph/net/GiphyStickerLoader.java deleted file mode 100644 index 06fcb18a94..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/giph/net/GiphyStickerLoader.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.thoughtcrime.securesms.giph.net; - - -import android.content.Context; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -public class GiphyStickerLoader extends GiphyLoader { - - public GiphyStickerLoader(@NonNull Context context, @Nullable String searchString) { - super(context, searchString); - } - - @Override - protected String getTrendingUrl() { - return "https://api.giphy.com/v1/stickers/trending?api_key=3o6ZsYH6U6Eri53TXy&offset=%d&limit=" + PAGE_SIZE; - } - - @Override - protected String getSearchUrl() { - return "https://api.giphy.com/v1/stickers/search?api_key=3o6ZsYH6U6Eri53TXy&offset=%d&limit=" + PAGE_SIZE + "&q=%s"; - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyActivity.java b/app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyActivity.java index d48ed6b501..db809ff491 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyActivity.java @@ -1,51 +1,29 @@ package org.thoughtcrime.securesms.giph.ui; -import android.annotation.SuppressLint; -import android.content.Context; import android.content.Intent; -import android.net.Uri; -import android.os.AsyncTask; import android.os.Bundle; -import android.view.View; import android.widget.Toast; import androidx.annotation.ColorInt; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.core.app.ActivityCompat; import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentManager; -import androidx.fragment.app.FragmentPagerAdapter; import androidx.lifecycle.ViewModelProviders; -import androidx.viewpager.widget.ViewPager; -import com.google.android.material.tabs.TabLayout; - -import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.PassphraseRequiredActivity; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.giph.mp4.GiphyMp4Fragment; import org.thoughtcrime.securesms.giph.mp4.GiphyMp4SaveResult; import org.thoughtcrime.securesms.giph.mp4.GiphyMp4ViewModel; -import org.thoughtcrime.securesms.providers.BlobProvider; import org.thoughtcrime.securesms.util.DynamicDarkToolbarTheme; import org.thoughtcrime.securesms.util.DynamicLanguage; import org.thoughtcrime.securesms.util.DynamicTheme; -import org.thoughtcrime.securesms.util.MediaUtil; import org.thoughtcrime.securesms.util.WindowUtil; import org.thoughtcrime.securesms.util.views.SimpleProgressDialog; -import java.io.IOException; -import java.util.concurrent.ExecutionException; - -public class GiphyActivity extends PassphraseRequiredActivity - implements GiphyActivityToolbar.OnFilterChangedListener, - GiphyAdapter.OnItemClickListener -{ - - private static final String TAG = Log.tag(GiphyActivity.class); +public class GiphyActivity extends PassphraseRequiredActivity implements GiphyActivityToolbar.OnFilterChangedListener { public static final String EXTRA_IS_MMS = "extra_is_mms"; public static final String EXTRA_WIDTH = "extra_width"; @@ -56,12 +34,6 @@ public class GiphyActivity extends PassphraseRequiredActivity private final DynamicTheme dynamicTheme = new DynamicDarkToolbarTheme(); private final DynamicLanguage dynamicLanguage = new DynamicLanguage(); - private Fragment gifFragment; - private GiphyStickerFragment stickerFragment; - private boolean forMms; - - private GiphyAdapter.GiphyViewHolder finishingImage; - private GiphyMp4ViewModel giphyMp4ViewModel; private AlertDialog progressDialog; @@ -75,17 +47,20 @@ public class GiphyActivity extends PassphraseRequiredActivity public void onCreate(Bundle bundle, boolean ready) { setContentView(R.layout.giphy_activity); - forMms = getIntent().getBooleanExtra(EXTRA_IS_MMS, false); - giphyMp4ViewModel = ViewModelProviders.of(this, new GiphyMp4ViewModel.Factory(forMms)).get(GiphyMp4ViewModel.class); + final boolean forMms = getIntent().getBooleanExtra(EXTRA_IS_MMS, false); + giphyMp4ViewModel = ViewModelProviders.of(this, new GiphyMp4ViewModel.Factory(forMms)).get(GiphyMp4ViewModel.class); giphyMp4ViewModel.getSaveResultEvents().observe(this, this::handleGiphyMp4SaveResult); initializeToolbar(); - initializeResources(); + + Fragment fragment = GiphyMp4Fragment.create(forMms); + getSupportFragmentManager().beginTransaction() + .replace(R.id.fragment_container, fragment) + .commit(); } private void initializeToolbar() { - GiphyActivityToolbar toolbar = findViewById(R.id.giphy_toolbar); toolbar.setOnFilterChangedListener(this); @@ -99,21 +74,6 @@ public class GiphyActivity extends PassphraseRequiredActivity getSupportActionBar().setDisplayShowTitleEnabled(false); } - private void initializeResources() { - ViewPager viewPager = findViewById(R.id.giphy_pager); - TabLayout tabLayout = findViewById(R.id.tab_layout); - - this.gifFragment = GiphyMp4Fragment.create(forMms); - this.stickerFragment = new GiphyStickerFragment(); - - stickerFragment.setClickListener(this); - - viewPager.setAdapter(new GiphyFragmentPagerAdapter(this, getSupportFragmentManager(), - gifFragment, stickerFragment)); - tabLayout.setupWithViewPager(viewPager); - tabLayout.setBackgroundColor(getConversationColor()); - } - private void handleGiphyMp4SaveResult(@NonNull GiphyMp4SaveResult result) { if (result instanceof GiphyMp4SaveResult.Success) { hideProgressDialog(); @@ -154,83 +114,5 @@ public class GiphyActivity extends PassphraseRequiredActivity @Override public void onFilterChanged(String filter) { giphyMp4ViewModel.updateSearchQuery(filter); - this.stickerFragment.setSearchString(filter); } - - @SuppressLint("StaticFieldLeak") - @Override - public void onClick(final GiphyAdapter.GiphyViewHolder viewHolder) { - if (finishingImage != null) finishingImage.gifProgress.setVisibility(View.GONE); - finishingImage = viewHolder; - finishingImage.gifProgress.setVisibility(View.VISIBLE); - - new AsyncTask() { - @Override - protected Uri doInBackground(Void... params) { - try { - byte[] data = viewHolder.getData(forMms); - - return BlobProvider.getInstance() - .forData(data) - .withMimeType(MediaUtil.IMAGE_GIF) - .createForSingleSessionOnDisk(GiphyActivity.this); - } catch (InterruptedException | ExecutionException | IOException e) { - Log.w(TAG, e); - return null; - } - } - - protected void onPostExecute(@Nullable Uri uri) { - if (uri == null) { - Toast.makeText(GiphyActivity.this, R.string.GiphyActivity_error_while_retrieving_full_resolution_gif, Toast.LENGTH_LONG).show(); - } else if (viewHolder == finishingImage) { - Intent intent = new Intent(); - intent.setData(uri); - intent.putExtra(EXTRA_WIDTH, viewHolder.image.getGifWidth()); - intent.putExtra(EXTRA_HEIGHT, viewHolder.image.getGifHeight()); - intent.putExtra(EXTRA_BORDERLESS, viewHolder.image.isSticker()); - setResult(RESULT_OK, intent); - finish(); - } else { - Log.w(TAG, "Resolved Uri is no longer the selected element..."); - } - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - - private static class GiphyFragmentPagerAdapter extends FragmentPagerAdapter { - - private final Context context; - private final Fragment gifFragment; - private final Fragment stickerFragment; - - private GiphyFragmentPagerAdapter(@NonNull Context context, - @NonNull FragmentManager fragmentManager, - @NonNull Fragment gifFragment, - @NonNull Fragment stickerFragment) - { - super(fragmentManager); - this.context = context.getApplicationContext(); - this.gifFragment = gifFragment; - this.stickerFragment = stickerFragment; - } - - @Override - public Fragment getItem(int position) { - if (position == 0) return gifFragment; - else return stickerFragment; - } - - @Override - public int getCount() { - return 2; - } - - @Override - public CharSequence getPageTitle(int position) { - if (position == 0) return context.getString(R.string.GiphyFragmentPagerAdapter_gifs); - else return context.getString(R.string.GiphyFragmentPagerAdapter_stickers); - } - } - } diff --git a/app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyAdapter.java deleted file mode 100644 index 13f14c099a..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyAdapter.java +++ /dev/null @@ -1,193 +0,0 @@ -package org.thoughtcrime.securesms.giph.ui; - - -import android.content.Context; -import android.graphics.drawable.ColorDrawable; -import android.graphics.drawable.Drawable; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ProgressBar; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.recyclerview.widget.RecyclerView; - -import com.bumptech.glide.RequestBuilder; -import com.bumptech.glide.load.DataSource; -import com.bumptech.glide.load.engine.DiskCacheStrategy; -import com.bumptech.glide.load.engine.GlideException; -import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions; -import com.bumptech.glide.load.resource.gif.GifDrawable; -import com.bumptech.glide.request.RequestListener; -import com.bumptech.glide.request.target.Target; -import com.bumptech.glide.util.ByteBufferUtil; - -import org.signal.core.util.logging.Log; -import org.thoughtcrime.securesms.R; -import org.thoughtcrime.securesms.color.MaterialColor; -import org.thoughtcrime.securesms.giph.model.ChunkedImageUrl; -import org.thoughtcrime.securesms.giph.model.GiphyImage; -import org.thoughtcrime.securesms.mms.GlideApp; -import org.thoughtcrime.securesms.mms.GlideRequests; -import org.thoughtcrime.securesms.util.Util; - -import java.util.List; -import java.util.concurrent.ExecutionException; - - -class GiphyAdapter extends RecyclerView.Adapter { - - private static final String TAG = Log.tag(GiphyAdapter.class); - - private final Context context; - private final GlideRequests glideRequests; - - private List images; - private OnItemClickListener listener; - - class GiphyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, RequestListener { - - public AspectRatioImageView thumbnail; - public GiphyImage image; - public ProgressBar gifProgress; - public volatile boolean modelReady; - - GiphyViewHolder(View view) { - super(view); - thumbnail = view.findViewById(R.id.thumbnail); - gifProgress = view.findViewById(R.id.gif_progress); - thumbnail.setOnClickListener(this); - gifProgress.setVisibility(View.GONE); - } - - @Override - public void onClick(View v) { - if (listener != null) listener.onClick(this); - } - - @Override - public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) { - Log.w(TAG, e); - - synchronized (this) { - if (new ChunkedImageUrl(image.getGifUrl(), image.getGifSize()).equals(model)) { - this.modelReady = true; - notifyAll(); - } - } - - return false; - } - - @Override - public boolean onResourceReady(Drawable resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) { - synchronized (this) { - if (new ChunkedImageUrl(image.getGifUrl(), image.getGifSize()).equals(model)) { - this.modelReady = true; - notifyAll(); - } - } - - return false; - } - - - public byte[] getData(boolean forMms) throws ExecutionException, InterruptedException { - synchronized (this) { - while (!modelReady) { - Util.wait(this, 0); - } - } - - GifDrawable drawable = glideRequests.asGif() - .load(forMms ? new ChunkedImageUrl(image.getGifMmsUrl(), image.getMmsGifSize()) : - new ChunkedImageUrl(image.getGifUrl(), image.getGifSize())) - .submit(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL) - .get(); - - return ByteBufferUtil.toBytes(drawable.getBuffer()); - } - - public synchronized void setModelReady() { - this.modelReady = true; - notifyAll(); - } - } - - GiphyAdapter(@NonNull Context context, @NonNull GlideRequests glideRequests, @NonNull List images) { - this.context = context.getApplicationContext(); - this.glideRequests = glideRequests; - this.images = images; - } - - public void setImages(@NonNull List images) { - this.images = images; - notifyDataSetChanged(); - } - - public void addImages(List images) { - this.images.addAll(images); - notifyDataSetChanged(); - } - - @Override - public @NonNull GiphyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - View itemView = LayoutInflater.from(parent.getContext()) - .inflate(R.layout.giphy_thumbnail, parent, false); - - return new GiphyViewHolder(itemView); - } - - @Override - public void onBindViewHolder(@NonNull GiphyViewHolder holder, int position) { - GiphyImage image = images.get(position); - - holder.modelReady = false; - holder.image = image; - holder.thumbnail.setAspectRatio(image.getGifAspectRatio()); - holder.gifProgress.setVisibility(View.GONE); - - RequestBuilder thumbnailRequest = GlideApp.with(context) - .load(new ChunkedImageUrl(image.getStillUrl(), image.getStillSize())) - .diskCacheStrategy(DiskCacheStrategy.ALL); - - if (Util.isLowMemory(context)) { - glideRequests.load(new ChunkedImageUrl(image.getStillUrl(), image.getStillSize())) - .placeholder(new ColorDrawable(Util.getRandomElement(MaterialColor.values()).toConversationColor(context))) - .diskCacheStrategy(DiskCacheStrategy.ALL) - .transition(DrawableTransitionOptions.withCrossFade()) - .listener(holder) - .into(holder.thumbnail); - - holder.setModelReady(); - } else { - glideRequests.load(new ChunkedImageUrl(image.getGifUrl(), image.getGifSize())) - .thumbnail(thumbnailRequest) - .placeholder(new ColorDrawable(Util.getRandomElement(MaterialColor.values()).toConversationColor(context))) - .diskCacheStrategy(DiskCacheStrategy.ALL) - .transition(DrawableTransitionOptions.withCrossFade()) - .listener(holder) - .into(holder.thumbnail); - } - } - - @Override - public void onViewRecycled(@NonNull GiphyViewHolder holder) { - super.onViewRecycled(holder); - glideRequests.clear(holder.thumbnail); - } - - @Override - public int getItemCount() { - return images.size(); - } - - public void setListener(OnItemClickListener listener) { - this.listener = listener; - } - - public interface OnItemClickListener { - void onClick(GiphyViewHolder viewHolder); - } -} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyFragment.java b/app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyFragment.java deleted file mode 100644 index e19988a619..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyFragment.java +++ /dev/null @@ -1,119 +0,0 @@ -package org.thoughtcrime.securesms.giph.ui; - -import android.os.AsyncTask; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ProgressBar; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.loader.app.LoaderManager; -import androidx.loader.content.Loader; -import androidx.recyclerview.widget.DefaultItemAnimator; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; -import androidx.recyclerview.widget.StaggeredGridLayoutManager; - -import org.signal.core.util.logging.Log; -import org.thoughtcrime.securesms.LoggingFragment; -import org.thoughtcrime.securesms.R; -import org.thoughtcrime.securesms.giph.model.GiphyImage; -import org.thoughtcrime.securesms.giph.net.GiphyLoader; -import org.thoughtcrime.securesms.giph.util.InfiniteScrollListener; -import org.thoughtcrime.securesms.mms.GlideApp; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.ViewUtil; - -import java.util.LinkedList; -import java.util.List; - -public abstract class GiphyFragment extends LoggingFragment implements LoaderManager.LoaderCallbacks>, GiphyAdapter.OnItemClickListener { - - private static final String TAG = Log.tag(GiphyFragment.class); - - private GiphyAdapter giphyAdapter; - private RecyclerView recyclerView; - private ProgressBar loadingProgress; - private TextView noResultsView; - private GiphyAdapter.OnItemClickListener listener; - - protected String searchString; - - @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup viewGroup, Bundle bundle) { - ViewGroup container = ViewUtil.inflate(inflater, viewGroup, R.layout.giphy_fragment); - this.recyclerView = container.findViewById(R.id.giphy_list); - this.loadingProgress = container.findViewById(R.id.loading_progress); - this.noResultsView = container.findViewById(R.id.no_results); - - return container; - } - - @Override - public void onActivityCreated(Bundle bundle) { - super.onActivityCreated(bundle); - - this.giphyAdapter = new GiphyAdapter(getActivity(), GlideApp.with(this), new LinkedList<>()); - this.giphyAdapter.setListener(this); - - this.recyclerView.setLayoutManager(new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL)); - this.recyclerView.setItemAnimator(new DefaultItemAnimator()); - this.recyclerView.setAdapter(giphyAdapter); - this.recyclerView.addOnScrollListener(new GiphyScrollListener()); - - getLoaderManager().initLoader(0, null, this); - } - - @Override - public void onLoadFinished(@NonNull Loader> loader, @NonNull List data) { - this.loadingProgress.setVisibility(View.GONE); - - if (data.isEmpty()) noResultsView.setVisibility(View.VISIBLE); - else noResultsView.setVisibility(View.GONE); - - this.giphyAdapter.setImages(data); - } - - @Override - public void onLoaderReset(@NonNull Loader> loader) { - noResultsView.setVisibility(View.GONE); - this.giphyAdapter.setImages(new LinkedList()); - } - - public void setClickListener(GiphyAdapter.OnItemClickListener listener) { - this.listener = listener; - } - - public void setSearchString(@Nullable String searchString) { - this.searchString = searchString; - this.noResultsView.setVisibility(View.GONE); - this.getLoaderManager().restartLoader(0, null, this); - } - - @Override - public void onClick(GiphyAdapter.GiphyViewHolder viewHolder) { - if (listener != null) listener.onClick(viewHolder); - } - - private class GiphyScrollListener extends InfiniteScrollListener { - @Override - public void onLoadMore(final int currentPage) { - final Loader> loader = getLoaderManager().getLoader(0); - if (loader == null) return; - - new AsyncTask>() { - @Override - protected List doInBackground(Void... params) { - return ((GiphyLoader)loader).loadPage(currentPage * GiphyLoader.PAGE_SIZE); - } - - protected void onPostExecute(List images) { - giphyAdapter.addImages(images); - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyStickerFragment.java b/app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyStickerFragment.java deleted file mode 100644 index d6adbbb5b3..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/giph/ui/GiphyStickerFragment.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.thoughtcrime.securesms.giph.ui; - - -import android.os.Bundle; - -import androidx.annotation.NonNull; -import androidx.loader.content.Loader; - -import org.thoughtcrime.securesms.giph.model.GiphyImage; -import org.thoughtcrime.securesms.giph.net.GiphyStickerLoader; - -import java.util.List; - -public class GiphyStickerFragment extends GiphyFragment { - @Override - public @NonNull Loader> onCreateLoader(int id, Bundle args) { - return new GiphyStickerLoader(getActivity(), searchString); - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/giph/util/InfiniteScrollListener.java b/app/src/main/java/org/thoughtcrime/securesms/giph/util/InfiniteScrollListener.java deleted file mode 100644 index 997a11e772..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/giph/util/InfiniteScrollListener.java +++ /dev/null @@ -1,51 +0,0 @@ -// From https://gist.github.com/mipreamble/b6d4b3d65b0b4775a22e#file-recyclerviewpositionhelper-java - -package org.thoughtcrime.securesms.giph.util; - -import androidx.annotation.NonNull; -import androidx.recyclerview.widget.RecyclerView; - -import org.signal.core.util.logging.Log; - -public abstract class InfiniteScrollListener extends RecyclerView.OnScrollListener { - - public static String TAG = Log.tag(InfiniteScrollListener.class); - - private int previousTotal = 0; // The total number of items in the dataset after the last load - private boolean loading = true; // True if we are still waiting for the last set of data to load. - private int visibleThreshold = 5; // The minimum amount of items to have below your current scroll position before loading more. - - int firstVisibleItem, visibleItemCount, totalItemCount; - - private int currentPage = 1; - - @Override - public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { - super.onScrolled(recyclerView, dx, dy); - - RecyclerViewPositionHelper recyclerViewPositionHelper = RecyclerViewPositionHelper.createHelper(recyclerView); - - visibleItemCount = recyclerView.getChildCount(); - totalItemCount = recyclerViewPositionHelper.getItemCount(); - firstVisibleItem = recyclerViewPositionHelper.findFirstVisibleItemPosition(); - - if (loading) { - if (totalItemCount > previousTotal) { - loading = false; - previousTotal = totalItemCount; - } - } - if (!loading && (totalItemCount - visibleItemCount) - <= (firstVisibleItem + visibleThreshold)) { - // End has been reached - // Do something - currentPage++; - - onLoadMore(currentPage); - - loading = true; - } - } - - public abstract void onLoadMore(int currentPage); -} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/giph/util/RecyclerViewPositionHelper.java b/app/src/main/java/org/thoughtcrime/securesms/giph/util/RecyclerViewPositionHelper.java deleted file mode 100644 index 4de39d22fd..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/giph/util/RecyclerViewPositionHelper.java +++ /dev/null @@ -1,116 +0,0 @@ -// From https://gist.github.com/mipreamble/b6d4b3d65b0b4775a22e#file-recyclerviewpositionhelper-java - -package org.thoughtcrime.securesms.giph.util; - - -import android.view.View; - -import androidx.recyclerview.widget.OrientationHelper; -import androidx.recyclerview.widget.RecyclerView; - -public class RecyclerViewPositionHelper { - - final RecyclerView recyclerView; - final RecyclerView.LayoutManager layoutManager; - - RecyclerViewPositionHelper(RecyclerView recyclerView) { - this.recyclerView = recyclerView; - this.layoutManager = recyclerView.getLayoutManager(); - } - - public static RecyclerViewPositionHelper createHelper(RecyclerView recyclerView) { - if (recyclerView == null) { - throw new NullPointerException("Recycler View is null"); - } - return new RecyclerViewPositionHelper(recyclerView); - } - - /** - * Returns the adapter item count. - * - * @return The total number on items in a layout manager - */ - public int getItemCount() { - return layoutManager == null ? 0 : layoutManager.getItemCount(); - } - - /** - * Returns the adapter position of the first visible view. This position does not include - * adapter changes that were dispatched after the last layout pass. - * - * @return The adapter position of the first visible item or {@link RecyclerView#NO_POSITION} if - * there aren't any visible items. - */ - public int findFirstVisibleItemPosition() { - final View child = findOneVisibleChild(0, layoutManager.getChildCount(), false, true); - return child == null ? RecyclerView.NO_POSITION : recyclerView.getChildAdapterPosition(child); - } - - /** - * Returns the adapter position of the first fully visible view. This position does not include - * adapter changes that were dispatched after the last layout pass. - * - * @return The adapter position of the first fully visible item or - * {@link RecyclerView#NO_POSITION} if there aren't any visible items. - */ - public int findFirstCompletelyVisibleItemPosition() { - final View child = findOneVisibleChild(0, layoutManager.getChildCount(), true, false); - return child == null ? RecyclerView.NO_POSITION : recyclerView.getChildAdapterPosition(child); - } - - /** - * Returns the adapter position of the last visible view. This position does not include - * adapter changes that were dispatched after the last layout pass. - * - * @return The adapter position of the last visible view or {@link RecyclerView#NO_POSITION} if - * there aren't any visible items - */ - public int findLastVisibleItemPosition() { - final View child = findOneVisibleChild(layoutManager.getChildCount() - 1, -1, false, true); - return child == null ? RecyclerView.NO_POSITION : recyclerView.getChildAdapterPosition(child); - } - - /** - * Returns the adapter position of the last fully visible view. This position does not include - * adapter changes that were dispatched after the last layout pass. - * - * @return The adapter position of the last fully visible view or - * {@link RecyclerView#NO_POSITION} if there aren't any visible items. - */ - public int findLastCompletelyVisibleItemPosition() { - final View child = findOneVisibleChild(layoutManager.getChildCount() - 1, -1, true, false); - return child == null ? RecyclerView.NO_POSITION : recyclerView.getChildAdapterPosition(child); - } - - View findOneVisibleChild(int fromIndex, int toIndex, boolean completelyVisible, - boolean acceptPartiallyVisible) { - OrientationHelper helper; - if (layoutManager.canScrollVertically()) { - helper = OrientationHelper.createVerticalHelper(layoutManager); - } else { - helper = OrientationHelper.createHorizontalHelper(layoutManager); - } - - final int start = helper.getStartAfterPadding(); - final int end = helper.getEndAfterPadding(); - final int next = toIndex > fromIndex ? 1 : -1; - View partiallyVisible = null; - for (int i = fromIndex; i != toIndex; i += next) { - final View child = layoutManager.getChildAt(i); - final int childStart = helper.getDecoratedStart(child); - final int childEnd = helper.getDecoratedEnd(child); - if (childStart < end && childEnd > start) { - if (completelyVisible) { - if (childStart >= start && childEnd <= end) { - return child; - } else if (acceptPartiallyVisible && partiallyVisible == null) { - partiallyVisible = child; - } - } else { - return child; - } - } - } - return partiallyVisible; - } -} \ No newline at end of file diff --git a/app/src/main/res/layout/giphy_activity.xml b/app/src/main/res/layout/giphy_activity.xml index 10bcd1099a..b3cf06abad 100644 --- a/app/src/main/res/layout/giphy_activity.xml +++ b/app/src/main/res/layout/giphy_activity.xml @@ -1,56 +1,46 @@ + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_above="@+id/giphy_logo" + android:orientation="vertical"> + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="?attr/colorPrimary"> - - + android:id="@+id/giphy_toolbar" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="?attr/colorPrimary" + android:theme="?attr/actionBarStyle" + app:layout_scrollFlags="scroll|enterAlways" /> - + - + diff --git a/app/src/main/res/layout/giphy_activity_toolbar.xml b/app/src/main/res/layout/giphy_activity_toolbar.xml index 01f67f4108..5dc0fa71ca 100644 --- a/app/src/main/res/layout/giphy_activity_toolbar.xml +++ b/app/src/main/res/layout/giphy_activity_toolbar.xml @@ -27,7 +27,7 @@ android:layout_width="0px" android:layout_weight="1" android:layout_marginStart="5dp" - android:hint="@string/giphy_activity_toolbar__search_gifs_and_stickers" + android:hint="@string/giphy_activity_toolbar__search_gifs" android:textColor="@color/signal_text_toolbar_title" android:textColorHint="@color/signal_text_toolbar_subtitle" android:textCursorDrawable="@null" diff --git a/app/src/main/res/layout/giphy_fragment.xml b/app/src/main/res/layout/giphy_fragment.xml deleted file mode 100644 index 0bac943757..0000000000 --- a/app/src/main/res/layout/giphy_fragment.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/giphy_mp4_fragment.xml b/app/src/main/res/layout/giphy_mp4_fragment.xml index 01c2817e62..32dcc62676 100644 --- a/app/src/main/res/layout/giphy_mp4_fragment.xml +++ b/app/src/main/res/layout/giphy_mp4_fragment.xml @@ -5,11 +5,29 @@ android:layout_width="match_parent" android:layout_height="match_parent"> + + + + + - diff --git a/app/src/main/res/layout/giphy_thumbnail.xml b/app/src/main/res/layout/giphy_thumbnail.xml deleted file mode 100644 index eed7e09e1a..0000000000 --- a/app/src/main/res/layout/giphy_thumbnail.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c56dce13cc..23ee10d046 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1993,7 +1993,7 @@ - Search GIFs and stickers + Search GIFs Nothing found