From ca3c9bc43a14c515428773dfef867320974135c9 Mon Sep 17 00:00:00 2001 From: tbarthel-fr Date: Sun, 22 May 2016 13:36:02 +0200 Subject: [PATCH 1/5] [TargetActivity] allow to provide a custom comparator for target activities. --- .../fr/tvbarthel/intentshare/IntentShare.java | 28 +++++ .../tvbarthel/intentshare/TargetActivity.java | 113 ++++++++++++------ .../TargetActivityComparatorProvider.java | 19 +++ .../intentshare/TargetActivityManager.java | 11 +- .../intentshare/TargetChooserActivity.java | 2 +- .../tvbarthel/intentsharesample/Adapter.java | 4 - .../intentsharesample/MainActivity.java | 12 ++ ...ocialTargetActivityComparatorProvider.java | 105 ++++++++++++++++ .../main/res/menu/activity_sample_menu.xml | 45 +++++-- sample/src/main/res/values/strings.xml | 4 + 10 files changed, 284 insertions(+), 59 deletions(-) create mode 100644 library/src/main/java/fr/tvbarthel/intentshare/TargetActivityComparatorProvider.java create mode 100644 sample/src/main/java/fr/tvbarthel/intentsharesample/SocialTargetActivityComparatorProvider.java diff --git a/library/src/main/java/fr/tvbarthel/intentshare/IntentShare.java b/library/src/main/java/fr/tvbarthel/intentshare/IntentShare.java index f278128..91c0fa1 100644 --- a/library/src/main/java/fr/tvbarthel/intentshare/IntentShare.java +++ b/library/src/main/java/fr/tvbarthel/intentshare/IntentShare.java @@ -76,6 +76,11 @@ public IntentShare[] newArray(int size) { */ IconLoader iconLoader; + /** + * Provide the comparator used to sort the target activities. + */ + TargetActivityComparatorProvider comparatorProvider; + /** * Title that will be displayed in the chooser. */ @@ -99,6 +104,7 @@ private IntentShare(Context context) { packageWithExtraProvider = new ArrayList<>(); this.listener = null; this.iconLoader = new AsyncIconLoader(); + this.comparatorProvider = new TargetActivity.RecencyComparatorProvider(); this.chooserTitle = context.getString(R.string.isl_default_sharing_label); } @@ -115,6 +121,7 @@ protected IntentShare(Parcel in) { this.mailSubject = in.readString(); this.extraProviders = in.createTypedArrayList(ExtraProvider.CREATOR); this.iconLoader = in.readParcelable(IconLoader.class.getClassLoader()); + this.comparatorProvider = in.readParcelable(TargetActivityComparatorProvider.class.getClassLoader()); this.chooserTitle = in.readString(); } @@ -131,6 +138,7 @@ public void writeToParcel(Parcel dest, int flags) { dest.writeString(this.mailSubject); dest.writeTypedList(this.extraProviders); dest.writeParcelable(this.iconLoader, flags); + dest.writeParcelable(this.comparatorProvider, flags); dest.writeString(this.chooserTitle); } @@ -170,6 +178,26 @@ public IntentShare iconLoader(@NonNull IconLoader iconLoader) { return this; } + /** + * Provide a custom {@link java.util.Comparator} in order to sort the {@link TargetActivity} + * displayed to the user. + *

+ * By default, TargetActivities are sorted by the recentness of their selection. If it's the + * behaviour that you are looking for, don't use this method and let the default comparator + * bring the magic. + * + * @param comparatorProvider comparator used to sort the TargetActivities displayed to the user. + * Will override the default sorting by recentness. + * @return current {@link IntentShare} for method chaining. + */ + public IntentShare comparatorProvider(@NonNull TargetActivityComparatorProvider comparatorProvider) { + if (comparatorProvider == null) { + throw new NullPointerException("Custom comparator provider can't be null."); + } + this.comparatorProvider = comparatorProvider; + return this; + } + /** * Text which will be shared. *

diff --git a/library/src/main/java/fr/tvbarthel/intentshare/TargetActivity.java b/library/src/main/java/fr/tvbarthel/intentshare/TargetActivity.java index d2901e6..5e44238 100644 --- a/library/src/main/java/fr/tvbarthel/intentshare/TargetActivity.java +++ b/library/src/main/java/fr/tvbarthel/intentshare/TargetActivity.java @@ -3,15 +3,15 @@ import android.content.Context; import android.content.pm.ResolveInfo; import android.net.Uri; +import android.os.Parcel; import java.io.File; -import java.text.Collator; import java.util.Comparator; /** * Plain java model for a sharing target activity. */ -class TargetActivity { +public class TargetActivity { private final int activityLabelResId; private final Uri iconUri; @@ -56,19 +56,6 @@ public boolean equals(Object o) { } TargetActivity that = (TargetActivity) o; - - if (activityLabelResId != that.activityLabelResId) { - return false; - } - if (isMail != that.isMail) { - return false; - } - if (lastSelection != that.lastSelection) { - return false; - } - if (!iconUri.equals(that.iconUri)) { - return false; - } return resolveInfo.equals(that.resolveInfo); } @@ -76,9 +63,6 @@ public boolean equals(Object o) { @Override public int hashCode() { int result = activityLabelResId; - result = 31 * result + iconUri.hashCode(); - result = 31 * result + (isMail ? 1 : 0); - result = 31 * result + (int) (lastSelection ^ (lastSelection >>> 32)); result = 31 * result + resolveInfo.hashCode(); return result; } @@ -140,12 +124,22 @@ public boolean isMailClient() { return this.isMail; } + /** + * Return a timestamp of the last selection inside the sharing dialog from you application. + * + * @return timestamp of the last selection in milliseconds since January 1, 1970 00:00:00.0 UTC + * or 0 if the target activity has never been selected by the user. + */ + public long getLastSelection() { + return lastSelection; + } + /** * Retrieve the label of the target activity. * * @return return target activity label or null if not yet loaded. */ - public CharSequence getLabel() { + CharSequence getLabel() { return label; } @@ -169,42 +163,85 @@ void setLabel(CharSequence label) { /** * Comparator used to sort {@link TargetActivity} based on the recency of their previous - * selection and their name as fallback when they have never been selected. + * selection and their default order as fallback when they have never been selected. *

* The ordering imposed by this comparator on a set of {@link TargetActivity} * is not consistent with equals since c.compare(e1, e2)==0 has not the same boolean * value as e1.equals(e2). */ - public static final class RecencyComparator implements Comparator { + public static final class RecencyComparatorProvider implements TargetActivityComparatorProvider { + + /** + * Parcelable. + */ + public static final Creator CREATOR = new Creator() { + @Override + public RecencyComparatorProvider createFromParcel(Parcel source) { + return new RecencyComparatorProvider(source); + } - private final Collator mCollator = Collator.getInstance(); + @Override + public RecencyComparatorProvider[] newArray(int size) { + return new RecencyComparatorProvider[size]; + } + }; /** * Comparator used to sort {@link TargetActivity} based on the recency of their previous - * selection and their name as fallback when they have never been selected. + * selection and their default order as fallback when they have never been selected. *

* The ordering imposed by this comparator on a set of {@link TargetActivity} * is not consistent with equals since c.compare(e1, e2)==0 has not the same boolean * value as e1.equals(e2). */ - public RecencyComparator() { - mCollator.setStrength(Collator.PRIMARY); + public RecencyComparatorProvider() { + } + /** + * Comparator used to sort {@link TargetActivity} based on the recency of their previous + * selection and their default order as fallback when they have never been selected. + *

+ * The ordering imposed by this comparator on a set of {@link TargetActivity} + * is not consistent with equals since c.compare(e1, e2)==0 has not the same boolean + * value as e1.equals(e2). + * + * @param in parcel. + */ + protected RecencyComparatorProvider(Parcel in) { + + } + + @Override - public int compare(TargetActivity lhs, TargetActivity rhs) { - float lhsScore = lhs.lastSelection; - float rhsScore = rhs.lastSelection; - - if (lhsScore > 0 && rhsScore > 0) { - return (int) (rhsScore - lhsScore); - } else if (lhsScore > 0) { - return -1; - } else if (rhsScore > 0) { - return 1; - } else { - return 0; - } + public Comparator provideComparator() { + return new Comparator() { + @Override + public int compare(TargetActivity lhs, TargetActivity rhs) { + float lhsScore = lhs.lastSelection; + float rhsScore = rhs.lastSelection; + + if (lhsScore > 0 && rhsScore > 0) { + return (int) (rhsScore - lhsScore); + } else if (lhsScore > 0) { + return -1; + } else if (rhsScore > 0) { + return 1; + } else { + return 0; + } + } + }; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + } } } diff --git a/library/src/main/java/fr/tvbarthel/intentshare/TargetActivityComparatorProvider.java b/library/src/main/java/fr/tvbarthel/intentshare/TargetActivityComparatorProvider.java new file mode 100644 index 0000000..c5a507a --- /dev/null +++ b/library/src/main/java/fr/tvbarthel/intentshare/TargetActivityComparatorProvider.java @@ -0,0 +1,19 @@ +package fr.tvbarthel.intentshare; + +import android.os.Parcelable; + +import java.util.Comparator; + +/** + * ˙Interface which allow to define which comparator will be provided for sorting the + * target activity inside the {@link TargetChooserActivity}. + */ +public interface TargetActivityComparatorProvider extends Parcelable { + + /** + * Provide the comparator used to sort {@link TargetActivity} displayed to the user. + * + * @return comparator used to sort {@link TargetActivity} displayed to the user. + */ + Comparator provideComparator(); +} diff --git a/library/src/main/java/fr/tvbarthel/intentshare/TargetActivityManager.java b/library/src/main/java/fr/tvbarthel/intentshare/TargetActivityManager.java index f86cc11..9bf390c 100644 --- a/library/src/main/java/fr/tvbarthel/intentshare/TargetActivityManager.java +++ b/library/src/main/java/fr/tvbarthel/intentshare/TargetActivityManager.java @@ -13,6 +13,7 @@ import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.List; /** @@ -55,10 +56,12 @@ public TargetActivityManager() { * Basically, resolve the list of {@link android.app.Activity} which can handled * {@link Intent#ACTION_SEND}. * - * @param context context used to resolves target activities. - * @param listener listener used to catch resolving events. + * @param context context used to resolves target activities. + * @param listener listener used to catch resolving events. + * @param comparator comparator used to sort the resolved target activities. */ - public void resolveTargetActivities(Context context, ResolveListener listener) { + public void resolveTargetActivities(Context context, ResolveListener listener, + Comparator comparator) { targetActivities.clear(); sharedPreferences = context.getSharedPreferences(SHARED_PREF_KEY, Context.MODE_PRIVATE); @@ -91,7 +94,7 @@ public void resolveTargetActivities(Context context, ResolveListener listener) { } } - Collections.sort(targetActivities, new TargetActivity.RecencyComparator()); + Collections.sort(targetActivities, comparator); for (int i = 0; i < targetActivities.size(); i++) { new AsyncLabelLoader(context, targetActivities.get(i), listener).execute(); diff --git a/library/src/main/java/fr/tvbarthel/intentshare/TargetChooserActivity.java b/library/src/main/java/fr/tvbarthel/intentshare/TargetChooserActivity.java index dda6155..03351dd 100644 --- a/library/src/main/java/fr/tvbarthel/intentshare/TargetChooserActivity.java +++ b/library/src/main/java/fr/tvbarthel/intentshare/TargetChooserActivity.java @@ -169,7 +169,7 @@ public void onCreate(Bundle savedInstanceState) { setUpStickyTitle(); targetActivityManager = new TargetActivityManager(); - targetActivityManager.resolveTargetActivities(this, this); + targetActivityManager.resolveTargetActivities(this, this, intentShare.comparatorProvider.provideComparator()); inInterpolator = new DecelerateInterpolator(); outInterpolator = new AccelerateInterpolator(); diff --git a/sample/src/main/java/fr/tvbarthel/intentsharesample/Adapter.java b/sample/src/main/java/fr/tvbarthel/intentsharesample/Adapter.java index bbffab8..d3d90e4 100644 --- a/sample/src/main/java/fr/tvbarthel/intentsharesample/Adapter.java +++ b/sample/src/main/java/fr/tvbarthel/intentsharesample/Adapter.java @@ -1,7 +1,6 @@ package fr.tvbarthel.intentsharesample; import android.support.v7.widget.RecyclerView; -import android.util.Log; import android.view.View; import android.view.ViewGroup; @@ -96,9 +95,6 @@ public void onAddExtraProviderRequested() { public void onExtraProviderDetailRequested(ExtraProviderWrapper wrapper) { if (listener != null) { int wrapperPosition = Adapter.this.extraProviders.indexOf(wrapper); - if (wrapperPosition == -1) { - Log.e("LARGONNE", "wrapper position -1 : " + wrapper); - } listener.onExtraProviderDetailRequested(wrapperPosition, wrapper); } } diff --git a/sample/src/main/java/fr/tvbarthel/intentsharesample/MainActivity.java b/sample/src/main/java/fr/tvbarthel/intentsharesample/MainActivity.java index 506b6dd..4af81ef 100644 --- a/sample/src/main/java/fr/tvbarthel/intentsharesample/MainActivity.java +++ b/sample/src/main/java/fr/tvbarthel/intentsharesample/MainActivity.java @@ -26,6 +26,7 @@ import fr.tvbarthel.intentshare.IconLoader; import fr.tvbarthel.intentshare.IntentShare; import fr.tvbarthel.intentshare.IntentShareListener; +import fr.tvbarthel.intentshare.TargetActivityComparatorProvider; import fr.tvbarthel.intentshare.loader.glide.GlideIconLoader; import fr.tvbarthel.intentshare.loader.picasso.PicassoIconLoader; @@ -49,6 +50,7 @@ public class MainActivity extends AppCompatActivity implements private PicassoIconLoader picassoIconLoader; private IconLoader iconLoader; + private TargetActivityComparatorProvider customComparatorProvider; private GlideIconLoader glideIconLoader; private String targetPackage; @@ -68,6 +70,7 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.activity_main); targetPackage = null; + customComparatorProvider = null; intentShareListener = new IntentShareListener() { @Override @@ -120,6 +123,12 @@ public boolean onOptionsItemSelected(MenuItem item) { } iconLoader = picassoIconLoader; break; + case R.id.sorting_default: + customComparatorProvider = null; + break; + case R.id.sorting_custom: + customComparatorProvider = new SocialTargetActivityComparatorProvider(); + break; default: return super.onOptionsItemSelected(item); @@ -200,6 +209,9 @@ public void onClick(View v) { .facebookBody(facebookLink) .twitterBody(tweet) .listener(intentShareListener); + if (customComparatorProvider != null) { + intentShare.comparatorProvider(customComparatorProvider); + } if (iconLoader != null) { intentShare.iconLoader(iconLoader); } diff --git a/sample/src/main/java/fr/tvbarthel/intentsharesample/SocialTargetActivityComparatorProvider.java b/sample/src/main/java/fr/tvbarthel/intentsharesample/SocialTargetActivityComparatorProvider.java new file mode 100644 index 0000000..b6f8fbc --- /dev/null +++ b/sample/src/main/java/fr/tvbarthel/intentsharesample/SocialTargetActivityComparatorProvider.java @@ -0,0 +1,105 @@ +package fr.tvbarthel.intentsharesample; + +import android.os.Parcel; + +import java.util.ArrayList; +import java.util.Comparator; + +import fr.tvbarthel.intentshare.TargetActivity; +import fr.tvbarthel.intentshare.TargetActivityComparatorProvider; + +/** + * Simple custom {@link TargetActivityComparatorProvider} used to illustrate how to provide + * a different sorting for the target activities displayed to the user. + *

+ * This example simply displayed some social media apps at first and then keep the original + * order returned by the system. + */ +class SocialTargetActivityComparatorProvider implements TargetActivityComparatorProvider { + + private ArrayList prioritizedPackageNames; + + /** + * Parcelable. + */ + public static final Creator CREATOR + = new Creator() { + @Override + public SocialTargetActivityComparatorProvider createFromParcel(Parcel source) { + return new SocialTargetActivityComparatorProvider(source); + } + + @Override + public SocialTargetActivityComparatorProvider[] newArray(int size) { + return new SocialTargetActivityComparatorProvider[size]; + } + }; + + /** + * Simple custom {@link TargetActivityComparatorProvider} used to illustrate how to provide + * a different sorting for the target activities displayed to the user. + *

+ * This example simply displayed some social media apps at first and then keep the original + * order returned by the system. + */ + public SocialTargetActivityComparatorProvider() { + prioritizedPackageNames = new ArrayList<>(); + prioritizedPackageNames.add("com.instagram.android"); // instagram + prioritizedPackageNames.add("com.snapchat.android"); // snapchat + prioritizedPackageNames.add("com.pinterest"); // pinterest + prioritizedPackageNames.add("com.sgiggle.production"); // tango + prioritizedPackageNames.add("jom.tencent.mm"); // wechat + prioritizedPackageNames.add("jp.naver.line.android"); // line + prioritizedPackageNames.add("com.whatsapp"); // what's app + prioritizedPackageNames.add("com.google.android.talk"); // hangout + prioritizedPackageNames.add("com.google.android.apps.plus"); // G+ + prioritizedPackageNames.add("com.twitter.android"); // twitter + prioritizedPackageNames.add("com.facebook.orca"); // FB + prioritizedPackageNames.add("com.facebook.katana"); // FB + } + + /** + * Simple custom {@link TargetActivityComparatorProvider} used to illustrate how to provide + * a different sorting for the target activities displayed to the user. + *

+ * This example simply displayed some social media apps at first and then keep the original + * order returned by the system. + * + * @param in parcel. + */ + protected SocialTargetActivityComparatorProvider(Parcel in) { + prioritizedPackageNames = new ArrayList<>(); + in.readStringList(prioritizedPackageNames); + } + + + @Override + public Comparator provideComparator() { + return new Comparator() { + @Override + public int compare(TargetActivity lhs, TargetActivity rhs) { + int lhsPriority = prioritizedPackageNames.indexOf(lhs.getPackageName()); + int rhsPriority = prioritizedPackageNames.indexOf(rhs.getPackageName()); + if (lhsPriority != -1 && rhsPriority != -1) { + return lhsPriority - rhsPriority; + } else if (lhsPriority != -1) { + return -1; + } else if (rhsPriority != -1) { + return 1; + } else { + return 0; // keep the original order if not present in the prioritized list. + } + } + }; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeStringList(prioritizedPackageNames); + } +} diff --git a/sample/src/main/res/menu/activity_sample_menu.xml b/sample/src/main/res/menu/activity_sample_menu.xml index debd6f7..44590ff 100644 --- a/sample/src/main/res/menu/activity_sample_menu.xml +++ b/sample/src/main/res/menu/activity_sample_menu.xml @@ -1,15 +1,36 @@

- - - - - + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sample/src/main/res/values/strings.xml b/sample/src/main/res/values/strings.xml index 3e59c7f..7620beb 100644 --- a/sample/src/main/res/values/strings.xml +++ b/sample/src/main/res/values/strings.xml @@ -3,6 +3,8 @@ Default IconLoader Picasso IconLoader Glide IconLoader + Default Sorting (recency) + Custom Sorting (social first) Improves the sharing experience on Android with IntentShare. http://tvbarthel.github.io/IntentShare/ @@ -32,4 +34,6 @@ Mail subject Mail body Default provider + Target sorting + Icon loading From 90bffadb3f669fb3037a9c0bb939a164db668b3e Mon Sep 17 00:00:00 2001 From: tbarthel-fr Date: Sun, 29 May 2016 15:34:03 +0200 Subject: [PATCH 2/5] [Marshmallow] implement Android M chooser look and feel. --- .../intentshare/LayoutManagerFactory.java | 55 ++++++++++++ .../intentshare/TargetActivityAdapter.java | 15 +++- .../intentshare/TargetActivityHeaderView.java | 6 +- .../intentshare/TargetActivityView.java | 12 +-- .../intentshare/TargetChooserActivity.java | 90 ++++++++++++------- .../layout-v23/isl_target_activity_view.xml | 25 ++++++ .../layout/isl_activity_target_chooser.xml | 16 +++- .../res/layout/isl_target_activity_view.xml | 4 +- library/src/main/res/values-v23/dimens.xml | 17 ++++ library/src/main/res/values/dimens.xml | 9 +- 10 files changed, 198 insertions(+), 51 deletions(-) create mode 100644 library/src/main/java/fr/tvbarthel/intentshare/LayoutManagerFactory.java create mode 100644 library/src/main/res/layout-v23/isl_target_activity_view.xml create mode 100644 library/src/main/res/values-v23/dimens.xml diff --git a/library/src/main/java/fr/tvbarthel/intentshare/LayoutManagerFactory.java b/library/src/main/java/fr/tvbarthel/intentshare/LayoutManagerFactory.java new file mode 100644 index 0000000..f9fba6c --- /dev/null +++ b/library/src/main/java/fr/tvbarthel/intentshare/LayoutManagerFactory.java @@ -0,0 +1,55 @@ +package fr.tvbarthel.intentshare; + +import android.content.Context; +import android.os.Build; +import android.support.v7.widget.GridLayoutManager; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; + +/** + * Factory used to handle layout manager according to the device android version seamlessly + */ +final class LayoutManagerFactory { + + private static final int MARSHMALLOW_SPAN_COUNT = 4; + + /** + * Non instantiable class. + */ + private LayoutManagerFactory() { + + } + + /** + * Build the layout manager for the {@link TargetActivity} list displayed to the user + * during the target activity selection. + * + * @param context context used to instantiate layout manager. + * @return layout manager matching the native look and feel linked to the device SDK version. + */ + public static RecyclerView.LayoutManager buildLayoutManager(Context context) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + GridLayoutManager gridLayoutManager = new GridLayoutManager(context, MARSHMALLOW_SPAN_COUNT); + gridLayoutManager.setSpanSizeLookup(new MarshmallowSpanSizeLookup()); + return gridLayoutManager; + } else { + return new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false); + } + } + + /** + * SpanSizeLookup used to fit the native look and feel provided by + * {@link android.content.Intent#createChooser(android.content.Intent, CharSequence)} + */ + private static final class MarshmallowSpanSizeLookup extends GridLayoutManager.SpanSizeLookup { + + @Override + public int getSpanSize(int position) { + if (position == 0) { + return MARSHMALLOW_SPAN_COUNT; // header taking the full width; + } else { + return 1; // target activity taking one unit; + } + } + } +} diff --git a/library/src/main/java/fr/tvbarthel/intentshare/TargetActivityAdapter.java b/library/src/main/java/fr/tvbarthel/intentshare/TargetActivityAdapter.java index 47928a3..c2a20ea 100644 --- a/library/src/main/java/fr/tvbarthel/intentshare/TargetActivityAdapter.java +++ b/library/src/main/java/fr/tvbarthel/intentshare/TargetActivityAdapter.java @@ -1,5 +1,6 @@ package fr.tvbarthel.intentshare; +import android.content.Context; import android.support.v7.widget.RecyclerView; import android.view.ViewGroup; @@ -15,6 +16,7 @@ class TargetActivityAdapter extends RecyclerView.Adapter(); selectedTargetActivity = null; @@ -165,6 +176,7 @@ public void onCreate(Bundle savedInstanceState) { rootView.setOnClickListener(this); rootView.setAlpha(0f); + stateRestored = savedInstanceState != null; setUpRecyclerView(savedInstanceState); setUpStickyTitle(); @@ -189,7 +201,7 @@ protected void onSaveInstanceState(Bundle outState) { @Override protected void onDestroy() { super.onDestroy(); - if (!listenerNotified) { + if (!listenerNotified && !isChangingConfigurations()) { IntentShareListener.notifySharingCanceled(this); } } @@ -199,7 +211,7 @@ public void finish() { super.finish(); if (selectedTargetActivity != null) { IntentShareListener.notifySharingCompleted(this, selectedTargetActivity.getPackageName()); - } else { + } else if (!isChangingConfigurations()) { IntentShareListener.notifySharingCanceled(this); } listenerNotified = true; @@ -230,13 +242,7 @@ public void onLabelResolved(TargetActivity targetActivity) { private void setUpRecyclerView(Bundle savedInstance) { - recyclerView.setLayoutManager( - new LinearLayoutManager( - this, - LinearLayoutManager.VERTICAL, - false - ) - ); + recyclerView.setLayoutManager(LayoutManagerFactory.buildLayoutManager(this)); targetActivities = new ArrayList<>(); adapter = new TargetActivityAdapter( targetActivities, @@ -247,6 +253,12 @@ private void setUpRecyclerView(Bundle savedInstance) { targetActivityViewHeight = getResources().getDimensionPixelSize(R.dimen.isl_target_activity_view_height); + if (savedInstance != null) { + currentRecyclerScrollY = savedInstance.getInt(SAVED_CURRENT_SCROLL_Y, 0); + } else { + currentRecyclerScrollY = 0; + } + recyclerView.getViewTreeObserver().addOnPreDrawListener( new ViewTreeObserver.OnPreDrawListener() { @@ -257,28 +269,37 @@ public boolean onPreDraw() { int maxStartingHeight = (int) (recyclerView.getHeight() / 2.5f); int startingHeight = Math.min(totalHeight, maxStartingHeight); recyclerPaddingTop = recyclerView.getHeight() - startingHeight; - recyclerView.setPadding(0, recyclerPaddingTop, 0, 0); + recyclerView.setPadding(recyclerView.getPaddingLeft(), recyclerPaddingTop, + recyclerView.getPaddingRight(), 0); recyclerView.setTranslationY(recyclerView.getHeight()); + int backgroundTranslationY = Math.max(0, recyclerPaddingTop - currentRecyclerScrollY); + background.setTranslationY(recyclerView.getHeight() + backgroundTranslationY); recyclerView.setAdapter(adapter); - rootView.animate() - .alpha(1f) - .setDuration(animationDuration) - .setInterpolator(inInterpolator) - .setListener(null); - recyclerView.animate() - .translationY(0) - .setDuration(animationDuration) - .setInterpolator(inInterpolator) - .setListener(null); + if (stateRestored) { + rootView.setAlpha(1f); + recyclerView.setTranslationY(0); + background.setTranslationY(backgroundTranslationY); + } else { + rootView.animate() + .alpha(1f) + .setDuration(animationDuration) + .setInterpolator(inInterpolator) + .setListener(null); + recyclerView.animate() + .translationY(0) + .setDuration(animationDuration) + .setInterpolator(inInterpolator) + .setListener(null); + background.animate() + .translationY(backgroundTranslationY) + .setDuration(animationDuration) + .setInterpolator(inInterpolator) + .setListener(null); + } return false; } } ); - if (savedInstance != null) { - currentRecyclerScrollY = savedInstance.getInt(SAVED_CURRENT_SCROLL_Y, 0); - } else { - currentRecyclerScrollY = 0; - } recyclerView.addOnScrollListener( new RecyclerView.OnScrollListener() { @Override @@ -289,10 +310,14 @@ public void onScrolled(RecyclerView recyclerView, int dx, int dy) { isStickyTitleDisplayed = true; stickyTitle.setVisibility(View.VISIBLE); stickyShadow.setVisibility(View.VISIBLE); - } else if (isStickyTitleDisplayed && currentRecyclerScrollY < recyclerPaddingTop) { - isStickyTitleDisplayed = false; - stickyTitle.setVisibility(View.INVISIBLE); - stickyShadow.setVisibility(View.INVISIBLE); + } else if (currentRecyclerScrollY < recyclerPaddingTop) { + if (isStickyTitleDisplayed) { + isStickyTitleDisplayed = false; + stickyTitle.setVisibility(View.INVISIBLE); + stickyShadow.setVisibility(View.INVISIBLE); + } + background.setTranslationY(recyclerPaddingTop - currentRecyclerScrollY); + } } } @@ -325,6 +350,11 @@ public void onAnimationEnd(Animator animation) { .setDuration(animationDuration) .setInterpolator(outInterpolator) .setListener(null); + background.animate() + .translationY(background.getTranslationY() + rootView.getHeight()) + .setDuration(animationDuration) + .setInterpolator(outInterpolator) + .setListener(null); stickyShadow.animate() .translationY(rootView.getHeight()) .setDuration(animationDuration) diff --git a/library/src/main/res/layout-v23/isl_target_activity_view.xml b/library/src/main/res/layout-v23/isl_target_activity_view.xml new file mode 100644 index 0000000..6436e1f --- /dev/null +++ b/library/src/main/res/layout-v23/isl_target_activity_view.xml @@ -0,0 +1,25 @@ + + + + + + + + \ No newline at end of file diff --git a/library/src/main/res/layout/isl_activity_target_chooser.xml b/library/src/main/res/layout/isl_activity_target_chooser.xml index e7c4ccb..ed88933 100644 --- a/library/src/main/res/layout/isl_activity_target_chooser.xml +++ b/library/src/main/res/layout/isl_activity_target_chooser.xml @@ -6,23 +6,33 @@ android:background="@color/isl_activity_translucent_background" android:fitsSystemWindows="true"> + + + android:overScrollMode="never" + android:paddingLeft="@dimen/isl_target_activity_recycler_padding" + android:paddingRight="@dimen/isl_target_activity_recycler_padding" /> + android:layout_height="wrap_content" + android:paddingLeft="@dimen/isl_default_padding" + android:paddingRight="@dimen/isl_default_padding" /> diff --git a/library/src/main/res/layout/isl_target_activity_view.xml b/library/src/main/res/layout/isl_target_activity_view.xml index d01e53d..c9bc933 100644 --- a/library/src/main/res/layout/isl_target_activity_view.xml +++ b/library/src/main/res/layout/isl_target_activity_view.xml @@ -15,10 +15,10 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="center_vertical" - android:layout_marginLeft="@dimen/isl_target_activity_view_label_margin_left" + android:layout_marginLeft="@dimen/isl_target_activity_view_label_icon_margin" android:ellipsize="end" android:singleLine="true" android:textColor="@color/isl_target_activity_view_label" - android:textSize="19sp"/> + android:textSize="@dimen/isl_target_activity_view_text_size"/> \ No newline at end of file diff --git a/library/src/main/res/values-v23/dimens.xml b/library/src/main/res/values-v23/dimens.xml new file mode 100644 index 0000000..8a16867 --- /dev/null +++ b/library/src/main/res/values-v23/dimens.xml @@ -0,0 +1,17 @@ + + + 16dp + + + 16dp + 4dp + 48dp + 0dp + 56dp + 19sp + 60dp + 120dp + 12sp + @dimen/isl_default_padding + 0dp + \ No newline at end of file diff --git a/library/src/main/res/values/dimens.xml b/library/src/main/res/values/dimens.xml index d4d354d..1bc34d1 100644 --- a/library/src/main/res/values/dimens.xml +++ b/library/src/main/res/values/dimens.xml @@ -3,10 +3,15 @@ 16dp + 16dp + 16dp 24dp 10dp - 54dp + 54dp 19sp + 60dp 60dp - + 19sp + 0dp + @dimen/isl_default_padding \ No newline at end of file From f5deee0bec7248706b698dc966e28d2d5acea30d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vincent=20Barth=C3=A9l=C3=A9my?= Date: Sat, 4 Jun 2016 16:22:36 +0200 Subject: [PATCH 3/5] [Gradle] Use the latest version of the gradle distribution and the gradle build tools. --- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 42e3725..a8f26b7 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:1.5.0' + classpath 'com.android.tools.build:gradle:2.1.2' classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.1' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3' // NOTE: Do not place your application dependencies here; they belong diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 6e24824..4f0f661 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.13-all.zip From 189a69ea2eae7d877b579303d35caf23390cc9c4 Mon Sep 17 00:00:00 2001 From: tbarthel-fr Date: Sat, 4 Jun 2016 17:07:57 +0200 Subject: [PATCH 4/5] [ReadMe] update readme with last features. --- README.md | 45 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8061248..fcee93d 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ This project is a light open-source library that improves the sharing experience * [Picasso](#picasso) * [Glide](#glide) * [Custom icon loader](#custom-icon-loader) +* [Comparator Provider](#comparator-provider) * [What's next](#whats-next) * [Contributing](#contributing) * [License](#license) @@ -257,12 +258,52 @@ IntentShare.with(context) }) .deliver(); ``` +# Comparator provider +By default, target activities are sorted based on the recency of their selection from your app. +```java + /** + * Comparator used to sort {@link TargetActivity} based on the recency of their previous + * selection and their default order as fallback when they have never been selected. + *

+ * The ordering imposed by this comparator on a set of {@link TargetActivity} + * is not consistent with equals since c.compare(e1, e2)==0 has not the same boolean + * value as e1.equals(e2). + */ + public RecencyComparatorProvider() { + + } +``` +Instead of using the default comparator, you can implement your own comparator provider in order to customize the target activities order display to the user: +```java +/** + * ˙Interface which allow to define which comparator will be provided for sorting the + * target activity inside the {@link TargetChooserActivity}. + */ +public interface TargetActivityComparatorProvider extends Parcelable { + + /** + * Provide the comparator used to sort {@link TargetActivity} displayed to the user. + * + * @return comparator used to sort {@link TargetActivity} displayed to the user. + */ + Comparator provideComparator(); +} +``` +```java +IntentShare.with(context) + .chooserTitle("Select a sharing target : ") + .text("Default text you would like to share.") + .comparatorProvider(customComparatorProvider) + .deliver(); +``` +An example from the sample can be found here : [SocialTargetActivityComparatorProvider.java](https://github.com/tvbarthel/IntentShare/blob/develop/sample/src/main/java/fr/tvbarthel/intentsharesample/SocialTargetActivityComparatorProvider.java) + + # What's next - * Providing custom sorting for target activities. * Providing easier way to share images. * Removing dependencies on support libraries. * Sample : implementing image selection for extra provider. - + # Contributing Contributions are very welcome (: You can contribute through GitHub by forking the repository and sending a pull request. From 70849217d4860f28bee1d8c4247dcbcf398b0e69 Mon Sep 17 00:00:00 2001 From: tbarthel-fr Date: Sat, 4 Jun 2016 19:46:46 +0200 Subject: [PATCH 5/5] [Release] v0.0.2 --- README.md | 6 +++--- build.gradle | 4 ++-- sample/build.gradle | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index fcee93d..1226187 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ Find more about our motivations [here](http://tvbarthel.fr/IntentShare/). # Gradle dependency available on jcenter. ```groovy -compile 'fr.tvbarthel.intentshare:library:0.0.1' +compile 'fr.tvbarthel.intentshare:library:0.0.2' ``` dependencies @@ -214,7 +214,7 @@ Default icon loader used to load target activities icons is based on AsyncTask. ## Picasso If your are already using Picasso, you may want to consider using PicassoIconLoader: ```groovy -compile 'fr.tvbarthel.intentshare:picasso-loader:0.0.1' +compile 'fr.tvbarthel.intentshare:picasso-loader:0.0.2' ``` ```java @@ -228,7 +228,7 @@ IntentShare.with(context) ## Glide If your are already using Glide, you may want to consider using GlideIconLoader: ```groovy -compile 'fr.tvbarthel.intentshare:glide-loader:0.0.1' +compile 'fr.tvbarthel.intentshare:glide-loader:0.0.2' ``` ```java diff --git a/build.gradle b/build.gradle index a8f26b7..794acdf 100644 --- a/build.gradle +++ b/build.gradle @@ -29,6 +29,6 @@ ext { compileSdkVersion = 23 targetSdkVersion = 23 minSdkVersion = 16 - versionCode = 1 - versionName = "0.0.1" + versionCode = 2 + versionName = "0.0.2" } diff --git a/sample/build.gradle b/sample/build.gradle index e9a6e4e..4ae107b 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -8,8 +8,8 @@ android { applicationId "fr.tvbarthel.intentsharesample" minSdkVersion 16 targetSdkVersion 23 - versionCode 2 - versionName "1.1" + versionCode 3 + versionName "1.2" } buildTypes { release {