From 29cbf9dfbe185c28036e9ad5dd64219b9ee8df9c Mon Sep 17 00:00:00 2001 From: Ray Li Date: Tue, 12 Dec 2017 17:57:58 -0600 Subject: [PATCH] RecyclerView Animation Creation #1 *Animate RecyclerView items when scrolling up and down. --- app/build.gradle | 26 +- .../rocketnotes/ImageItemViewholder.java | 225 ++++---- .../java/stream/rocketnotes/MainActivity.java | 29 +- .../rocketnotes/NoteItemViewholder.java | 20 + .../stream/rocketnotes/PopupActivity.java | 6 +- .../rocketnotes/WidgetReviewViewholder.java | 543 +++++++++--------- .../rocketnotes/ui/LandingItemAnimator.java | 65 +++ 7 files changed, 523 insertions(+), 391 deletions(-) create mode 100644 app/src/main/java/stream/rocketnotes/ui/LandingItemAnimator.java diff --git a/app/build.gradle b/app/build.gradle index 3beff42..0fec89b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,14 +1,14 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 26 - buildToolsVersion "26.0.2" + compileSdkVersion 27 + buildToolsVersion "27.0.1" defaultConfig { applicationId "stream.rocketnotes" minSdkVersion 16 - targetSdkVersion 26 - versionCode 15 - versionName "1.2.2" + targetSdkVersion 27 + versionCode 16 + versionName "1.3.0" } buildTypes { release { @@ -33,15 +33,15 @@ repositories { dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') - implementation 'com.github.searchy2:CustomPermissionsDialogue:1.3' - implementation 'com.github.searchy2:CustomAlertViewDialogue:1.8.1' - implementation 'com.github.searchy2:AndroidCrossPromotion:1.3.3' + implementation 'com.github.searchy2:CustomPermissionsDialogue:1.4' + implementation 'com.github.searchy2:CustomAlertViewDialogue:1.9' + implementation 'com.github.searchy2:AndroidCrossPromotion:1.4' - implementation 'com.android.support:appcompat-v7:26.1.0' - implementation 'com.android.support:support-v4:26.1.0' - implementation 'com.android.support:design:26.1.0' - implementation 'com.android.support:recyclerview-v7:26.1.0' - implementation 'com.android.support:cardview-v7:26.1.0' + implementation 'com.android.support:appcompat-v7:27.0.2' + implementation 'com.android.support:support-v4:27.0.2' + implementation 'com.android.support:design:27.0.2' + implementation 'com.android.support:recyclerview-v7:27.0.2' + implementation 'com.android.support:cardview-v7:27.0.2' implementation 'com.android.volley:volley:1.0.0' implementation 'com.afollestad:material-camera:0.4.5' //https://github.com/afollestad/material-camera diff --git a/app/src/main/java/stream/rocketnotes/ImageItemViewholder.java b/app/src/main/java/stream/rocketnotes/ImageItemViewholder.java index 2b67265..cf200f0 100644 --- a/app/src/main/java/stream/rocketnotes/ImageItemViewholder.java +++ b/app/src/main/java/stream/rocketnotes/ImageItemViewholder.java @@ -1,103 +1,122 @@ -package stream.rocketnotes; - -import android.content.Context; -import android.content.Intent; -import android.view.View; -import android.widget.ImageView; - -import com.squareup.picasso.Picasso; - -import java.util.List; - -import eu.davidea.flexibleadapter.FlexibleAdapter; -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem; -import eu.davidea.viewholders.FlexibleViewHolder; - -public class ImageItemViewholder extends AbstractFlexibleItem { - - private String id; - private String image; - - public ImageItemViewholder(String id, String image) { - this.id = id; - this.image = image; - } - - /** - * When an item is equals to another? - * Write your own concept of equals, mandatory to implement. - * This will be explained in the "Item interfaces" Wiki page. - */ - @Override - public boolean equals(Object inObject) { - if (inObject instanceof ImageItemViewholder) { - ImageItemViewholder inItem = (ImageItemViewholder) inObject; - return this.id.equals(inItem.id); - } - return false; - } - - /** - * You should implement also this method if equals() is implemented. - * This method, if implemented, has several implications that Adapter handles better: - * - The Hash increases performance in big list during Update & Filter operations. - * - Collapsing many expandable items is much faster. - * - You might want to activate stable ids via Constructor for RV, if your id - * is unique (read more in the wiki page: "Setting Up Advanced"). - */ - @Override - public int hashCode() { - return id.hashCode(); - } - - /** - * For the item type we need an int value: the layoutResID is sufficient. - */ - @Override - public int getLayoutRes() { - return R.layout.item_image; - } - - @Override - public ImageViewHolder createViewHolder(View view, FlexibleAdapter adapter) { - return new ImageViewHolder(view, adapter); - } - - /** - * The Adapter and the Payload are provided to get more specific information from it. - */ - @Override - public void bindViewHolder(FlexibleAdapter adapter, ImageViewHolder holder, final int position, - List payloads) { - final Context context = holder.itemView.getContext(); -// UXCam.occludeSensitiveView(holder.noteImage); - - Picasso.with(context).load(image).transform(ImageTransformer.getSquare(holder.noteImage)).placeholder(R.drawable.image_picture_full).into(holder.noteImage); - - holder.noteImage.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - Intent intent = new Intent(context, ImageViewerActivity.class); - intent.setAction(Constants.OPEN_IMAGE_SINGLE); - intent.putExtra(Constants.ID, Integer.valueOf(id)); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); - } - }); - } - - /** - * The ViewHolder used by this item. - * Extending from FlexibleViewHolder is recommended especially when you will use - * more advanced features. - */ - public class ImageViewHolder extends FlexibleViewHolder { - - public ImageView noteImage; - - public ImageViewHolder(View view, FlexibleAdapter adapter) { - super(view, adapter); - noteImage = view.findViewById(R.id.item_image); - } - } -} +package stream.rocketnotes; + +import android.animation.Animator; +import android.content.Context; +import android.content.Intent; +import android.support.annotation.NonNull; +import android.support.v7.widget.GridLayoutManager; +import android.view.View; +import android.widget.ImageView; + +import com.squareup.picasso.Picasso; + +import java.util.List; + +import eu.davidea.flexibleadapter.FlexibleAdapter; +import eu.davidea.flexibleadapter.helpers.AnimatorHelper; +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem; +import eu.davidea.viewholders.FlexibleViewHolder; + +public class ImageItemViewholder extends AbstractFlexibleItem { + + private String id; + private String image; + + public ImageItemViewholder(String id, String image) { + this.id = id; + this.image = image; + } + + /** + * When an item is equals to another? + * Write your own concept of equals, mandatory to implement. + * This will be explained in the "Item interfaces" Wiki page. + */ + @Override + public boolean equals(Object inObject) { + if (inObject instanceof ImageItemViewholder) { + ImageItemViewholder inItem = (ImageItemViewholder) inObject; + return this.id.equals(inItem.id); + } + return false; + } + + /** + * You should implement also this method if equals() is implemented. + * This method, if implemented, has several implications that Adapter handles better: + * - The Hash increases performance in big list during Update & Filter operations. + * - Collapsing many expandable items is much faster. + * - You might want to activate stable ids via Constructor for RV, if your id + * is unique (read more in the wiki page: "Setting Up Advanced"). + */ + @Override + public int hashCode() { + return id.hashCode(); + } + + /** + * For the item type we need an int value: the layoutResID is sufficient. + */ + @Override + public int getLayoutRes() { + return R.layout.item_image; + } + + @Override + public ImageViewHolder createViewHolder(View view, FlexibleAdapter adapter) { + return new ImageViewHolder(view, adapter); + } + + /** + * The Adapter and the Payload are provided to get more specific information from it. + */ + @Override + public void bindViewHolder(FlexibleAdapter adapter, ImageViewHolder holder, final int position, + List payloads) { + final Context context = holder.itemView.getContext(); +// UXCam.occludeSensitiveView(holder.noteImage); + + Picasso.with(context).load(image).transform(ImageTransformer.getSquare(holder.noteImage)).placeholder(R.drawable.image_picture_full).into(holder.noteImage); + + holder.noteImage.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + Intent intent = new Intent(context, ImageViewerActivity.class); + intent.setAction(Constants.OPEN_IMAGE_SINGLE); + intent.putExtra(Constants.ID, Integer.valueOf(id)); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + } + }); + } + + /** + * The ViewHolder used by this item. + * Extending from FlexibleViewHolder is recommended especially when you will use + * more advanced features. + */ + public class ImageViewHolder extends FlexibleViewHolder { + + public ImageView noteImage; + + public ImageViewHolder(View view, FlexibleAdapter adapter) { + super(view, adapter); + noteImage = view.findViewById(R.id.item_image); + } + + @Override + public void scrollAnimators(@NonNull List animators, int position, boolean isForward) { + if (mAdapter.getRecyclerView().getLayoutManager() instanceof GridLayoutManager) { + if (position % 2 != 0) + AnimatorHelper.slideInFromRightAnimator(animators, itemView, mAdapter.getRecyclerView(), 0.5f); + else + AnimatorHelper.slideInFromLeftAnimator(animators, itemView, mAdapter.getRecyclerView(), 0.5f); + } else { + if (isForward) + AnimatorHelper.slideInFromBottomAnimator(animators, itemView, mAdapter.getRecyclerView()); + else + AnimatorHelper.slideInFromTopAnimator(animators, itemView, mAdapter.getRecyclerView()); + } + } + } +} diff --git a/app/src/main/java/stream/rocketnotes/MainActivity.java b/app/src/main/java/stream/rocketnotes/MainActivity.java index 1b9b763..f1c58ff 100644 --- a/app/src/main/java/stream/rocketnotes/MainActivity.java +++ b/app/src/main/java/stream/rocketnotes/MainActivity.java @@ -9,6 +9,7 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.os.Bundle; +import android.os.Handler; import android.preference.PreferenceManager; import android.speech.RecognizerIntent; import android.support.design.internal.NavigationMenu; @@ -20,6 +21,7 @@ import android.util.Log; import android.view.MenuItem; import android.view.View; +import android.view.animation.DecelerateInterpolator; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; @@ -41,6 +43,7 @@ import java.util.Map; import eu.davidea.flexibleadapter.FlexibleAdapter; +import eu.davidea.flexibleadapter.common.FlexibleItemAnimator; import eu.davidea.flexibleadapter.common.SmoothScrollStaggeredLayoutManager; import eu.davidea.flexibleadapter.items.AbstractFlexibleItem; import eu.davidea.flexibleadapter.items.IFlexible; @@ -50,6 +53,7 @@ import stream.custompermissionsdialogue.utils.PermissionUtils; import stream.rocketnotes.filter.FilterMaterialSearchView; import stream.rocketnotes.filter.model.Filter; +import stream.rocketnotes.ui.LandingItemAnimator; import stream.rocketnotes.utils.AnalyticsUtils; public class MainActivity extends AppCompatActivity implements AppBarLayout.OnOffsetChangedListener { @@ -98,18 +102,19 @@ public void OnClick(View view, Dialog dialog) { dbHelper = new DatabaseHelper(mContext); sharedPref = PreferenceManager.getDefaultSharedPreferences(this); //Checks for first launch - if (sharedPref.getBoolean("prefs_first_start", true)) { - - //Start sequence finished - SharedPreferences.Editor editor = sharedPref.edit(); - editor.putBoolean("prefs_tutorial_intro", false); - editor.apply(); - } +// if (sharedPref.getBoolean("prefs_first_start", true)) { +// +// //Start sequence finished +// SharedPreferences.Editor editor = sharedPref.edit(); +// editor.putBoolean("prefs_tutorial_intro", false); +// editor.apply(); +// } mAppBar = findViewById(R.id.app_bar); mAppBar.addOnOffsetChangedListener(this); - InitializeRecyclerView(savedInstanceState); + InitializeRecyclerView(); + // checkVoiceRecognition(); mFilterView = findViewById(R.id.sv); SetupSearchBar(); @@ -135,17 +140,23 @@ protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); } - private void InitializeRecyclerView(Bundle savedInstanceState) { + private void InitializeRecyclerView() { // Optional but strongly recommended: Compose the initial list List myItems = getDatabaseList(); // Initialize the Adapter mAdapter = new FlexibleAdapter<>(myItems); + mAdapter.setAnimationOnScrolling(true) + .setAnimationEntryStep(true) + .setAnimationOnReverseScrolling(true) + .setAnimationInterpolator(new DecelerateInterpolator()) + .setAnimationDuration(300L); mStaggeredLayoutManager = createNewStaggeredGridLayoutManager(); // Prepare the RecyclerView and attach the Adapter to it mRecyclerView = findViewById(R.id.recycler_view); + mRecyclerView.setItemViewCacheSize(0); //Setting ViewCache to 0 (default=2) will animate items better while scrolling down+up with LinearLayout mRecyclerView.setLayoutManager(mStaggeredLayoutManager); mRecyclerView.setAdapter(mAdapter); } diff --git a/app/src/main/java/stream/rocketnotes/NoteItemViewholder.java b/app/src/main/java/stream/rocketnotes/NoteItemViewholder.java index 52206c9..900076f 100644 --- a/app/src/main/java/stream/rocketnotes/NoteItemViewholder.java +++ b/app/src/main/java/stream/rocketnotes/NoteItemViewholder.java @@ -1,10 +1,12 @@ package stream.rocketnotes; +import android.animation.Animator; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.preference.PreferenceManager; import android.support.annotation.NonNull; +import android.support.v7.widget.GridLayoutManager; import android.text.TextUtils; import android.util.Log; import android.view.MenuItem; @@ -23,6 +25,7 @@ import es.dmoral.toasty.Toasty; import eu.davidea.flexibleadapter.FlexibleAdapter; +import eu.davidea.flexibleadapter.helpers.AnimatorHelper; import eu.davidea.flexibleadapter.items.AbstractFlexibleItem; import eu.davidea.flexibleadapter.items.IFilterable; import eu.davidea.viewholders.FlexibleViewHolder; @@ -42,6 +45,8 @@ public NoteItemViewholder(String id, String noteText) { this.noteText = noteText; } + + /** * When an item is equals to another? * Write your own concept of equals, mandatory to implement. @@ -205,5 +210,20 @@ public MyViewHolder(View view, FlexibleAdapter adapter) { mBtnAdd = view.findViewById(R.id.btn_add); mBtnMore = view.findViewById(R.id.btn_more); } + + @Override + public void scrollAnimators(@NonNull List animators, int position, boolean isForward) { + if (mAdapter.getRecyclerView().getLayoutManager() instanceof GridLayoutManager) { + if (position % 2 != 0) + AnimatorHelper.slideInFromRightAnimator(animators, itemView, mAdapter.getRecyclerView(), 0.5f); + else + AnimatorHelper.slideInFromLeftAnimator(animators, itemView, mAdapter.getRecyclerView(), 0.5f); + } else { + if (isForward) + AnimatorHelper.slideInFromBottomAnimator(animators, itemView, mAdapter.getRecyclerView()); + else + AnimatorHelper.slideInFromTopAnimator(animators, itemView, mAdapter.getRecyclerView()); + } + } } } diff --git a/app/src/main/java/stream/rocketnotes/PopupActivity.java b/app/src/main/java/stream/rocketnotes/PopupActivity.java index 29d51e5..cb43b03 100644 --- a/app/src/main/java/stream/rocketnotes/PopupActivity.java +++ b/app/src/main/java/stream/rocketnotes/PopupActivity.java @@ -142,12 +142,10 @@ public void afterTextChanged(Editable s) { Log.d("Note Title", noteText[0]); } - public void beforeTextChanged(CharSequence s, int start, - int count, int after) { + public void beforeTextChanged(CharSequence s, int start, int count, int after) { } - public void onTextChanged(CharSequence s, int start, - int before, int count) { + public void onTextChanged(CharSequence s, int start, int before, int count) { if (s.length() >= 1) { float scale = getResources().getDisplayMetrics().density; int dpAsPixels = (int) (12 * scale + 0.5f); diff --git a/app/src/main/java/stream/rocketnotes/WidgetReviewViewholder.java b/app/src/main/java/stream/rocketnotes/WidgetReviewViewholder.java index 168a836..bd69cf2 100644 --- a/app/src/main/java/stream/rocketnotes/WidgetReviewViewholder.java +++ b/app/src/main/java/stream/rocketnotes/WidgetReviewViewholder.java @@ -1,262 +1,281 @@ -package stream.rocketnotes; - -import android.app.Activity; -import android.app.Dialog; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.graphics.Typeface; -import android.net.Uri; -import android.util.DisplayMetrics; -import android.util.Log; -import android.view.Gravity; -import android.view.View; -import android.widget.ImageButton; -import android.widget.LinearLayout; -import android.widget.RelativeLayout; -import android.widget.TextView; -import android.widget.Toast; - -import com.suke.widget.SwitchButton; - -import org.greenrobot.eventbus.EventBus; - -import java.util.ArrayList; -import java.util.List; - -import es.dmoral.toasty.Toasty; -import eu.davidea.flexibleadapter.FlexibleAdapter; -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem; -import eu.davidea.viewholders.FlexibleViewHolder; -import stream.customalert.CustomAlertDialogue; -import stream.rocketnotes.utils.AnalyticsUtils; -import stream.rocketnotes.utils.AnimUtils; -import stream.rocketnotes.utils.PermissionUtils; - -public class WidgetReviewViewholder extends AbstractFlexibleItem { - - private String id; - private boolean widgetReview; - private String mActivity = this.getClass().getSimpleName(); - private Activity activity; - - public WidgetReviewViewholder(String id, Activity activity) { - this.id = id; - this.activity = activity; - } - - /** - * When an item is equals to another? - * Write your own concept of equals, mandatory to implement. - * This will be explained in the "Item interfaces" Wiki page. - */ - @Override - public boolean equals(Object inObject) { - if (inObject instanceof WidgetReviewViewholder) { - WidgetReviewViewholder inItem = (WidgetReviewViewholder) inObject; - return this.id.equals(inItem.id); - } - return false; - } - - /** - * You should implement also this method if equals() is implemented. - * This method, if implemented, has several implications that Adapter handles better: - * - The Hash increases performance in big list during Update & Filter operations. - * - Collapsing many expandable items is much faster. - * - You might want to activate stable ids via Constructor for RV, if your id - * is unique (read more in the wiki page: "Setting Up Advanced"). - */ - @Override - public int hashCode() { - return id.hashCode(); - } - - /** - * For the item type we need an int value: the layoutResID is sufficient. - */ - @Override - public int getLayoutRes() { - return R.layout.item_widgetreview; - } - - @Override - public MyViewHolder createViewHolder(View view, FlexibleAdapter adapter) { - return new MyViewHolder(view, adapter); - } - - /** - * The Adapter and the Payload are provided to get more specific information from it. - */ - @Override - public void bindViewHolder(final FlexibleAdapter adapter, final MyViewHolder holder, final int position, - List payloads) { - final Context context = holder.itemView.getContext(); - GetReviewStatus(context); - final String appName = context.getPackageName(); - if (!widgetReview) { - holder.rateYes.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - AnalyticsUtils.AnalyticEvent(mActivity, "Click", "Review"); - holder.rateYes.startAnimation(AnimUtils.Bounce(context, R.anim.anim_bounce, 600)); - SetReviewStatus(context, true); - Intent intent = new Intent(Intent.ACTION_VIEW); - if (PermissionUtils.isAppInstalled(context, "com.android.vending")) { - intent.setData(Uri.parse("market://details?id=" + appName)); - try { - context.startActivity(intent); - } catch (android.content.ActivityNotFoundException ex) { - intent.setData(Uri.parse(String.format("https://play.google.com/store/apps/details?id=%s", context.getString(R.string.app_package)))); - context.startActivity(intent); - } - } else if (PermissionUtils.isAppInstalled(context, "com.amazon.venezia")) { - intent.setData(Uri.parse("amzn://apps/android?p=" + appName)); - try { - context.startActivity(intent); - } catch (android.content.ActivityNotFoundException ex) { - intent.setData(Uri.parse("https://www.amazon.com/Stream-Inc-Blank-Icon/dp/B06XDVL38F")); - context.startActivity(intent); - } - } else { - intent.setData(Uri.parse(String.format("https://play.google.com/store/apps/details?id=%s", context.getString(R.string.app_package)))); - context.startActivity(intent); - } - } - }); - holder.rateNo.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - AnalyticsUtils.AnalyticEvent(mActivity, "Click", "Feedback"); - holder.rateNo.startAnimation(AnimUtils.Bounce(context, R.anim.anim_bounce, 600)); - - ArrayList boxHint = new ArrayList<>(); - boxHint.add("Message"); - - CustomAlertDialogue.Builder alert = new CustomAlertDialogue.Builder(context) - .setStyle(CustomAlertDialogue.Style.INPUT) - .setTitle("Why so sad :(") - .setMessage("Please send us your feedback so we can make this app better!") - .setPositiveText("Submit") - .setPositiveColor(R.color.positive) - .setPositiveTypeface(Typeface.DEFAULT_BOLD) - .setOnInputClicked(new CustomAlertDialogue.OnInputClicked() { - @Override - public void OnClick(View view, Dialog dialog, ArrayList inputList) { - AnalyticsUtils.AnalyticEvent(mActivity, "Feedback", "Submit"); - Intent intent = new Intent(Intent.ACTION_SEND); - intent.setType("message/rfc822"); - intent.setType("vnd.android.cursor.item/email"); - intent.putExtra(Intent.EXTRA_EMAIL, new String[]{context.getString(R.string.email_mailto)}); - intent.putExtra(Intent.EXTRA_SUBJECT, context.getString(R.string.email_subject)); - intent.putExtra(Intent.EXTRA_TEXT, context.getString(R.string.email_message) + inputList.get(0)); - try { - Toasty.normal(context, "Send via email", Toast.LENGTH_SHORT).show(); - context.startActivity(Intent.createChooser(intent, "Send email using...")); - } catch (android.content.ActivityNotFoundException ex) { - Toasty.normal(context, "There are no email clients installed.", Toast.LENGTH_SHORT).show(); - } - } - }) - .setNegativeText("Close") - .setNegativeColor(R.color.negative) - .setOnNegativeClicked(new CustomAlertDialogue.OnNegativeClicked() { - @Override - public void OnClick(View view, Dialog dialog) { - dialog.dismiss(); - AnalyticsUtils.AnalyticEvent(mActivity, "Feedback", "Cancel"); - } - }) - .setBoxInputHint(boxHint) - .build(); - alert.show(); - } - }); - } else { - holder.rateNoLayout.setVisibility(View.GONE); - LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(dpToPx(context, 120), dpToPx(context, 120)); - layoutParams.gravity = Gravity.CENTER_HORIZONTAL; - holder.rateYes.setLayoutParams(layoutParams); - holder.body.setText("Thanks for the great review."); - holder.rateYesText.setText("You're the BEST!"); - holder.rateYes.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - holder.rateYes.startAnimation(AnimUtils.Bounce(context, R.anim.anim_bounce, 600)); - SetReviewStatus(context, false); - } - }); - holder.hideLayout.setVisibility(View.VISIBLE); - holder.switchButton.setOnCheckedChangeListener(new SwitchButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(SwitchButton view, boolean isChecked) { - if (!isChecked) { - AnalyticsUtils.AnalyticEvent(mActivity, "Click", "Hide"); - SetHideStatus(context, true); - EventBus.getDefault().post(new UpdateMainEvent(Constants.HIDE_REVIEW)); - Log.d("Notification", Constants.HIDE_REVIEW); - } - } - }); - } - } - - /** - * The ViewHolder used by this item. - * Extending from FlexibleViewHolder is recommended especially when you will use - * more advanced features. - */ - public class MyViewHolder extends FlexibleViewHolder { - - RelativeLayout widgetReviewLayout; - LinearLayout rateYesLayout; - LinearLayout rateNoLayout; - LinearLayout hideLayout; - ImageButton rateYes; - ImageButton rateNo; - TextView body; - TextView rateYesText; - SwitchButton switchButton; - - public MyViewHolder(View view, FlexibleAdapter adapter) { - super(view, adapter); - widgetReviewLayout = view.findViewById(R.id.item_widgetreview); - rateYes = view.findViewById(R.id.smiley_yes); - rateNo = view.findViewById(R.id.smiley_no); - rateYesLayout = view.findViewById(R.id.smiley_yes_layout); - rateNoLayout = view.findViewById(R.id.smiley_no_layout); - hideLayout = view.findViewById(R.id.hide_container); - body = view.findViewById(R.id.item_widgetreview_body); - rateYesText = view.findViewById(R.id.smiley_yes_text); - switchButton = view.findViewById(R.id.hide_switch); - } - } - - private void SetReviewStatus(Context context, boolean review) { - SharedPreferences prefs = context.getSharedPreferences("prefs", 0); - SharedPreferences.Editor editor = prefs.edit(); - editor.putBoolean(Constants.WIDGET_REVIEW, review); - editor.apply(); - } - - private void GetReviewStatus(Context context) { - SharedPreferences prefs = context.getSharedPreferences("prefs", 0); - widgetReview = prefs.getBoolean(Constants.WIDGET_REVIEW, false); - } - - private void SetHideStatus(Context context, boolean hide) { - SharedPreferences prefs = context.getSharedPreferences("prefs", 0); - SharedPreferences.Editor editor = prefs.edit(); - editor.putBoolean(Constants.WIDGET_REVIEW_HIDE, hide); - editor.apply(); - } - - /** - * Converts dp to pixels. - */ - public static int dpToPx(Context context, int dp) { - DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); - int px = Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT)); - return px; - } -} +package stream.rocketnotes; + +import android.animation.Animator; +import android.app.Activity; +import android.app.Dialog; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.graphics.Typeface; +import android.net.Uri; +import android.support.annotation.NonNull; +import android.support.v7.widget.GridLayoutManager; +import android.util.DisplayMetrics; +import android.util.Log; +import android.view.Gravity; +import android.view.View; +import android.widget.ImageButton; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; +import android.widget.TextView; +import android.widget.Toast; + +import com.suke.widget.SwitchButton; + +import org.greenrobot.eventbus.EventBus; + +import java.util.ArrayList; +import java.util.List; + +import es.dmoral.toasty.Toasty; +import eu.davidea.flexibleadapter.FlexibleAdapter; +import eu.davidea.flexibleadapter.helpers.AnimatorHelper; +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem; +import eu.davidea.viewholders.FlexibleViewHolder; +import stream.customalert.CustomAlertDialogue; +import stream.rocketnotes.utils.AnalyticsUtils; +import stream.rocketnotes.utils.AnimUtils; +import stream.rocketnotes.utils.PermissionUtils; + +public class WidgetReviewViewholder extends AbstractFlexibleItem { + + private String id; + private boolean widgetReview; + private String mActivity = this.getClass().getSimpleName(); + private Activity activity; + + public WidgetReviewViewholder(String id, Activity activity) { + this.id = id; + this.activity = activity; + } + + /** + * When an item is equals to another? + * Write your own concept of equals, mandatory to implement. + * This will be explained in the "Item interfaces" Wiki page. + */ + @Override + public boolean equals(Object inObject) { + if (inObject instanceof WidgetReviewViewholder) { + WidgetReviewViewholder inItem = (WidgetReviewViewholder) inObject; + return this.id.equals(inItem.id); + } + return false; + } + + /** + * You should implement also this method if equals() is implemented. + * This method, if implemented, has several implications that Adapter handles better: + * - The Hash increases performance in big list during Update & Filter operations. + * - Collapsing many expandable items is much faster. + * - You might want to activate stable ids via Constructor for RV, if your id + * is unique (read more in the wiki page: "Setting Up Advanced"). + */ + @Override + public int hashCode() { + return id.hashCode(); + } + + /** + * For the item type we need an int value: the layoutResID is sufficient. + */ + @Override + public int getLayoutRes() { + return R.layout.item_widgetreview; + } + + @Override + public MyViewHolder createViewHolder(View view, FlexibleAdapter adapter) { + return new MyViewHolder(view, adapter); + } + + /** + * The Adapter and the Payload are provided to get more specific information from it. + */ + @Override + public void bindViewHolder(final FlexibleAdapter adapter, final MyViewHolder holder, final int position, + List payloads) { + final Context context = holder.itemView.getContext(); + GetReviewStatus(context); + final String appName = context.getPackageName(); + if (!widgetReview) { + holder.rateYes.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + AnalyticsUtils.AnalyticEvent(mActivity, "Click", "Review"); + holder.rateYes.startAnimation(AnimUtils.Bounce(context, R.anim.anim_bounce, 600)); + SetReviewStatus(context, true); + Intent intent = new Intent(Intent.ACTION_VIEW); + if (PermissionUtils.isAppInstalled(context, "com.android.vending")) { + intent.setData(Uri.parse("market://details?id=" + appName)); + try { + context.startActivity(intent); + } catch (android.content.ActivityNotFoundException ex) { + intent.setData(Uri.parse(String.format("https://play.google.com/store/apps/details?id=%s", context.getString(R.string.app_package)))); + context.startActivity(intent); + } + } else if (PermissionUtils.isAppInstalled(context, "com.amazon.venezia")) { + intent.setData(Uri.parse("amzn://apps/android?p=" + appName)); + try { + context.startActivity(intent); + } catch (android.content.ActivityNotFoundException ex) { + intent.setData(Uri.parse("https://www.amazon.com/Stream-Inc-Blank-Icon/dp/B06XDVL38F")); + context.startActivity(intent); + } + } else { + intent.setData(Uri.parse(String.format("https://play.google.com/store/apps/details?id=%s", context.getString(R.string.app_package)))); + context.startActivity(intent); + } + } + }); + holder.rateNo.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + AnalyticsUtils.AnalyticEvent(mActivity, "Click", "Feedback"); + holder.rateNo.startAnimation(AnimUtils.Bounce(context, R.anim.anim_bounce, 600)); + + ArrayList boxHint = new ArrayList<>(); + boxHint.add("Message"); + + CustomAlertDialogue.Builder alert = new CustomAlertDialogue.Builder(context) + .setStyle(CustomAlertDialogue.Style.INPUT) + .setTitle("Why so sad :(") + .setMessage("Please send us your feedback so we can make this app better!") + .setPositiveText("Submit") + .setPositiveColor(R.color.positive) + .setPositiveTypeface(Typeface.DEFAULT_BOLD) + .setOnInputClicked(new CustomAlertDialogue.OnInputClicked() { + @Override + public void OnClick(View view, Dialog dialog, ArrayList inputList) { + AnalyticsUtils.AnalyticEvent(mActivity, "Feedback", "Submit"); + Intent intent = new Intent(Intent.ACTION_SEND); + intent.setType("message/rfc822"); + intent.setType("vnd.android.cursor.item/email"); + intent.putExtra(Intent.EXTRA_EMAIL, new String[]{context.getString(R.string.email_mailto)}); + intent.putExtra(Intent.EXTRA_SUBJECT, context.getString(R.string.email_subject)); + intent.putExtra(Intent.EXTRA_TEXT, context.getString(R.string.email_message) + inputList.get(0)); + try { + Toasty.normal(context, "Send via email", Toast.LENGTH_SHORT).show(); + context.startActivity(Intent.createChooser(intent, "Send email using...")); + } catch (android.content.ActivityNotFoundException ex) { + Toasty.normal(context, "There are no email clients installed.", Toast.LENGTH_SHORT).show(); + } + } + }) + .setNegativeText("Close") + .setNegativeColor(R.color.negative) + .setOnNegativeClicked(new CustomAlertDialogue.OnNegativeClicked() { + @Override + public void OnClick(View view, Dialog dialog) { + dialog.dismiss(); + AnalyticsUtils.AnalyticEvent(mActivity, "Feedback", "Cancel"); + } + }) + .setBoxInputHint(boxHint) + .build(); + alert.show(); + } + }); + } else { + holder.rateNoLayout.setVisibility(View.GONE); + LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(dpToPx(context, 120), dpToPx(context, 120)); + layoutParams.gravity = Gravity.CENTER_HORIZONTAL; + holder.rateYes.setLayoutParams(layoutParams); + holder.body.setText("Thanks for the great review."); + holder.rateYesText.setText("You're the BEST!"); + holder.rateYes.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + holder.rateYes.startAnimation(AnimUtils.Bounce(context, R.anim.anim_bounce, 600)); + SetReviewStatus(context, false); + } + }); + holder.hideLayout.setVisibility(View.VISIBLE); + holder.switchButton.setOnCheckedChangeListener(new SwitchButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(SwitchButton view, boolean isChecked) { + if (!isChecked) { + AnalyticsUtils.AnalyticEvent(mActivity, "Click", "Hide"); + SetHideStatus(context, true); + EventBus.getDefault().post(new UpdateMainEvent(Constants.HIDE_REVIEW)); + Log.d("Notification", Constants.HIDE_REVIEW); + } + } + }); + } + } + + /** + * The ViewHolder used by this item. + * Extending from FlexibleViewHolder is recommended especially when you will use + * more advanced features. + */ + public class MyViewHolder extends FlexibleViewHolder { + + RelativeLayout widgetReviewLayout; + LinearLayout rateYesLayout; + LinearLayout rateNoLayout; + LinearLayout hideLayout; + ImageButton rateYes; + ImageButton rateNo; + TextView body; + TextView rateYesText; + SwitchButton switchButton; + + public MyViewHolder(View view, FlexibleAdapter adapter) { + super(view, adapter); + widgetReviewLayout = view.findViewById(R.id.item_widgetreview); + rateYes = view.findViewById(R.id.smiley_yes); + rateNo = view.findViewById(R.id.smiley_no); + rateYesLayout = view.findViewById(R.id.smiley_yes_layout); + rateNoLayout = view.findViewById(R.id.smiley_no_layout); + hideLayout = view.findViewById(R.id.hide_container); + body = view.findViewById(R.id.item_widgetreview_body); + rateYesText = view.findViewById(R.id.smiley_yes_text); + switchButton = view.findViewById(R.id.hide_switch); + } + + @Override + public void scrollAnimators(@NonNull List animators, int position, boolean isForward) { + if (mAdapter.getRecyclerView().getLayoutManager() instanceof GridLayoutManager) { + if (position % 2 != 0) + AnimatorHelper.slideInFromRightAnimator(animators, itemView, mAdapter.getRecyclerView(), 0.5f); + else + AnimatorHelper.slideInFromLeftAnimator(animators, itemView, mAdapter.getRecyclerView(), 0.5f); + } else { + if (isForward) + AnimatorHelper.slideInFromBottomAnimator(animators, itemView, mAdapter.getRecyclerView()); + else + AnimatorHelper.slideInFromTopAnimator(animators, itemView, mAdapter.getRecyclerView()); + } + } + } + + private void SetReviewStatus(Context context, boolean review) { + SharedPreferences prefs = context.getSharedPreferences("prefs", 0); + SharedPreferences.Editor editor = prefs.edit(); + editor.putBoolean(Constants.WIDGET_REVIEW, review); + editor.apply(); + } + + private void GetReviewStatus(Context context) { + SharedPreferences prefs = context.getSharedPreferences("prefs", 0); + widgetReview = prefs.getBoolean(Constants.WIDGET_REVIEW, false); + } + + private void SetHideStatus(Context context, boolean hide) { + SharedPreferences prefs = context.getSharedPreferences("prefs", 0); + SharedPreferences.Editor editor = prefs.edit(); + editor.putBoolean(Constants.WIDGET_REVIEW_HIDE, hide); + editor.apply(); + } + + /** + * Converts dp to pixels. + */ + public static int dpToPx(Context context, int dp) { + DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); + int px = Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT)); + return px; + } +} diff --git a/app/src/main/java/stream/rocketnotes/ui/LandingItemAnimator.java b/app/src/main/java/stream/rocketnotes/ui/LandingItemAnimator.java new file mode 100644 index 0000000..feae913 --- /dev/null +++ b/app/src/main/java/stream/rocketnotes/ui/LandingItemAnimator.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2015 Wasabeef + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package stream.rocketnotes.ui; + +import android.support.v4.view.ViewCompat; +import android.support.v7.widget.RecyclerView; +import android.view.animation.Interpolator; + +import eu.davidea.flexibleadapter.common.FlexibleItemAnimator; + +public class LandingItemAnimator extends FlexibleItemAnimator { + + public LandingItemAnimator() { + } + + public LandingItemAnimator(Interpolator interpolator) { + mInterpolator = interpolator; + } + + @Override + protected void animateRemoveImpl(final RecyclerView.ViewHolder holder, final int index) { + ViewCompat.animate(holder.itemView) + .alpha(0) + .scaleX(1.5f) + .scaleY(1.5f) + .setDuration(getRemoveDuration()) + .setInterpolator(mInterpolator) + .setListener(new DefaultRemoveVpaListener(holder)) + .start(); + } + + @Override + protected boolean preAnimateAddImpl(final RecyclerView.ViewHolder holder) { + holder.itemView.setAlpha(0); + holder.itemView.setScaleX(1.5f); + holder.itemView.setScaleY(1.5f); + return true; + } + + @Override + protected void animateAddImpl(final RecyclerView.ViewHolder holder, final int index) { + ViewCompat.animate(holder.itemView) + .alpha(1) + .scaleX(1) + .scaleY(1) + .setDuration(getAddDuration()) + .setInterpolator(mInterpolator) + .setListener(new DefaultAddVpaListener(holder)) + .start(); + } + +} \ No newline at end of file