From 358e52cb90d5aa12e278fc76d3cbeac1c9197942 Mon Sep 17 00:00:00 2001 From: Zac Sweers Date: Tue, 1 Dec 2015 10:18:02 -0800 Subject: [PATCH] Backport Pt. 1 This commit is intended to be minimal-to-no structural changes, focusing just on compat drop-ins, API annotations, and beginning some work in gating off transitions that won't run on pre-lollipop. This commit: - Drops in compat* versions wherever possible, while including some local compat implementations as well. - Target annotations on areas that will only be Lollipop+ (for future reference, no logic changes yet) - Using appcompat theming where possible rather than material Future work would focus around fully supporting conditional transitions and vector drawable interactions, taking advantage of android's resource directory versioning for API-dependent resources, and backporting some functionality with new logic (such as circular imageviews). --- app/build.gradle | 1 + .../data/api/dribbble/model/Comment.java | 3 +- .../java/io/plaidapp/ui/AboutActivity.java | 7 +- .../io/plaidapp/ui/DesignerNewsLogin.java | 41 ++-- .../io/plaidapp/ui/DesignerNewsStory.java | 120 ++++----- .../java/io/plaidapp/ui/DribbbleLogin.java | 110 +++++---- .../java/io/plaidapp/ui/DribbbleShot.java | 229 ++++++++++-------- .../main/java/io/plaidapp/ui/FeedAdapter.java | 37 +-- .../java/io/plaidapp/ui/FilterAdapter.java | 46 ++-- .../java/io/plaidapp/ui/HomeActivity.java | 114 +++++---- .../io/plaidapp/ui/HomeGridItemAnimator.java | 33 +-- .../java/io/plaidapp/ui/PlayerActivity.java | 27 ++- .../main/java/io/plaidapp/ui/PlayerSheet.java | 25 +- .../plaidapp/ui/PostNewDesignerNewsStory.java | 34 ++- .../java/io/plaidapp/ui/SearchActivity.java | 89 +++---- .../plaidapp/ui/drawable/MorphDrawable.java | 9 +- .../ui/transitions/FabDialogMorphSetup.java | 5 +- .../ui/transitions/MorphDialogToFab.java | 7 +- .../ui/transitions/MorphFabToDialog.java | 7 +- .../java/io/plaidapp/ui/transitions/Pop.java | 3 + .../ui/transitions/ShotSharedEnter.java | 3 + .../ui/transitions/StoryTitleSharedEnter.java | 3 + .../ui/transitions/TextResizeTransition.java | 3 + .../ui/widget/BadgedFourThreeImageView.java | 4 +- .../ui/widget/BaselineGridTextView.java | 5 +- .../io/plaidapp/ui/widget/BottomSheet.java | 6 +- .../plaidapp/ui/widget/CircularImageView.java | 22 +- .../ui/widget/CollapsingTitleLayout.java | 19 +- .../plaidapp/ui/widget/DynamicTextView.java | 23 +- .../widget/ElasticDragDismissFrameLayout.java | 43 ++-- .../io/plaidapp/ui/widget/FontTextView.java | 3 + .../ui/widget/ForegroundImageView.java | 9 +- .../ui/widget/ForegroundLinearLayout.java | 3 +- .../ui/widget/ForegroundRelativeLayout.java | 8 +- .../plaidapp/ui/widget/InkPageIndicator.java | 21 +- .../ui/widget/ParallaxScrimageView.java | 6 +- .../plaidapp/ui/widget/PinnedOffsetView.java | 5 +- .../main/java/io/plaidapp/util/AnimUtils.java | 27 ++- .../java/io/plaidapp/util/DribbbleUtils.java | 3 +- .../main/java/io/plaidapp/util/HtmlUtils.java | 5 +- .../java/io/plaidapp/util/ImageUtils.java | 3 +- .../main/java/io/plaidapp/util/MathUtils.java | 17 ++ .../main/java/io/plaidapp/util/ViewUtils.java | 53 ++-- .../io/plaidapp/util/compat/CanvasCompat.java | 27 +++ .../plaidapp/util/compat/ImageViewCompat.java | 43 ++++ .../util/compat/LocalTextViewCompat.java | 32 +++ .../util/compat/ObjectAnimatorCompat.java | 92 +++++++ .../util/compat/TransitionManagerCompat.java | 25 ++ .../util/compat/ViewAnimationUtilsCompat.java | 55 +++++ .../util/glide/GlideConfiguration.java | 3 +- .../plaidapp/util/glide/ImageSpanTarget.java | 4 +- .../designer_news_app_bar_background.xml | 4 +- .../designer_news_custom_tab_placeholder.xml | 2 +- app/src/main/res/drawable/fab.xml | 4 +- app/src/main/res/drawable/ic_add_light.xml | 2 +- app/src/main/res/drawable/ic_edit.xml | 2 +- .../designer_news_story_title_toolbar.xml | 13 +- app/src/main/res/layout/about_icon.xml | 2 +- app/src/main/res/layout/about_plaid.xml | 4 +- .../layout/activity_designer_news_login.xml | 4 +- .../layout/activity_designer_news_story.xml | 6 +- .../res/layout/activity_dribbble_shot.xml | 4 +- app/src/main/res/layout/activity_home.xml | 6 +- .../activity_post_new_designer_news_story.xml | 4 +- app/src/main/res/layout/activity_search.xml | 27 ++- .../main/res/layout/designer_news_comment.xml | 2 +- .../layout/designer_news_comment_actions.xml | 6 +- .../layout/designer_news_enter_comment.xml | 4 +- app/src/main/res/layout/dribbble_comment.xml | 10 +- .../res/layout/dribbble_enter_comment.xml | 4 +- app/src/main/res/layout/filter_item.xml | 4 +- app/src/main/res/layout/library.xml | 6 +- app/src/main/res/layout/no_connection.xml | 2 +- app/src/main/res/layout/no_filters.xml | 2 +- app/src/main/res/menu/main.xml | 15 +- app/src/main/res/values-h500dp/dimens.xml | 2 +- app/src/main/res/values/dimens.xml | 4 +- app/src/main/res/values/styles.xml | 141 ++++++----- 78 files changed, 1144 insertions(+), 664 deletions(-) create mode 100644 app/src/main/java/io/plaidapp/util/compat/CanvasCompat.java create mode 100644 app/src/main/java/io/plaidapp/util/compat/ImageViewCompat.java create mode 100644 app/src/main/java/io/plaidapp/util/compat/LocalTextViewCompat.java create mode 100644 app/src/main/java/io/plaidapp/util/compat/ObjectAnimatorCompat.java create mode 100644 app/src/main/java/io/plaidapp/util/compat/TransitionManagerCompat.java create mode 100644 app/src/main/java/io/plaidapp/util/compat/ViewAnimationUtilsCompat.java diff --git a/app/build.gradle b/app/build.gradle index 47e735c42..aa3eaf09e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -44,6 +44,7 @@ android { buildConfigField "String", "PROCUCT_HUNT_DEVELOPER_TOKEN", "\"${product_hunt_developer_token}\"" + generatedDensities = [] } buildTypes { release { diff --git a/app/src/main/java/io/plaidapp/data/api/dribbble/model/Comment.java b/app/src/main/java/io/plaidapp/data/api/dribbble/model/Comment.java index 5903ac885..1bd25aa5d 100644 --- a/app/src/main/java/io/plaidapp/data/api/dribbble/model/Comment.java +++ b/app/src/main/java/io/plaidapp/data/api/dribbble/model/Comment.java @@ -23,6 +23,7 @@ import java.util.Date; import io.plaidapp.util.DribbbleUtils; +import io.plaidapp.util.compat.LocalTextViewCompat; /** * Models a commend on a Dribbble shot. @@ -59,7 +60,7 @@ public Comment(long id, public Spanned getParsedBody(TextView textView) { if (parsedBody == null && !TextUtils.isEmpty(body)) { parsedBody = DribbbleUtils.parseDribbbleHtml(body, textView.getLinkTextColors(), - textView.getHighlightColor()); + LocalTextViewCompat.getHighlightColor(textView)); } return parsedBody; } diff --git a/app/src/main/java/io/plaidapp/ui/AboutActivity.java b/app/src/main/java/io/plaidapp/ui/AboutActivity.java index 3b6abf12d..351fadb02 100644 --- a/app/src/main/java/io/plaidapp/ui/AboutActivity.java +++ b/app/src/main/java/io/plaidapp/ui/AboutActivity.java @@ -20,8 +20,10 @@ import android.content.Context; import android.content.Intent; import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.support.annotation.Nullable; +import android.support.v4.app.ActivityCompat; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; import android.support.v7.widget.RecyclerView; @@ -79,12 +81,13 @@ protected void onCreate(Bundle savedInstanceState) { public void onDragDismissed() { // if we drag dismiss downward then the default reversal of the enter // transition would slide content upward which looks weird. So reverse it. - if (draggableFrame.getTranslationY() > 0) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP + && draggableFrame.getTranslationY() > 0) { getWindow().setReturnTransition( TransitionInflater.from(AboutActivity.this) .inflateTransition(R.transition.about_return_downward)); } - finishAfterTransition(); + ActivityCompat.finishAfterTransition(AboutActivity.this); } }); } diff --git a/app/src/main/java/io/plaidapp/ui/DesignerNewsLogin.java b/app/src/main/java/io/plaidapp/ui/DesignerNewsLogin.java index f5d0d8bcc..d09eb9a44 100644 --- a/app/src/main/java/io/plaidapp/ui/DesignerNewsLogin.java +++ b/app/src/main/java/io/plaidapp/ui/DesignerNewsLogin.java @@ -23,14 +23,16 @@ import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.design.widget.TextInputLayout; +import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; +import android.support.v4.view.ViewCompat; import android.text.Editable; import android.text.TextWatcher; import android.transition.Transition; -import android.transition.TransitionManager; import android.util.Log; import android.util.Patterns; import android.view.Gravity; @@ -72,6 +74,8 @@ import io.plaidapp.ui.transitions.FabDialogMorphSetup; import io.plaidapp.util.AnimUtils; import io.plaidapp.util.ScrimUtil; +import io.plaidapp.util.ViewUtils; +import io.plaidapp.util.compat.TransitionManagerCompat; import io.plaidapp.util.glide.CircleTransform; import retrofit.Callback; import retrofit.RestAdapter; @@ -104,9 +108,9 @@ protected void onCreate(Bundle savedInstanceState) { ButterKnife.bind(this); FabDialogMorphSetup.setupSharedEelementTransitions(this, container, getResources().getDimensionPixelSize(R.dimen.dialog_corners)); - if (getWindow().getSharedElementEnterTransition() != null) { - getWindow().getSharedElementEnterTransition().addListener(new AnimUtils - .TransitionListenerAdapter() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP + && getWindow().getSharedElementEnterTransition() != null) { + getWindow().getSharedElementEnterTransition().addListener(new AnimUtils.TransitionListenerAdapter() { @Override public void onTransitionEnd(Transition transition) { finishSetup(); @@ -151,10 +155,10 @@ public void onBackPressed() { @Override public void onRequestPermissionsResult(int requestCode, - String[] permissions, - int[] grantResults) { + @NonNull String[] permissions, + @NonNull int[] grantResults) { if (requestCode == PERMISSIONS_REQUEST_GET_ACCOUNTS) { - TransitionManager.beginDelayedTransition(container); + TransitionManagerCompat.beginDelayedTransition(container); if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { setupAccountAutocomplete(); username.requestFocus(); @@ -162,11 +166,11 @@ public void onRequestPermissionsResult(int requestCode, } else { // if permission was denied check if we should ask again in the future (i.e. they // did not check 'never ask again') - if (shouldShowRequestPermissionRationale(Manifest.permission.GET_ACCOUNTS)) { + if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.GET_ACCOUNTS)) { setupPermissionPrimer(); } else { // denied & shouldn't ask again. deal with it (•_•) ( •_•)>⌐■-■ (⌐■_■) - TransitionManager.beginDelayedTransition(container); + TransitionManagerCompat.beginDelayedTransition(container); permissionPrimer.setVisibility(View.GONE); } } @@ -186,7 +190,7 @@ public void signup(View view) { public void dismiss(View view) { isDismissing = true; setResult(Activity.RESULT_CANCELED); - finishAfterTransition(); + ActivityCompat.finishAfterTransition(this); } /** @@ -196,7 +200,7 @@ public void dismiss(View view) { */ private void finishSetup() { if (shouldPromptForPermission) { - requestPermissions(new String[]{ Manifest.permission.GET_ACCOUNTS }, + ActivityCompat.requestPermissions(this, new String[]{ Manifest.permission.GET_ACCOUNTS }, PERMISSIONS_REQUEST_GET_ACCOUNTS); shouldPromptForPermission = false; } @@ -222,7 +226,7 @@ public void afterTextChanged(Editable s) { private void maybeShowAccounts() { if (username.hasFocus() - && username.isAttachedToWindow() + && ViewCompat.isAttachedToWindow(username) && username.getAdapter() != null && username.getAdapter().getCount() > 0) { username.showDropDown(); @@ -234,7 +238,7 @@ private boolean isLoginValid() { } private void showLoading() { - TransitionManager.beginDelayedTransition(container); + TransitionManagerCompat.beginDelayedTransition(container); title.setVisibility(View.GONE); usernameLabel.setVisibility(View.GONE); permissionPrimer.setVisibility(View.GONE); @@ -244,7 +248,7 @@ private void showLoading() { } private void showLogin() { - TransitionManager.beginDelayedTransition(container); + TransitionManagerCompat.beginDelayedTransition(container); title.setVisibility(View.VISIBLE); usernameLabel.setVisibility(View.VISIBLE); passwordLabel.setVisibility(View.VISIBLE); @@ -314,8 +318,8 @@ public void success(UserResponse userResponse, Response response) { .placeholder(R.drawable.avatar_placeholder) .transform(new CircleTransform(getApplicationContext())) .into((ImageView) v.findViewById(R.id.avatar)); - v.findViewById(R.id.scrim).setBackground(ScrimUtil - .makeCubicGradientScrimDrawable( + ViewUtils.setBackground(v.findViewById(R.id.scrim), + ScrimUtil.makeCubicGradientScrimDrawable( ContextCompat.getColor(DesignerNewsLogin.this, R.color.scrim), 5, Gravity.BOTTOM)); confirmLogin.setView(v); @@ -345,7 +349,7 @@ private void setupAccountAutocomplete() { username.setAdapter(new ArrayAdapter<>(this, R.layout.account_dropdown_item, new ArrayList<>(emailSet))); } else { - if (shouldShowRequestPermissionRationale(Manifest.permission.GET_ACCOUNTS)) { + if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.GET_ACCOUNTS)) { setupPermissionPrimer(); } else { permissionPrimer.setVisibility(View.GONE); @@ -361,7 +365,8 @@ private void setupPermissionPrimer() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (isChecked) { - requestPermissions(new String[]{ Manifest.permission.GET_ACCOUNTS }, + ActivityCompat.requestPermissions(DesignerNewsLogin.this, new String[]{ Manifest.permission + .GET_ACCOUNTS }, PERMISSIONS_REQUEST_GET_ACCOUNTS); } } diff --git a/app/src/main/java/io/plaidapp/ui/DesignerNewsStory.java b/app/src/main/java/io/plaidapp/ui/DesignerNewsStory.java index 4ec06ef68..9c15d1f65 100644 --- a/app/src/main/java/io/plaidapp/ui/DesignerNewsStory.java +++ b/app/src/main/java/io/plaidapp/ui/DesignerNewsStory.java @@ -22,7 +22,6 @@ import android.animation.ObjectAnimator; import android.annotation.TargetApi; import android.app.Activity; -import android.app.ActivityOptions; import android.app.PendingIntent; import android.app.assist.AssistContent; import android.content.Context; @@ -37,11 +36,17 @@ import android.support.customtabs.CustomTabsIntent; import android.support.customtabs.CustomTabsSession; import android.support.design.widget.TextInputLayout; +import android.support.v4.app.ActivityCompat; +import android.support.v4.app.ActivityOptionsCompat; import android.support.v4.app.ShareCompat; import android.support.v4.content.ContextCompat; +import android.support.v4.view.MarginLayoutParamsCompat; +import android.support.v4.view.ViewCompat; +import android.support.v4.view.ViewPropertyAnimatorListenerAdapter; import android.support.v7.widget.DefaultItemAnimator; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.Toolbar; import android.text.SpannableString; import android.text.Spanned; import android.text.TextUtils; @@ -51,7 +56,6 @@ import android.transition.Transition; import android.util.TypedValue; import android.view.View; -import android.view.ViewAnimationUtils; import android.view.ViewGroup; import android.view.animation.Interpolator; import android.widget.Button; @@ -60,7 +64,6 @@ import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; -import android.widget.Toolbar; import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; @@ -97,6 +100,8 @@ import io.plaidapp.util.ImageUtils; import io.plaidapp.util.ImeUtils; import io.plaidapp.util.ViewUtils; +import io.plaidapp.util.compat.ObjectAnimatorCompat; +import io.plaidapp.util.compat.ViewAnimationUtilsCompat; import io.plaidapp.util.customtabs.CustomTabActivityHelper; import io.plaidapp.util.glide.CircleTransform; import io.plaidapp.util.glide.ImageSpanTarget; @@ -143,7 +148,9 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_designer_news_story); ButterKnife.bind(this); - getWindow().getSharedElementReturnTransition().addListener(returnHomeListener); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getWindow().getSharedElementReturnTransition().addListener(returnHomeListener); + } story = getIntent().getParcelableExtra(EXTRA_STORY); fab.setOnClickListener(fabClick); @@ -151,7 +158,7 @@ protected void onCreate(Bundle savedInstanceState) { chromeFader = new ElasticDragDismissFrameLayout.SystemChromeFader(getWindow()) { @Override public void onDragDismissed() { - finishAfterTransition(); + ActivityCompat.finishAfterTransition(DesignerNewsStory.this); } }; @@ -191,7 +198,7 @@ public void onDragDismissed() { toolbar.setNavigationOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { - finishAfterTransition(); + ActivityCompat.finishAfterTransition(DesignerNewsStory.this); } }); @@ -331,23 +338,23 @@ private void updateFabVisibility() { if (!fabShouldBeVisible && fabIsVisible) { fabIsVisible = false; - fab.animate() + ViewCompat.animate(fab) .scaleX(0f) .scaleY(0f) .alpha(0.6f) .setDuration(200L) - .setInterpolator(getFastOutLinearInInterpolator(this)) + .setInterpolator(getFastOutLinearInInterpolator()) .withLayer() .setListener(postHideFab) .start(); } else if (fabShouldBeVisible && !fabIsVisible) { fabIsVisible = true; - fab.animate() + ViewCompat.animate(fab) .scaleX(1f) .scaleY(1f) .alpha(1f) .setDuration(200L) - .setInterpolator(getLinearOutSlowInInterpolator(this)) + .setInterpolator(getLinearOutSlowInInterpolator()) .withLayer() .setListener(preShowFab) .start(); @@ -355,16 +362,16 @@ private void updateFabVisibility() { } } - private AnimatorListenerAdapter preShowFab = new AnimatorListenerAdapter() { + private ViewPropertyAnimatorListenerAdapter preShowFab = new ViewPropertyAnimatorListenerAdapter() { @Override - public void onAnimationStart(Animator animation) { + public void onAnimationStart(View view) { fab.setVisibility(View.VISIBLE); } }; - private AnimatorListenerAdapter postHideFab = new AnimatorListenerAdapter() { + private ViewPropertyAnimatorListenerAdapter postHideFab = new ViewPropertyAnimatorListenerAdapter() { @Override - public void onAnimationEnd(Animator animation) { + public void onAnimationEnd(View view) { fab.setVisibility(View.GONE); } }; @@ -375,9 +382,9 @@ public void onAnimationEnd(Animator animation) { public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { if ((bottom - top) != (oldBottom - oldTop)) { - commentsList.setPaddingRelative(commentsList.getPaddingStart(), + ViewCompat.setPaddingRelative(commentsList, ViewCompat.getPaddingStart(commentsList), collapsingToolbar.getHeight(), - commentsList.getPaddingEnd(), + ViewCompat.getPaddingEnd(commentsList), commentsList.getPaddingBottom()); commentsList.scrollToPosition(0); } @@ -422,7 +429,7 @@ private void doFabExpand() { // then reveal the placeholder ui, starting from the center & same dimens as fab fabExpand.setVisibility(View.VISIBLE); - Animator reveal = ViewAnimationUtils.createCircularReveal( + Animator reveal = ViewAnimationUtilsCompat.createCircularReveal( fabExpand, fabExpand.getWidth() / 2, fabExpand.getHeight() / 2, @@ -434,12 +441,12 @@ private void doFabExpand() { ArcMotion arcMotion = new ArcMotion(); arcMotion.setMinimumVerticalAngle(70f); Path motionPath = arcMotion.getPath(translateX, translateY, 0, 0); - Animator position = ObjectAnimator.ofFloat(fabExpand, View.TRANSLATION_X, View + Animator position = ObjectAnimatorCompat.ofFloat(fabExpand, View.TRANSLATION_X, View .TRANSLATION_Y, motionPath) .setDuration(fabExpandDuration); // animate from the FAB colour to the placeholder background color - Animator background = ObjectAnimator.ofArgb(fabExpand, + Animator background = ObjectAnimatorCompat.ofArgb(fabExpand, ViewUtils.BACKGROUND_COLOR, ContextCompat.getColor(this, R.color.designer_news), ContextCompat.getColor(this, R.color.background_light)) @@ -451,7 +458,7 @@ private void doFabExpand() { // play 'em all together with the material interpolator AnimatorSet show = new AnimatorSet(); - show.setInterpolator(getFastOutSlowInInterpolator(DesignerNewsStory.this)); + show.setInterpolator(getFastOutSlowInInterpolator()); show.playTogether(reveal, background, position, fadeOutFab); show.start(); } @@ -591,10 +598,10 @@ private void needsLogin(View triggeringView, int requestCode) { DesignerNewsLogin.class); login.putExtra(FabDialogMorphSetup.EXTRA_SHARED_ELEMENT_START_COLOR, ContextCompat.getColor(DesignerNewsStory.this, R.color.background_light)); - ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation( + ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation( DesignerNewsStory.this, triggeringView, getString(R.string.transition_designer_news_login)); - startActivityForResult(login, requestCode, options.toBundle()); + ActivityCompat.startActivityForResult(this, login, requestCode, options.toBundle()); } private void createDesignerNewsApi() { @@ -849,13 +856,14 @@ public void loadImage(String src, ImageLoadingSpan loadingSpan) { holder.itemView.setActivated(holder.getAdapterPosition() == expandedCommentPosition); if (holder.getAdapterPosition() == expandedCommentPosition) { final int threadDepthWidth = holder.threadDepth.getDrawable().getIntrinsicWidth(); - final float leftShift = -(threadDepthWidth + ((ViewGroup.MarginLayoutParams) - holder.threadDepth.getLayoutParams()).getMarginEnd()); + final float leftShift = -(threadDepthWidth + + MarginLayoutParamsCompat.getMarginEnd( + (ViewGroup.MarginLayoutParams) holder.threadDepth.getLayoutParams())); holder.author.setTranslationX(leftShift); holder.comment.setTranslationX(leftShift); holder.threadDepth.setTranslationX(-(threadDepthWidth - + ((ViewGroup.MarginLayoutParams) - holder.threadDepth.getLayoutParams()).getMarginStart())); + + MarginLayoutParamsCompat.getMarginStart( + (ViewGroup.MarginLayoutParams) holder.threadDepth.getLayoutParams()))); } else { holder.threadDepth.setTranslationX(0f); holder.author.setTranslationX(0f); @@ -947,8 +955,7 @@ public void failure(RetrofitError error) { @Override public void onFocusChange(View v, boolean hasFocus) { replyToCommentFocused = hasFocus; - final Interpolator interp = getFastOutSlowInInterpolator(holder - .itemView.getContext()); + final Interpolator interp = getFastOutSlowInInterpolator(); if (hasFocus) { holder.commentVotes.animate() .translationX(-holder.commentVotes.getWidth()) @@ -968,12 +975,12 @@ public void onFocusChange(View v, boolean hasFocus) { .setListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { - holder.itemView.setHasTransientState(true); + ViewCompat.setHasTransientState(holder.itemView, true); } @Override public void onAnimationEnd(Animator animation) { - holder.itemView.setHasTransientState(false); + ViewCompat.setHasTransientState(holder.itemView, false); } }); updateFabVisibility(); @@ -994,13 +1001,13 @@ public void onAnimationEnd(Animator animation) { .setListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { - holder.itemView.setHasTransientState(true); + ViewCompat.setHasTransientState(holder.itemView, true); } @Override public void onAnimationEnd(Animator animation) { holder.postReply.setVisibility(View.INVISIBLE); - holder.itemView.setHasTransientState(true); + ViewCompat.setHasTransientState(holder.itemView, true); } }); updateFabVisibility(); @@ -1079,10 +1086,11 @@ public boolean canReuseUpdatedViewHolder(RecyclerView.ViewHolder viewHolder) { @NonNull @Override - public ItemHolderInfo recordPreLayoutInformation(RecyclerView.State state, - RecyclerView.ViewHolder viewHolder, - int changeFlags, - List payloads) { + public ItemHolderInfo recordPreLayoutInformation( + @NonNull RecyclerView.State state, + @NonNull RecyclerView.ViewHolder viewHolder, + int changeFlags, + @NonNull List payloads) { CommentItemHolderInfo info = (CommentItemHolderInfo) super.recordPreLayoutInformation(state, viewHolder, changeFlags, payloads); info.doExpand = payloads.contains(EXPAND_COMMENT); @@ -1091,23 +1099,23 @@ public ItemHolderInfo recordPreLayoutInformation(RecyclerView.State state, } @Override - public boolean animateChange(RecyclerView.ViewHolder oldHolder, - RecyclerView.ViewHolder newHolder, - ItemHolderInfo preInfo, - ItemHolderInfo postInfo) { + public boolean animateChange( + @NonNull RecyclerView.ViewHolder oldHolder, + @NonNull RecyclerView.ViewHolder newHolder, + @NonNull ItemHolderInfo preInfo, + @NonNull ItemHolderInfo postInfo) { if (newHolder instanceof CommentHolder && preInfo instanceof CommentItemHolderInfo) { final CommentHolder holder = (CommentHolder) newHolder; final CommentItemHolderInfo info = (CommentItemHolderInfo) preInfo; - final float expandedThreadOffset = -(holder.threadDepth.getWidth() + ((ViewGroup - .MarginLayoutParams) holder.threadDepth.getLayoutParams()) - .getMarginStart()); - final float expandedAuthorCommentOffset = -(holder.threadDepth.getWidth() + - ((ViewGroup.MarginLayoutParams) holder.threadDepth.getLayoutParams()) - .getMarginEnd()); + final float expandedThreadOffset = -(holder.threadDepth.getWidth() + + MarginLayoutParamsCompat.getMarginStart( + (ViewGroup.MarginLayoutParams) holder.threadDepth.getLayoutParams())); + final float expandedAuthorCommentOffset = -(holder.threadDepth.getWidth() + + MarginLayoutParamsCompat.getMarginEnd( + (ViewGroup.MarginLayoutParams) holder.threadDepth.getLayoutParams())); if (info.doExpand) { - Interpolator moveInterpolator = getFastOutSlowInInterpolator(holder - .itemView.getContext()); + Interpolator moveInterpolator = getFastOutSlowInInterpolator(); holder.threadDepth.setTranslationX(0f); holder.threadDepth.animate() .translationX(expandedThreadOffset) @@ -1128,22 +1136,18 @@ public boolean animateChange(RecyclerView.ViewHolder oldHolder, @Override public void onAnimationStart(Animator animation) { dispatchChangeStarting(holder, false); - holder.itemView.setHasTransientState(true); + ViewCompat.setHasTransientState(holder.itemView, true); } @Override public void onAnimationEnd(Animator animation) { - holder.itemView.setHasTransientState(false); + ViewCompat.setHasTransientState(holder.itemView, false); dispatchChangeFinished(holder, false); } }); } else if (info.doCollapse) { - Interpolator enterInterpolator = getLinearOutSlowInInterpolator - (holder.itemView - .getContext()); - Interpolator moveInterpolator = getFastOutSlowInInterpolator(holder - .itemView - .getContext()); + Interpolator enterInterpolator = getLinearOutSlowInInterpolator(); + Interpolator moveInterpolator = getFastOutSlowInInterpolator(); // return the thread depth indicator into place holder.threadDepth.setTranslationX(expandedThreadOffset); @@ -1156,12 +1160,12 @@ public void onAnimationEnd(Animator animation) { @Override public void onAnimationStart(Animator animation) { dispatchChangeStarting(holder, false); - holder.itemView.setHasTransientState(true); + ViewCompat.setHasTransientState(holder.itemView, true); } @Override public void onAnimationEnd(Animator animation) { - holder.itemView.setHasTransientState(false); + ViewCompat.setHasTransientState(holder.itemView, false); dispatchChangeFinished(holder, false); } }); diff --git a/app/src/main/java/io/plaidapp/ui/DribbbleLogin.java b/app/src/main/java/io/plaidapp/ui/DribbbleLogin.java index b1f568c1c..af59dd82d 100644 --- a/app/src/main/java/io/plaidapp/ui/DribbbleLogin.java +++ b/app/src/main/java/io/plaidapp/ui/DribbbleLogin.java @@ -17,17 +17,19 @@ package io.plaidapp.ui; import android.animation.ObjectAnimator; +import android.annotation.TargetApi; import android.app.Activity; import android.app.SharedElementCallback; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; +import android.os.Build; import android.os.Bundle; import android.os.Parcelable; +import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.text.TextUtils; -import android.transition.TransitionManager; import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; @@ -55,6 +57,8 @@ import io.plaidapp.data.prefs.DribbblePrefs; import io.plaidapp.ui.transitions.FabDialogMorphSetup; import io.plaidapp.util.ScrimUtil; +import io.plaidapp.util.ViewUtils; +import io.plaidapp.util.compat.TransitionManagerCompat; import io.plaidapp.util.glide.CircleTransform; import retrofit.Callback; import retrofit.RestAdapter; @@ -102,7 +106,7 @@ public void doLogin(View view) { public void dismiss(View view) { isDismissing = true; setResult(Activity.RESULT_CANCELED); - finishAfterTransition(); + ActivityCompat.finishAfterTransition(this); } @Override @@ -111,14 +115,14 @@ public void onBackPressed() { } private void showLoading() { - TransitionManager.beginDelayedTransition(container); + TransitionManagerCompat.beginDelayedTransition(container); message.setVisibility(View.GONE); login.setVisibility(View.GONE); loading.setVisibility(View.VISIBLE); } private void showLogin() { - TransitionManager.beginDelayedTransition(container); + TransitionManagerCompat.beginDelayedTransition(container); message.setVisibility(View.VISIBLE); login.setVisibility(View.VISIBLE); loading.setVisibility(View.GONE); @@ -149,7 +153,7 @@ public void success(AccessToken accessToken, Response response) { dribbblePrefs.setAccessToken(accessToken.access_token); showLoggedInUser(); setResult(Activity.RESULT_OK); - finishAfterTransition(); + ActivityCompat.finishAfterTransition(DribbbleLogin.this); } @Override @@ -190,7 +194,7 @@ public void success(User user, Response response) { .placeholder(R.drawable.ic_player) .transform(new CircleTransform(getApplicationContext())) .into((ImageView) v.findViewById(R.id.avatar)); - v.findViewById(R.id.scrim).setBackground(ScrimUtil.makeCubicGradientScrimDrawable + ViewUtils.setBackground(v.findViewById(R.id.scrim), ScrimUtil.makeCubicGradientScrimDrawable (ContextCompat.getColor(DribbbleLogin.this, R.color.scrim), 5, Gravity.BOTTOM)); confirmLogin.setView(v); @@ -215,56 +219,60 @@ private void forceSharedElementLayout() { .getBottom()); } - private SharedElementCallback sharedElementEnterCallback = new SharedElementCallback() { - @Override - public View onCreateSnapshotView(Context context, Parcelable snapshot) { - // grab the saved fab snapshot and pass it to the below via a View - View view = new View(context); - final Bitmap snapshotBitmap = getSnapshot(snapshot); - if (snapshotBitmap != null) { - view.setBackground(new BitmapDrawable(context.getResources(), snapshotBitmap)); + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + private SharedElementCallback sharedElementEnterCallback() { + return new SharedElementCallback() { + @Override + public View onCreateSnapshotView(Context context, Parcelable snapshot) { + // grab the saved fab snapshot and pass it to the below via a View + View view = new View(context); + final Bitmap snapshotBitmap = getSnapshot(snapshot); + if (snapshotBitmap != null) { + view.setBackground(new BitmapDrawable(context.getResources(), snapshotBitmap)); + } + return view; } - return view; - } - @Override - public void onSharedElementStart(List sharedElementNames, - List sharedElements, - List sharedElementSnapshots) { - // grab the fab snapshot and fade it out/in (depending on if we are entering or exiting) - for (int i = 0; i < sharedElements.size(); i++) { - if (sharedElements.get(i) == container) { - View snapshot = sharedElementSnapshots.get(i); - BitmapDrawable fabSnapshot = (BitmapDrawable) snapshot.getBackground(); - fabSnapshot.setBounds(0, 0, snapshot.getWidth(), snapshot.getHeight()); - container.getOverlay().clear(); - container.getOverlay().add(fabSnapshot); - if (!isDismissing) { - // fab -> login: fade out the fab snapshot - ObjectAnimator.ofInt(fabSnapshot, "alpha", 0).setDuration(100).start(); - } else { - // login -> fab: fade in the fab snapshot toward the end of the transition - fabSnapshot.setAlpha(0); - ObjectAnimator fadeIn = ObjectAnimator.ofInt(fabSnapshot, "alpha", 255) - .setDuration(150); - fadeIn.setStartDelay(150); - fadeIn.start(); + @Override + public void onSharedElementStart( + List sharedElementNames, + List sharedElements, + List sharedElementSnapshots) { + // grab the fab snapshot and fade it out/in (depending on if we are entering or exiting) + for (int i = 0; i < sharedElements.size(); i++) { + if (sharedElements.get(i) == container) { + View snapshot = sharedElementSnapshots.get(i); + BitmapDrawable fabSnapshot = (BitmapDrawable) snapshot.getBackground(); + fabSnapshot.setBounds(0, 0, snapshot.getWidth(), snapshot.getHeight()); + container.getOverlay().clear(); + container.getOverlay().add(fabSnapshot); + if (!isDismissing) { + // fab -> login: fade out the fab snapshot + ObjectAnimator.ofInt(fabSnapshot, "alpha", 0).setDuration(100).start(); + } else { + // login -> fab: fade in the fab snapshot toward the end of the transition + fabSnapshot.setAlpha(0); + ObjectAnimator fadeIn = ObjectAnimator.ofInt(fabSnapshot, "alpha", 255) + .setDuration(150); + fadeIn.setStartDelay(150); + fadeIn.start(); + } + forceSharedElementLayout(); + break; } - forceSharedElementLayout(); - break; } } - } - private Bitmap getSnapshot(Parcelable parcel) { - if (parcel instanceof Bitmap) { - return (Bitmap) parcel; - } else if (parcel instanceof Bundle) { - Bundle bundle = (Bundle) parcel; - // see SharedElementCallback#onCaptureSharedElementSnapshot - return (Bitmap) bundle.getParcelable("sharedElement:snapshot:bitmap"); + private Bitmap getSnapshot(Parcelable parcel) { + if (parcel instanceof Bitmap) { + return (Bitmap) parcel; + } else if (parcel instanceof Bundle) { + Bundle bundle = (Bundle) parcel; + // see SharedElementCallback#onCaptureSharedElementSnapshot + return (Bitmap) bundle.getParcelable("sharedElement:snapshot:bitmap"); + } + return null; } - return null; - } - }; + }; + } } diff --git a/app/src/main/java/io/plaidapp/ui/DribbbleShot.java b/app/src/main/java/io/plaidapp/ui/DribbbleShot.java index 976b59020..a9160dcdd 100644 --- a/app/src/main/java/io/plaidapp/ui/DribbbleShot.java +++ b/app/src/main/java/io/plaidapp/ui/DribbbleShot.java @@ -23,7 +23,6 @@ import android.animation.ValueAnimator; import android.annotation.TargetApi; import android.app.Activity; -import android.app.ActivityOptions; import android.app.SharedElementCallback; import android.app.assist.AssistContent; import android.content.Context; @@ -39,16 +38,18 @@ import android.os.Bundle; import android.os.Parcelable; import android.support.customtabs.CustomTabsIntent; +import android.support.v4.app.ActivityCompat; +import android.support.v4.app.ActivityOptionsCompat; import android.support.v4.content.ContextCompat; +import android.support.v4.util.Pair; +import android.support.v4.view.ViewCompat; import android.support.v7.graphics.Palette; import android.text.Spanned; import android.text.TextUtils; import android.text.format.DateUtils; import android.transition.AutoTransition; import android.transition.Transition; -import android.transition.TransitionManager; import android.util.Log; -import android.util.Pair; import android.util.TypedValue; import android.view.LayoutInflater; import android.view.View; @@ -103,6 +104,7 @@ import io.plaidapp.util.HtmlUtils; import io.plaidapp.util.ImeUtils; import io.plaidapp.util.ViewUtils; +import io.plaidapp.util.compat.TransitionManagerCompat; import io.plaidapp.util.customtabs.CustomTabActivityHelper; import io.plaidapp.util.glide.CircleTransform; import io.plaidapp.util.glide.GlideUtils; @@ -111,6 +113,7 @@ import retrofit.client.Response; import retrofit.converter.GsonConverter; +import static io.plaidapp.util.AnimUtils.getFastOutLinearInInterpolator; import static io.plaidapp.util.AnimUtils.getFastOutSlowInInterpolator; import static io.plaidapp.util.AnimUtils.getLinearOutSlowInInterpolator; @@ -158,8 +161,10 @@ protected void onCreate(final Bundle savedInstanceState) { setContentView(R.layout.activity_dribbble_shot); shot = getIntent().getParcelableExtra(EXTRA_SHOT); setupDribbble(); - setExitSharedElementCallback(fabLoginSharedElementCallback); - getWindow().getSharedElementReturnTransition().addListener(shotReturnHomeListener); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + setExitSharedElementCallback(fabLoginSharedElementCallback()); + getWindow().getSharedElementReturnTransition().addListener(shotReturnHomeListener); + } circleTransform = new CircleTransform(this); Resources res = getResources(); @@ -206,7 +211,7 @@ public void onDragDismissed() { imageView.setOnClickListener(shotClick); shotSpacer.setOnClickListener(shotClick); - postponeEnterTransition(); + ActivityCompat.postponeEnterTransition(this); imageView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver .OnPreDrawListener() { @Override @@ -214,7 +219,7 @@ public boolean onPreDraw() { imageView.getViewTreeObserver().removeOnPreDrawListener(this); calculateFabPosition(); enterAnimation(savedInstanceState != null); - startPostponedEnterTransition(); + ActivityCompat.startPostponedEnterTransition(DribbbleShot.this); return true; } }); @@ -251,7 +256,7 @@ public void onClick(View v) { } }); if (shot.likes_count == 0) { - likeCount.setBackground(null); // clear touch ripple if doesn't do anything + ViewUtils.setBackground(likeCount, null); // clear touch ripple if doesn't do anything } viewCount.setText( res.getQuantityString(R.plurals.views, @@ -290,13 +295,13 @@ public void onClick(View v) { player.putExtra(PlayerActivity.EXTRA_PLAYER_NAME, shot.user.username); player.putExtra(PlayerActivity.EXTRA_PLAYER_ID, shot.user.id); } - ActivityOptions options = - ActivityOptions.makeSceneTransitionAnimation(DribbbleShot.this, + ActivityOptionsCompat options = + ActivityOptionsCompat.makeSceneTransitionAnimation(DribbbleShot.this, Pair.create((View) playerAvatar, getString(R.string.transition_player_avatar)), Pair.create((View) playerName, getString(R.string.transition_player_name))); - startActivity(player, options.toBundle()); + ActivityCompat.startActivity(DribbbleShot.this, player, options.toBundle()); } }; playerAvatar.setOnClickListener(playerClick); @@ -417,9 +422,10 @@ private void openLink(String url) { private RequestListener shotLoadListener = new RequestListener() { @Override - public boolean onResourceReady(GlideDrawable resource, String model, - Target target, boolean isFromMemoryCache, - boolean isFirstResource) { + public boolean onResourceReady( + GlideDrawable resource, String model, + Target target, boolean isFromMemoryCache, + boolean isFirstResource) { final Bitmap bitmap = GlideUtils.getBitmap(resource); final int twentyFourDip = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 24, DribbbleShot.this.getResources().getDisplayMetrics()); @@ -445,37 +451,39 @@ public void onGenerated(Palette palette) { DribbbleShot.this, R.color.dark_icon)); } - // color the status bar. Set a complementary dark color on L, - // light or dark color on M (with matching status bar icons) - int statusBarColor = getWindow().getStatusBarColor(); - final Palette.Swatch topColor = - ColorUtils.getMostPopulousSwatch(palette); - if (topColor != null && - (isDark || Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)) { - statusBarColor = ColorUtils.scrimify(topColor.getRgb(), - isDark, SCRIM_ADJUSTMENT); - // set a light status bar on M+ - if (!isDark && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - ViewUtils.setLightStatusBar(imageView); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + // color the status bar. Set a complementary dark color on L, + // light or dark color on M (with matching status bar icons) + int statusBarColor = getWindow().getStatusBarColor(); + final Palette.Swatch topColor = + ColorUtils.getMostPopulousSwatch(palette); + if (topColor != null && + (isDark || Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)) { + statusBarColor = ColorUtils.scrimify(topColor.getRgb(), + isDark, SCRIM_ADJUSTMENT); + // set a light status bar on M+ + if (!isDark && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + ViewUtils.setLightStatusBar(imageView); + } } - } - if (statusBarColor != getWindow().getStatusBarColor()) { - imageView.setScrimColor(statusBarColor); - ValueAnimator statusBarColorAnim = ValueAnimator.ofArgb( - getWindow().getStatusBarColor(), statusBarColor); - statusBarColorAnim.addUpdateListener(new ValueAnimator - .AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - getWindow().setStatusBarColor( - (int) animation.getAnimatedValue()); - } - }); - statusBarColorAnim.setDuration(1000L); - statusBarColorAnim.setInterpolator( - getFastOutSlowInInterpolator(DribbbleShot.this)); - statusBarColorAnim.start(); + if (statusBarColor != getWindow().getStatusBarColor()) { + imageView.setScrimColor(statusBarColor); + ValueAnimator statusBarColorAnim = ValueAnimator.ofArgb( + getWindow().getStatusBarColor(), statusBarColor); + statusBarColorAnim.addUpdateListener(new ValueAnimator + .AnimatorUpdateListener() { + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + @Override + public void onAnimationUpdate(ValueAnimator animation) { + getWindow().setStatusBarColor( + (int) animation.getAnimatedValue()); + } + }); + statusBarColorAnim.setDuration(1000L); + statusBarColorAnim.setInterpolator(getFastOutSlowInInterpolator()); + statusBarColorAnim.start(); + } } } }); @@ -486,7 +494,7 @@ public void onAnimationUpdate(ValueAnimator animation) { @Override public void onGenerated(Palette palette) { // color the ripple on the image spacer (default is grey) - shotSpacer.setBackground(ViewUtils.createRipple(palette, 0.25f, 0.5f, + ViewUtils.setBackground(shotSpacer, ViewUtils.createRipple(palette, 0.25f, 0.5f, ContextCompat.getColor(DribbbleShot.this, R.color.mid_grey), true)); // slightly more opaque ripple on the pinned image to compensate @@ -498,13 +506,14 @@ public void onGenerated(Palette palette) { }); // TODO should keep the background if the image contains transparency?! - imageView.setBackground(null); + ViewUtils.setBackground(imageView, null); return false; } @Override - public boolean onException(Exception e, String model, Target target, - boolean isFirstResource) { + public boolean onException( + Exception e, String model, Target target, + boolean isFirstResource) { return false; } }; @@ -550,51 +559,54 @@ public void onClick(View view) { final Intent login = new Intent(DribbbleShot.this, DribbbleLogin.class); login.putExtra(FabDialogMorphSetup.EXTRA_SHARED_ELEMENT_START_COLOR, ContextCompat.getColor(DribbbleShot.this, R.color.dribbble)); - ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation + ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation (DribbbleShot.this, fab, getString(R.string.transition_dribbble_login)); - startActivityForResult(login, RC_LOGIN_LIKE, options.toBundle()); + ActivityCompat.startActivityForResult(DribbbleShot.this, login, RC_LOGIN_LIKE, options.toBundle()); } } }; - private SharedElementCallback fabLoginSharedElementCallback = new SharedElementCallback() { - @Override - public Parcelable onCaptureSharedElementSnapshot(View sharedElement, - Matrix viewToGlobalMatrix, - RectF screenBounds) { - // store a snapshot of the fab to fade out when morphing to the login dialog - int bitmapWidth = Math.round(screenBounds.width()); - int bitmapHeight = Math.round(screenBounds.height()); - Bitmap bitmap = null; - if (bitmapWidth > 0 && bitmapHeight > 0) { - bitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888); - sharedElement.draw(new Canvas(bitmap)); + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + private SharedElementCallback fabLoginSharedElementCallback() { + return new SharedElementCallback() { + @Override + public Parcelable onCaptureSharedElementSnapshot(View sharedElement, + Matrix viewToGlobalMatrix, + RectF screenBounds) { + // store a snapshot of the fab to fade out when morphing to the login dialog + int bitmapWidth = Math.round(screenBounds.width()); + int bitmapHeight = Math.round(screenBounds.height()); + Bitmap bitmap = null; + if (bitmapWidth > 0 && bitmapHeight > 0) { + bitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888); + sharedElement.draw(new Canvas(bitmap)); + } + return bitmap; } - return bitmap; - } - }; + }; + } - private Transition.TransitionListener shotReturnHomeListener = new AnimUtils - .TransitionListenerAdapter() { - @Override - public void onTransitionStart(Transition transition) { - super.onTransitionStart(transition); - // hide the fab as for some reason it jumps position?? TODO work out why - fab.setVisibility(View.INVISIBLE); - // fade out the "toolbar" & list as we don't want them to be visible during return - // animation - back.animate() - .alpha(0f) - .setDuration(100) - .setInterpolator(getLinearOutSlowInInterpolator(DribbbleShot.this)); - imageView.setElevation(1f); - back.setElevation(0f); - commentsList.animate() - .alpha(0f) - .setDuration(50) - .setInterpolator(getLinearOutSlowInInterpolator(DribbbleShot.this)); - } - }; + private Transition.TransitionListener shotReturnHomeListener = new AnimUtils + .TransitionListenerAdapter() { + @Override + public void onTransitionStart(Transition transition) { + super.onTransitionStart(transition); + // hide the fab as for some reason it jumps position?? TODO work out why + fab.setVisibility(View.INVISIBLE); + // fade out the "toolbar" & list as we don't want them to be visible during return + // animation + back.animate() + .alpha(0f) + .setDuration(100) + .setInterpolator(getLinearOutSlowInInterpolator()); + ViewCompat.setElevation(imageView, 1f); + ViewCompat.setElevation(back, 0f); + commentsList.animate() + .alpha(0f) + .setDuration(50) + .setInterpolator(getLinearOutSlowInInterpolator()); + } + }; private void loadComments() { commentsList.setAdapter(getLoadingCommentsAdapter()); @@ -608,7 +620,7 @@ public void success(List comments, Response response) { commentsAdapter = new DribbbleCommentsAdapter(DribbbleShot.this, R.layout .dribbble_comment, comments); commentsList.setAdapter(commentsAdapter); - commentsList.setDivider(getDrawable(R.drawable.list_divider)); + commentsList.setDivider(ContextCompat.getDrawable(DribbbleShot.this, R.drawable.list_divider)); commentsList.setDividerHeight(getResources().getDimensionPixelSize(R.dimen .divider_height)); } @@ -625,16 +637,16 @@ private void expandImageAndFinish() { Animator expandImage = ObjectAnimator.ofFloat(imageView, ParallaxScrimageView.OFFSET, 0f); expandImage.setDuration(80); - expandImage.setInterpolator(getFastOutSlowInInterpolator(this)); + expandImage.setInterpolator(getFastOutSlowInInterpolator()); expandImage.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - finishAfterTransition(); + ActivityCompat.finishAfterTransition(DribbbleShot.this); } }); expandImage.start(); } else { - finishAfterTransition(); + ActivityCompat.finishAfterTransition(this); } } @@ -658,7 +670,7 @@ private void calculateFabPosition() { fab.setOffset(fabOffset); // calculate min position i.e. pinned to the collapsed image when scrolled - fab.setMinOffset(imageView.getMinimumHeight() - (fab.getHeight() / 2)); + fab.setMinOffset(ViewCompat.getMinimumHeight(imageView) - (fab.getHeight() / 2)); } /** @@ -667,7 +679,7 @@ private void calculateFabPosition() { * plays nicely with #calculateFabPosition */ private void enterAnimation(boolean isOrientationChange) { - Interpolator interp = getFastOutSlowInInterpolator(this); + Interpolator interp = getFastOutSlowInInterpolator(); int offset = title.getHeight(); viewEnterAnimation(title, offset, interp); if (description.getVisibility() == View.VISIBLE) { @@ -704,7 +716,7 @@ private void enterAnimation(boolean isOrientationChange) { PropertyValuesHolder.ofFloat(View.SCALE_Y, 0f, 1f)); showFab.setStartDelay(300L); showFab.setDuration(300L); - showFab.setInterpolator(getLinearOutSlowInInterpolator(this)); + showFab.setInterpolator(getLinearOutSlowInInterpolator()); showFab.start(); } } @@ -792,10 +804,10 @@ public void failure(RetrofitError error) { Intent login = new Intent(DribbbleShot.this, DribbbleLogin.class); login.putExtra(FabDialogMorphSetup.EXTRA_SHARED_ELEMENT_START_COLOR, ContextCompat.getColor (this, R.color.background_light)); - ActivityOptions options = - ActivityOptions.makeSceneTransitionAnimation(DribbbleShot.this, postComment, + ActivityOptionsCompat options = + ActivityOptionsCompat.makeSceneTransitionAnimation(DribbbleShot.this, postComment, getString(R.string.transition_dribbble_login)); - startActivityForResult(login, RC_LOGIN_COMMENT, options.toBundle()); + ActivityCompat.startActivityForResult(this, login, RC_LOGIN_COMMENT, options.toBundle()); } } @@ -827,8 +839,7 @@ public long getItemId(int position) { @Override public View getView(int position, View convertView, ViewGroup parent) { - return DribbbleShot.this.getLayoutInflater().inflate(R.layout.loading, parent, - false); + return DribbbleShot.this.getLayoutInflater().inflate(R.layout.loading, parent, false); } }; } @@ -836,15 +847,23 @@ public View getView(int position, View convertView, ViewGroup parent) { protected class DribbbleCommentsAdapter extends ArrayAdapter { private final LayoutInflater inflater; - private final Transition change; + private Object change; private int expandedCommentPosition = ListView.INVALID_POSITION; public DribbbleCommentsAdapter(Context context, int resource, List comments) { super(context, resource, comments); inflater = LayoutInflater.from(context); - change = new AutoTransition(); - change.setDuration(200L); - change.setInterpolator(getFastOutSlowInInterpolator(context)); + } + + @TargetApi(Build.VERSION_CODES.KITKAT) + private Transition getChange() { + if (change == null) { + Transition transition = new AutoTransition(); + transition.setDuration(200L); + transition.setInterpolator(getFastOutLinearInInterpolator()); + change = transition; + } + return (Transition) change; } @Override @@ -889,13 +908,13 @@ private void bindComment(final Comment comment, final int position, final View v public void onClick(View v) { Intent player = new Intent(DribbbleShot.this, PlayerActivity.class); player.putExtra(PlayerActivity.EXTRA_PLAYER, comment.user); - ActivityOptions options = - ActivityOptions.makeSceneTransitionAnimation(DribbbleShot.this, + ActivityOptionsCompat options = + ActivityOptionsCompat.makeSceneTransitionAnimation(DribbbleShot.this, Pair.create((View) avatar, getString(R.string.transition_player_avatar)), Pair.create((View) author, getString(R.string.transition_player_name))); - startActivity(player, options.toBundle()); + ActivityCompat.startActivity(DribbbleShot.this, player, options.toBundle()); } }); author.setText(comment.user.name); @@ -911,7 +930,7 @@ public void onClick(View v) { @Override public void onClick(View v) { final boolean isExpanded = reply.getVisibility() == View.VISIBLE; - TransitionManager.beginDelayedTransition(commentsList, change); + TransitionManagerCompat.beginDelayedTransition(commentsList, getChange()); view.setActivated(!isExpanded); if (!isExpanded) { // do expand expandedCommentPosition = position; diff --git a/app/src/main/java/io/plaidapp/ui/FeedAdapter.java b/app/src/main/java/io/plaidapp/ui/FeedAdapter.java index b4c99c089..803fa4dba 100644 --- a/app/src/main/java/io/plaidapp/ui/FeedAdapter.java +++ b/app/src/main/java/io/plaidapp/ui/FeedAdapter.java @@ -20,8 +20,8 @@ import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; +import android.annotation.SuppressLint; import android.app.Activity; -import android.app.ActivityOptions; import android.content.Intent; import android.content.res.TypedArray; import android.graphics.Color; @@ -31,14 +31,18 @@ import android.graphics.drawable.TransitionDrawable; import android.net.Uri; import android.support.annotation.ColorInt; +import android.os.Build; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.customtabs.CustomTabsIntent; +import android.support.v4.app.ActivityCompat; +import android.support.v4.app.ActivityOptionsCompat; import android.support.v4.content.ContextCompat; +import android.support.v4.util.Pair; +import android.support.v4.view.ViewCompat; import android.support.v7.widget.RecyclerView; import android.transition.Transition; import android.transition.TransitionInflater; -import android.util.Pair; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; @@ -77,6 +81,7 @@ import io.plaidapp.util.AnimUtils; import io.plaidapp.util.ObservableColorMatrix; import io.plaidapp.util.ViewUtils; +import io.plaidapp.util.compat.ImageViewCompat; import io.plaidapp.util.customtabs.CustomTabActivityHelper; import io.plaidapp.util.glide.DribbbleTarget; @@ -213,17 +218,17 @@ public void onClick(View commentsView) { intent.putExtra(DesignerNewsStory.EXTRA_STORY, (Story) getItem(holder.getAdapterPosition())); setGridItemContentTransitions(holder.itemView); - final ActivityOptions options = - ActivityOptions.makeSceneTransitionAnimation(host, + final ActivityOptionsCompat options = + ActivityOptionsCompat.makeSceneTransitionAnimation(host, Pair.create(holder.itemView, host.getString(R.string.transition_story_title_background)), Pair.create(holder.itemView, host.getString(R.string.transition_story_background))); - host.startActivity(intent, options.toBundle()); + ActivityCompat.startActivity(host, intent, options.toBundle()); } }); if (pocketIsInstalled) { - holder.pocket.setImageAlpha(178); // grumble... no xml setter, grumble... + ImageViewCompat.setImageAlpha(holder.pocket, 178); // grumble... no xml setter, grumble... holder.pocket.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View view) { @@ -251,7 +256,7 @@ private DribbbleShotHolder createDribbbleShotHolder(ViewGroup parent) { holder.image.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { - holder.itemView.setTransitionName(holder.itemView.getResources().getString(R + ViewCompat.setTransitionName(holder.itemView, holder.itemView.getResources().getString(R .string.transition_shot)); holder.itemView.setBackgroundColor( ContextCompat.getColor(host, R.color.background_light)); @@ -260,12 +265,12 @@ public void onClick(View view) { intent.putExtra(DribbbleShot.EXTRA_SHOT, (Shot) getItem(holder.getAdapterPosition())); setGridItemContentTransitions(holder.itemView); - ActivityOptions options = - ActivityOptions.makeSceneTransitionAnimation(host, + ActivityOptionsCompat options = + ActivityOptionsCompat.makeSceneTransitionAnimation(host, Pair.create(view, host.getString(R.string.transition_shot)), Pair.create(view, host.getString(R.string .transition_shot_background))); - host.startActivity(intent, options.toBundle()); + ActivityCompat.startActivity(host, intent, options.toBundle()); } }); // play animated GIFs whilst touched @@ -325,7 +330,7 @@ public boolean onResourceReady(GlideDrawable resource, boolean isFromMemoryCache, boolean isFirstResource) { if (!shot.hasFadedIn) { - holder.image.setHasTransientState(true); + ViewCompat.setHasTransientState(holder.image, true); final ObservableColorMatrix cm = new ObservableColorMatrix(); ObjectAnimator saturation = ObjectAnimator.ofFloat(cm, ObservableColorMatrix.SATURATION, 0f, 1f); @@ -343,11 +348,11 @@ public void onAnimationUpdate(ValueAnimator valueAnimator) { } }); saturation.setDuration(2000); - saturation.setInterpolator(getFastOutSlowInInterpolator(host)); + saturation.setInterpolator(getFastOutSlowInInterpolator()); saturation.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - holder.image.setHasTransientState(false); + ViewCompat.setHasTransientState(holder.image, false); } }); saturation.start(); @@ -590,8 +595,12 @@ public int getItemCount() { * This can cause a strange layers-passing-through-each-other effect, especially on return. * In this situation, hide the FAB on exit and re-show it on return. */ + @SuppressLint("NewApi") private void setGridItemContentTransitions(View gridItem) { - if (!ViewUtils.viewsIntersect(gridItem, host.findViewById(R.id.fab))) return; + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP + || !ViewUtils.viewsIntersect(gridItem, host.findViewById(R.id.fab))) { + return; + } final TransitionInflater ti = TransitionInflater.from(host); host.getWindow().setExitTransition( diff --git a/app/src/main/java/io/plaidapp/ui/FilterAdapter.java b/app/src/main/java/io/plaidapp/ui/FilterAdapter.java index 11d67c7ca..58cb152d8 100644 --- a/app/src/main/java/io/plaidapp/ui/FilterAdapter.java +++ b/app/src/main/java/io/plaidapp/ui/FilterAdapter.java @@ -23,6 +23,7 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.content.ContextCompat; +import android.support.v4.view.ViewCompat; import android.support.v7.widget.DefaultItemAnimator; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; @@ -44,6 +45,8 @@ import io.plaidapp.util.AnimUtils; import io.plaidapp.util.ColorUtils; import io.plaidapp.util.ViewUtils; +import io.plaidapp.util.compat.ImageViewCompat; +import io.plaidapp.util.compat.ObjectAnimatorCompat; /** * Adapter for showing the list of data sources used as filters for the home grid. @@ -193,9 +196,9 @@ public void onBindViewHolder(final FilterViewHolder holder, int position) { holder.filterName.setEnabled(filter.active); if (filter.iconRes > 0) { holder.filterIcon.setImageDrawable( - holder.itemView.getContext().getDrawable(filter.iconRes)); + ContextCompat.getDrawable(holder.itemView.getContext(), filter.iconRes)); } - holder.filterIcon.setImageAlpha(filter.active ? FILTER_ICON_ENABLED_ALPHA : + ImageViewCompat.setImageAlpha(holder.filterIcon, filter.active ? FILTER_ICON_ENABLED_ALPHA : FILTER_ICON_DISABLED_ALPHA); } @@ -323,10 +326,11 @@ public ItemHolderInfo obtainHolderInfo() { @NonNull @Override - public ItemHolderInfo recordPreLayoutInformation(RecyclerView.State state, - RecyclerView.ViewHolder viewHolder, - int changeFlags, - List payloads) { + public ItemHolderInfo recordPreLayoutInformation( + @NonNull RecyclerView.State state, + @NonNull RecyclerView.ViewHolder viewHolder, + int changeFlags, + @NonNull List payloads) { FilterHolderInfo info = (FilterHolderInfo) super.recordPreLayoutInformation(state, viewHolder, changeFlags, payloads); if (!payloads.isEmpty()) { @@ -338,32 +342,32 @@ public ItemHolderInfo recordPreLayoutInformation(RecyclerView.State state, } @Override - public boolean animateChange(RecyclerView.ViewHolder oldHolder, - RecyclerView.ViewHolder newHolder, - ItemHolderInfo preInfo, - ItemHolderInfo postInfo) { + public boolean animateChange( + @NonNull RecyclerView.ViewHolder oldHolder, + @NonNull RecyclerView.ViewHolder newHolder, + @NonNull ItemHolderInfo preInfo, + @NonNull ItemHolderInfo postInfo) { if (newHolder instanceof FilterViewHolder && preInfo instanceof FilterHolderInfo) { final FilterViewHolder holder = (FilterViewHolder) newHolder; final FilterHolderInfo info = (FilterHolderInfo) preInfo; if (info.doEnable || info.doDisable) { ObjectAnimator iconAlpha = ObjectAnimator.ofInt(holder.filterIcon, - ViewUtils.IMAGE_ALPHA, - info.doEnable ? FILTER_ICON_ENABLED_ALPHA : - FILTER_ICON_DISABLED_ALPHA); + ViewUtils.IMAGE_ALPHA, + info.doEnable ? FILTER_ICON_ENABLED_ALPHA : + FILTER_ICON_DISABLED_ALPHA); iconAlpha.setDuration(300L); - iconAlpha.setInterpolator(AnimUtils.getFastOutSlowInInterpolator(holder - .itemView.getContext())); + iconAlpha.setInterpolator(AnimUtils.getFastOutSlowInInterpolator()); iconAlpha.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { dispatchChangeStarting(holder, false); - holder.itemView.setHasTransientState(true); + ViewCompat.setHasTransientState(holder.itemView, true); } @Override public void onAnimationEnd(Animator animation) { - holder.itemView.setHasTransientState(false); + ViewCompat.setHasTransientState(holder.itemView, false); dispatchChangeFinished(holder, false); } }); @@ -372,7 +376,7 @@ public void onAnimationEnd(Animator animation) { int highlightColor = ContextCompat.getColor(holder.itemView.getContext(), R.color.accent); int fadeFromTo = ColorUtils.modifyAlpha(highlightColor, 0); - ObjectAnimator highlightBackground = ObjectAnimator.ofArgb( + ObjectAnimator highlightBackground = ObjectAnimatorCompat.ofArgb( holder.itemView, ViewUtils.BACKGROUND_COLOR, fadeFromTo, @@ -384,13 +388,13 @@ public void onAnimationEnd(Animator animation) { @Override public void onAnimationStart(Animator animation) { dispatchChangeStarting(holder, false); - holder.itemView.setHasTransientState(true); + ViewCompat.setHasTransientState(holder.itemView, true); } @Override public void onAnimationEnd(Animator animation) { - holder.itemView.setBackground(null); - holder.itemView.setHasTransientState(false); + ViewUtils.setBackground(holder.itemView, null); + ViewCompat.setHasTransientState(holder.itemView, false); dispatchChangeFinished(holder, false); } }); diff --git a/app/src/main/java/io/plaidapp/ui/HomeActivity.java b/app/src/main/java/io/plaidapp/ui/HomeActivity.java index 0e595d1d2..1f95e3da9 100644 --- a/app/src/main/java/io/plaidapp/ui/HomeActivity.java +++ b/app/src/main/java/io/plaidapp/ui/HomeActivity.java @@ -18,9 +18,7 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; -import android.app.Activity; import android.app.ActivityManager; -import android.app.ActivityOptions; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -31,13 +29,22 @@ import android.graphics.drawable.AnimatedVectorDrawable; import android.net.ConnectivityManager; import android.net.NetworkInfo; +import android.os.Build; import android.os.Bundle; +import android.support.v4.app.ActivityCompat; +import android.support.v4.app.ActivityOptionsCompat; import android.support.v4.content.ContextCompat; import android.support.v4.content.LocalBroadcastManager; import android.support.v4.view.GravityCompat; +import android.support.v4.view.OnApplyWindowInsetsListener; +import android.support.v4.view.ViewCompat; +import android.support.v4.view.WindowInsetsCompat; import android.support.v4.widget.DrawerLayout; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.ActionMenuView; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.Toolbar; import android.support.v7.widget.helper.ItemTouchHelper; import android.text.Spannable; import android.text.SpannableStringBuilder; @@ -53,16 +60,13 @@ import android.view.ViewAnimationUtils; import android.view.ViewGroup; import android.view.ViewStub; -import android.view.WindowInsets; import android.view.animation.AnimationUtils; -import android.widget.ActionMenuView; import android.widget.FrameLayout; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; -import android.widget.Toolbar; import java.security.InvalidParameterException; import java.util.ArrayList; @@ -92,7 +96,7 @@ import io.plaidapp.util.ViewUtils; -public class HomeActivity extends Activity { +public class HomeActivity extends AppCompatActivity { private static final int RC_SEARCH = 0; private static final int RC_AUTH_DRIBBBLE_FOLLOWING = 1; @@ -101,6 +105,15 @@ public class HomeActivity extends Activity { private static final int RC_NEW_DESIGNER_NEWS_STORY = 4; private static final int RC_NEW_DESIGNER_NEWS_LOGIN = 5; + private static final OnApplyWindowInsetsListener NOOP_WINDOW_INSETS_LISTENER = new OnApplyWindowInsetsListener() { + @Override + public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) { + // We have to noop this because the support implementation assumes the input listener is't null + // https://code.google.com/p/android/issues/detail?id=197492 + return insets.consumeSystemWindowInsets(); + } + }; + @Bind(R.id.drawer) DrawerLayout drawer; @Bind(R.id.toolbar) Toolbar toolbar; @Bind(R.id.stories_grid) RecyclerView grid; @@ -125,12 +138,14 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.activity_home); ButterKnife.bind(this); - drawer.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE - | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + drawer.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); + } //toolbar.inflateMenu(R.menu.main); - setActionBar(toolbar); + setSupportActionBar(toolbar); if (savedInstanceState == null) { animateToolbar(); } @@ -144,10 +159,10 @@ public void requestDribbbleAuthorisation(View sharedElemeent, Source forSource) Intent login = new Intent(HomeActivity.this, DribbbleLogin.class); login.putExtra(FabDialogMorphSetup.EXTRA_SHARED_ELEMENT_START_COLOR, ContextCompat.getColor(HomeActivity.this, R.color.background_dark)); - ActivityOptions options = - ActivityOptions.makeSceneTransitionAnimation(HomeActivity.this, + ActivityOptionsCompat options = + ActivityOptionsCompat.makeSceneTransitionAnimation(HomeActivity.this, sharedElemeent, getString(R.string.transition_dribbble_login)); - startActivityForResult(login, + ActivityCompat.startActivityForResult(HomeActivity.this, login, getAuthSourceRequestCode(forSource), options.toBundle()); } }); @@ -181,26 +196,23 @@ public void onLoadMore() { grid.setItemAnimator(new HomeGridItemAnimator()); // drawer layout treats fitsSystemWindows specially so we have to handle insets ourselves - drawer.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() { + ViewCompat.setOnApplyWindowInsetsListener(drawer, new OnApplyWindowInsetsListener() { @Override - public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) { + public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) { // inset the toolbar down by the status bar height - ViewGroup.MarginLayoutParams lpToolbar = (ViewGroup.MarginLayoutParams) toolbar - .getLayoutParams(); + ViewGroup.MarginLayoutParams lpToolbar = (ViewGroup.MarginLayoutParams) toolbar.getLayoutParams(); lpToolbar.topMargin += insets.getSystemWindowInsetTop(); lpToolbar.rightMargin += insets.getSystemWindowInsetRight(); toolbar.setLayoutParams(lpToolbar); // inset the grid top by statusbar+toolbar & the bottom by the navbar (don't clip) grid.setPadding(grid.getPaddingLeft(), - insets.getSystemWindowInsetTop() + ViewUtils.getActionBarSize - (HomeActivity.this), + insets.getSystemWindowInsetTop() + ViewUtils.getActionBarSize(HomeActivity.this), grid.getPaddingRight() + insets.getSystemWindowInsetRight(), // landscape grid.getPaddingBottom() + insets.getSystemWindowInsetBottom()); // inset the fab for the navbar - ViewGroup.MarginLayoutParams lpFab = (ViewGroup.MarginLayoutParams) fab - .getLayoutParams(); + ViewGroup.MarginLayoutParams lpFab = (ViewGroup.MarginLayoutParams) fab.getLayoutParams(); lpFab.bottomMargin += insets.getSystemWindowInsetBottom(); // portrait lpFab.rightMargin += insets.getSystemWindowInsetRight(); // landscape fab.setLayoutParams(lpFab); @@ -215,22 +227,20 @@ public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) { // we place a background behind the status bar to combine with it's semi-transparent // color to get the desired appearance. Set it's height to the status bar height View statusBarBackground = findViewById(R.id.status_bar_background); - FrameLayout.LayoutParams lpStatus = (FrameLayout.LayoutParams) - statusBarBackground.getLayoutParams(); + FrameLayout.LayoutParams lpStatus = (FrameLayout.LayoutParams) statusBarBackground.getLayoutParams(); lpStatus.height = insets.getSystemWindowInsetTop(); statusBarBackground.setLayoutParams(lpStatus); // inset the filters list for the status bar / navbar // need to set the padding end for landscape case - final boolean ltr = filtersList.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR; - filtersList.setPaddingRelative(filtersList.getPaddingStart(), + final boolean ltr = ViewCompat.getLayoutDirection(filtersList) == ViewCompat.LAYOUT_DIRECTION_LTR; + ViewCompat.setPaddingRelative(filtersList, ViewCompat.getPaddingStart(filtersList), filtersList.getPaddingTop() + insets.getSystemWindowInsetTop(), - filtersList.getPaddingEnd() + (ltr ? insets.getSystemWindowInsetRight() : - 0), + ViewCompat.getPaddingEnd(filtersList) + (ltr ? insets.getSystemWindowInsetRight() : 0), filtersList.getPaddingBottom() + insets.getSystemWindowInsetBottom()); // clear this listener so insets aren't re-applied - drawer.setOnApplyWindowInsetsListener(null); + ViewCompat.setOnApplyWindowInsetsListener(drawer, NOOP_WINDOW_INSETS_LISTENER); return insets.consumeSystemWindowInsets(); } @@ -296,16 +306,16 @@ protected void fabClick() { ContextCompat.getColor(this, R.color.accent)); intent.putExtra(PostStoryService.EXTRA_BROADCAST_RESULT, true); registerPostStoryResultListener(); - ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this, fab, + ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(this, fab, getString(R.string.transition_new_designer_news_post)); - startActivityForResult(intent, RC_NEW_DESIGNER_NEWS_STORY, options.toBundle()); + ActivityCompat.startActivityForResult(this, intent, RC_NEW_DESIGNER_NEWS_STORY, options.toBundle()); } else { Intent intent = new Intent(this, DesignerNewsLogin.class); intent.putExtra(FabDialogMorphSetup.EXTRA_SHARED_ELEMENT_START_COLOR, ContextCompat.getColor(this, R.color.accent)); - ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this, fab, + ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(this, fab, getString(R.string.transition_designer_news_login)); - startActivityForResult(intent, RC_NEW_DESIGNER_NEWS_LOGIN, options.toBundle()); + ActivityCompat.startActivityForResult(this, intent, RC_NEW_DESIGNER_NEWS_LOGIN, options.toBundle()); } } @@ -343,8 +353,7 @@ public void run() { .rotation(90f) .setStartDelay(2000L) // leave error on screen briefly .setDuration(300L) - .setInterpolator(AnimUtils.getFastOutSlowInInterpolator(HomeActivity - .this)) + .setInterpolator(AnimUtils.getFastOutSlowInInterpolator()) .setListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -396,7 +405,7 @@ private void revealPostingProgress() { 0f, fabPosting.getWidth() / 2) .setDuration(600L); - reveal.setInterpolator(AnimUtils.getFastOutLinearInInterpolator(this)); + reveal.setInterpolator(AnimUtils.getFastOutLinearInInterpolator()); reveal.start(); AnimatedVectorDrawable uploading = (AnimatedVectorDrawable) getDrawable(R.drawable.avd_uploading); @@ -419,7 +428,7 @@ private void checkEmptyState() { loading.setVisibility(View.GONE); setNoFiltersEmptyTextVisibility(View.VISIBLE); } - toolbar.setTranslationZ(0f); + ViewCompat.setTranslationZ(toolbar, 0f); } else { loading.setVisibility(View.GONE); setNoFiltersEmptyTextVisibility(View.GONE); @@ -472,11 +481,13 @@ private void setupTaskDescription() { // and looks bad on top of colorPrimary //Bitmap overviewIcon = ImageUtils.vectorToBitmap(this, R.drawable.ic_launcher_silhouette); // TODO replace launcher icon with a monochrome version from RN. - Bitmap overviewIcon = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher); - setTaskDescription(new ActivityManager.TaskDescription(getString(R.string.app_name), - overviewIcon, - ContextCompat.getColor(this, R.color.primary))); - overviewIcon.recycle(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + Bitmap overviewIcon = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher); + setTaskDescription(new ActivityManager.TaskDescription(getString(R.string.app_name), + overviewIcon, + ContextCompat.getColor(this, R.color.primary))); + overviewIcon.recycle(); + } } @Override @@ -509,7 +520,7 @@ private void animateToolbar() { .scaleX(1f) .setStartDelay(300) .setDuration(900) - .setInterpolator(AnimUtils.getFastOutSlowInInterpolator(this)); + .setInterpolator(AnimUtils.getFastOutSlowInInterpolator()); } View amv = toolbar.getChildAt(1); if (amv != null & amv instanceof ActionMenuView) { @@ -569,9 +580,14 @@ public boolean onOptionsItemSelected(MenuItem item) { View searchMenuView = toolbar.findViewById(R.id.menu_search); int[] loc = new int[2]; searchMenuView.getLocationOnScreen(loc); - startActivityForResult(SearchActivity.createStartIntent(this, loc[0], loc[0] + - (searchMenuView.getWidth() / 2)), RC_SEARCH, ActivityOptions - .makeSceneTransitionAnimation(this).toBundle()); + ActivityCompat.startActivityForResult(this, + SearchActivity.createStartIntent(this, + loc[0], + loc[0] + (searchMenuView.getWidth() / 2) + ), + RC_SEARCH, + ActivityOptionsCompat.makeSceneTransitionAnimation(this).toBundle() + ); searchMenuView.setAlpha(0f); return true; case R.id.menu_dribbble_login: @@ -595,8 +611,8 @@ public boolean onOptionsItemSelected(MenuItem item) { } return true; case R.id.menu_about: - startActivity(new Intent(HomeActivity.this, AboutActivity.class), - ActivityOptions.makeSceneTransitionAnimation(this).toBundle()); + ActivityCompat.startActivity(this, new Intent(HomeActivity.this, AboutActivity.class), + ActivityOptionsCompat.makeSceneTransitionAnimation(this).toBundle()); return true; } return super.onOptionsItemSelected(item); @@ -681,7 +697,7 @@ private void showFab() { .scaleY(1f) .translationY(0f) .setDuration(300L) - .setInterpolator(AnimUtils.getLinearOutSlowInInterpolator(this)) + .setInterpolator(AnimUtils.getLinearOutSlowInInterpolator()) .start(); } @@ -765,7 +781,7 @@ private void checkConnectivity() { ViewStub stub = (ViewStub) findViewById(R.id.stub_no_connection); ImageView iv = (ImageView) stub.inflate(); final AnimatedVectorDrawable avd = - (AnimatedVectorDrawable) getDrawable(R.drawable.avd_no_connection); + (AnimatedVectorDrawable) ContextCompat.getDrawable(this, R.drawable.avd_no_connection); iv.setImageDrawable(avd); avd.start(); } diff --git a/app/src/main/java/io/plaidapp/ui/HomeGridItemAnimator.java b/app/src/main/java/io/plaidapp/ui/HomeGridItemAnimator.java index d4aeca24e..0decfa6b6 100644 --- a/app/src/main/java/io/plaidapp/ui/HomeGridItemAnimator.java +++ b/app/src/main/java/io/plaidapp/ui/HomeGridItemAnimator.java @@ -22,6 +22,7 @@ import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.support.annotation.NonNull; +import android.support.v4.view.ViewCompat; import android.support.v7.widget.DefaultItemAnimator; import android.support.v7.widget.RecyclerView; import android.transition.ArcMotion; @@ -32,6 +33,7 @@ import io.plaidapp.util.AnimUtils; import io.plaidapp.util.ViewUtils; +import io.plaidapp.util.compat.ObjectAnimatorCompat; /** * An extension to {@link DefaultItemAnimator} for running animations specific to our home grid. @@ -47,10 +49,11 @@ public boolean canReuseUpdatedViewHolder(RecyclerView.ViewHolder viewHolder) { @NonNull @Override - public ItemHolderInfo recordPreLayoutInformation(RecyclerView.State state, - RecyclerView.ViewHolder viewHolder, - int changeFlags, - List payloads) { + public ItemHolderInfo recordPreLayoutInformation( + @NonNull RecyclerView.State state, + @NonNull RecyclerView.ViewHolder viewHolder, + int changeFlags, + @NonNull List payloads) { ItemHolderInfo info = super.recordPreLayoutInformation(state, viewHolder, changeFlags, payloads); if (payloads.contains(ANIMATE_ADD_POCKET)) { @@ -62,10 +65,11 @@ public ItemHolderInfo recordPreLayoutInformation(RecyclerView.State state, } @Override - public boolean animateChange(RecyclerView.ViewHolder oldHolder, - RecyclerView.ViewHolder newHolder, - ItemHolderInfo preInfo, - ItemHolderInfo postInfo) { + public boolean animateChange( + @NonNull RecyclerView.ViewHolder oldHolder, + @NonNull RecyclerView.ViewHolder newHolder, + @NonNull ItemHolderInfo preInfo, + @NonNull ItemHolderInfo postInfo) { if (preInfo instanceof DesignerNewsItemHolderInfo && ((DesignerNewsItemHolderInfo) preInfo).animateAddToPocket) { final FeedAdapter.DesignerNewsStoryHolder holder = @@ -87,7 +91,7 @@ public boolean animateChange(RecyclerView.ViewHolder oldHolder, -(holder.itemView.getHeight() / 5)), PropertyValuesHolder.ofFloat(View.ALPHA, 0.54f)); - Animator pocketMoveUp = ObjectAnimator.ofFloat(holder.pocket, + Animator pocketMoveUp = ObjectAnimatorCompat.ofFloat(holder.pocket, View.TRANSLATION_X, View.TRANSLATION_Y, arc.getPath(initialLeft, initialTop, translatedLeft, translatedTop)); Animator pocketScaleUp = ObjectAnimator.ofPropertyValuesHolder(holder.pocket, @@ -99,13 +103,13 @@ public boolean animateChange(RecyclerView.ViewHolder oldHolder, AnimatorSet up = new AnimatorSet(); up.playTogether(titleMoveFadeOut, pocketMoveUp, pocketScaleUp, pocketFadeUp); up.setDuration(300); - up.setInterpolator(AnimUtils.getFastOutSlowInInterpolator(holder.itemView.getContext())); + up.setInterpolator(AnimUtils.getFastOutSlowInInterpolator()); // animate everything back into place Animator titleMoveFadeIn = ObjectAnimator.ofPropertyValuesHolder(holder.title, PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, 0f), PropertyValuesHolder.ofFloat(View.ALPHA, 1f)); - Animator pocketMoveDown = ObjectAnimator.ofFloat(holder.pocket, + Animator pocketMoveDown = ObjectAnimatorCompat.ofFloat(holder.pocket, View.TRANSLATION_X, View.TRANSLATION_Y, arc.getPath(translatedLeft, translatedTop, 0, 0)); Animator pvhPocketScaleDown = ObjectAnimator.ofPropertyValuesHolder(holder.pocket, @@ -117,8 +121,7 @@ public boolean animateChange(RecyclerView.ViewHolder oldHolder, AnimatorSet down = new AnimatorSet(); down.playTogether(titleMoveFadeIn, pocketMoveDown, pvhPocketScaleDown, pocketFadeDown); down.setDuration(300); - down.setInterpolator(AnimUtils.getFastOutSlowInInterpolator(holder.itemView - .getContext())); + down.setInterpolator(AnimUtils.getFastOutSlowInInterpolator()); down.setStartDelay(500); // play it @@ -130,14 +133,14 @@ public boolean animateChange(RecyclerView.ViewHolder oldHolder, @Override public void onAnimationStart(Animator animation) { - holder.itemView.setHasTransientState(true); + ViewCompat.setHasTransientState(holder.itemView, true); dispatchAnimationStarted(holder); } @Override public void onAnimationEnd(Animator animation) { ((ViewGroup) holder.pocket.getParent().getParent()).setClipChildren(true); - holder.itemView.setHasTransientState(false); + ViewCompat.setHasTransientState(holder.itemView, false); dispatchAnimationFinished(holder); } }); diff --git a/app/src/main/java/io/plaidapp/ui/PlayerActivity.java b/app/src/main/java/io/plaidapp/ui/PlayerActivity.java index 67192934b..3970efca4 100644 --- a/app/src/main/java/io/plaidapp/ui/PlayerActivity.java +++ b/app/src/main/java/io/plaidapp/ui/PlayerActivity.java @@ -16,14 +16,19 @@ package io.plaidapp.ui; +import android.annotation.TargetApi; import android.app.Activity; import android.app.ActivityOptions; import android.app.SharedElementCallback; import android.content.Intent; import android.content.res.Resources; import android.graphics.drawable.AnimatedVectorDrawable; +import android.os.Build; import android.os.Bundle; +import android.support.v4.app.ActivityCompat; +import android.support.v4.app.ActivityOptionsCompat; import android.support.v4.content.ContextCompat; +import android.support.v4.widget.TextViewCompat; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.RecyclerView; import android.text.TextUtils; @@ -60,6 +65,7 @@ import io.plaidapp.ui.widget.ElasticDragDismissFrameLayout; import io.plaidapp.util.DribbbleUtils; import io.plaidapp.util.ViewUtils; +import io.plaidapp.util.compat.TransitionManagerCompat; import io.plaidapp.util.glide.CircleTransform; import retrofit.Callback; import retrofit.RestAdapter; @@ -108,7 +114,7 @@ protected void onCreate(Bundle savedInstanceState) { chromeFader = new ElasticDragDismissFrameLayout.SystemChromeFader(getWindow()) { @Override public void onDragDismissed() { - finishAfterTransition(); + ActivityCompat.finishAfterTransition(PlayerActivity.this); } }; @@ -163,8 +169,8 @@ private void bindPlayer() { shotCount.setText(res.getQuantityString(R.plurals.shots, player.shots_count, nf.format(player.shots_count))); if (player.shots_count == 0) { - shotCount.setCompoundDrawablesRelativeWithIntrinsicBounds( - null, getDrawable(R.drawable.avd_no_shots), null, null); + TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(shotCount, + null, ContextCompat.getDrawable(this, R.drawable.avd_no_shots), null, null); } setFollowerCount(player.followers_count); likesCount.setText(res.getQuantityString(R.plurals.likes, player.likes_count, @@ -228,7 +234,7 @@ public boolean onTouch(View v, MotionEvent event) { // check if following if (dataManager.getDribbblePrefs().isLoggedIn()) { if (player.id == dataManager.getDribbblePrefs().getUserId()) { - TransitionManager.beginDelayedTransition(playerDescription); + TransitionManagerCompat.beginDelayedTransition(playerDescription); follow.setVisibility(View.GONE); ViewUtils.setPaddingTop(shots, playerDescription.getHeight() - follow.getHeight() - ((ViewGroup.MarginLayoutParams) follow.getLayoutParams()).bottomMargin); @@ -237,7 +243,7 @@ public boolean onTouch(View v, MotionEvent event) { @Override public void success(Void voyd, Response response) { following = true; - TransitionManager.beginDelayedTransition(playerDescription); + TransitionManagerCompat.beginDelayedTransition(playerDescription); follow.setText(R.string.following); follow.setActivated(true); } @@ -259,6 +265,7 @@ public void failure(RetrofitError error) { } } + @TargetApi(Build.VERSION_CODES.LOLLIPOP) private void setupSharedEnter() { setEnterSharedElementCallback(new SharedElementCallback() { private float finalSize; @@ -337,7 +344,7 @@ private void setFollowerCount(int count) { followersCount.setText(getResources().getQuantityString(R.plurals.follower_count, followerCount, NumberFormat.getInstance().format(followerCount))); if (followerCount == 0) { - followersCount.setBackground(null); + ViewUtils.setBackground(followersCount, null); } } @@ -351,7 +358,7 @@ private void setFollowerCount(int count) { @Override public void failure(RetrofitError error) { } }); following = false; - TransitionManager.beginDelayedTransition(playerDescription); + TransitionManagerCompat.beginDelayedTransition(playerDescription); follow.setText(R.string.follow); follow.setActivated(false); setFollowerCount(followerCount - 1); @@ -362,7 +369,7 @@ private void setFollowerCount(int count) { @Override public void failure(RetrofitError error) { } }); following = true; - TransitionManager.beginDelayedTransition(playerDescription); + TransitionManagerCompat.beginDelayedTransition(playerDescription); follow.setText(R.string.following); follow.setActivated(true); setFollowerCount(followerCount + 1); @@ -373,9 +380,9 @@ private void setFollowerCount(int count) { ContextCompat.getColor(this, R.color.dribbble)); login.putExtra(FabDialogMorphSetup.EXTRA_SHARED_ELEMENT_START_CORNER_RADIUS, getResources().getDimensionPixelSize(R.dimen.dialog_corners)); - ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation + ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation (this, follow, getString(R.string.transition_dribbble_login)); - startActivity(login, options.toBundle()); + ActivityCompat.startActivity(this, login, options.toBundle()); } } diff --git a/app/src/main/java/io/plaidapp/ui/PlayerSheet.java b/app/src/main/java/io/plaidapp/ui/PlayerSheet.java index b5a776c13..2e9760712 100644 --- a/app/src/main/java/io/plaidapp/ui/PlayerSheet.java +++ b/app/src/main/java/io/plaidapp/ui/PlayerSheet.java @@ -19,18 +19,19 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.app.Activity; -import android.app.ActivityOptions; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.support.annotation.IntDef; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.support.v4.app.ActivityCompat; +import android.support.v4.app.ActivityOptionsCompat; +import android.support.v4.util.Pair; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.text.TextUtils; import android.text.format.DateUtils; -import android.util.Pair; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -101,16 +102,16 @@ public static void start(Activity launching, Shot shot) { Intent starter = new Intent(launching, PlayerSheet.class); starter.putExtra(EXTRA_MODE, MODE_SHOT_LIKES); starter.putExtra(EXTRA_SHOT, shot); - launching.startActivity(starter, - ActivityOptions.makeSceneTransitionAnimation(launching).toBundle()); + ActivityCompat.startActivity(launching, starter, + ActivityOptionsCompat.makeSceneTransitionAnimation(launching).toBundle()); } public static void start(Activity launching, User player) { Intent starter = new Intent(launching, PlayerSheet.class); starter.putExtra(EXTRA_MODE, MODE_FOLLOWERS); starter.putExtra(EXTRA_USER, player); - launching.startActivity(starter, - ActivityOptions.makeSceneTransitionAnimation(launching).toBundle()); + ActivityCompat.startActivity(launching, starter, + ActivityOptionsCompat.makeSceneTransitionAnimation(launching).toBundle()); } @Override @@ -155,7 +156,7 @@ public void onFollowersLoaded(List followers) { bottomSheet.registerCallback(new BottomSheet.Callbacks() { @Override public void onSheetDismissed() { - finishAfterTransition(); + ActivityCompat.finishAfterTransition(PlayerSheet.this); } @Override @@ -199,7 +200,7 @@ private void showClose() { .scaleX(1f) .scaleY(1f) .setDuration(200L) - .setInterpolator(getLinearOutSlowInInterpolator(PlayerSheet.this)) + .setInterpolator(getLinearOutSlowInInterpolator()) .setListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { @@ -216,7 +217,7 @@ private void hideClose() { .scaleX(0.8f) .scaleY(0.8f) .setDuration(200L) - .setInterpolator(getFastOutLinearInInterpolator(PlayerSheet.this)) + .setInterpolator(getFastOutLinearInInterpolator()) .setListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -271,13 +272,13 @@ public void onClick(View v) { final User user = items.get(holder.getAdapterPosition()).getPlayer(); final Intent player = new Intent(PlayerSheet.this, PlayerActivity.class); player.putExtra(PlayerActivity.EXTRA_PLAYER, user); - final ActivityOptions options = - ActivityOptions.makeSceneTransitionAnimation(PlayerSheet.this, + final ActivityOptionsCompat options = + ActivityOptionsCompat.makeSceneTransitionAnimation(PlayerSheet.this, Pair.create((View) holder.playerAvatar, getString(R.string.transition_player_avatar)), Pair.create((View) holder.playerName, getString(R.string.transition_player_name))); - startActivity(player, options.toBundle()); + ActivityCompat.startActivity(PlayerSheet.this, player, options.toBundle()); } }); return holder; diff --git a/app/src/main/java/io/plaidapp/ui/PostNewDesignerNewsStory.java b/app/src/main/java/io/plaidapp/ui/PostNewDesignerNewsStory.java index 7b0067fce..8e93be07f 100644 --- a/app/src/main/java/io/plaidapp/ui/PostNewDesignerNewsStory.java +++ b/app/src/main/java/io/plaidapp/ui/PostNewDesignerNewsStory.java @@ -19,12 +19,14 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.app.Activity; -import android.app.ActivityOptions; import android.content.Intent; import android.os.Bundle; import android.support.design.widget.TextInputLayout; +import android.support.v4.app.ActivityCompat; +import android.support.v4.app.ActivityOptionsCompat; import android.support.v4.app.ShareCompat; import android.support.v4.content.ContextCompat; +import android.support.v4.view.ViewCompat; import android.text.TextUtils; import android.view.KeyEvent; import android.view.ViewGroup; @@ -88,21 +90,19 @@ public void onSheetDismissed() { @Override public void onScrolled(int scrollY) { if (scrollY != 0 - && sheetTitle.getTranslationZ() != appBarElevation) { - sheetTitle.animate() + && ViewCompat.getTranslationZ(sheetTitle) != appBarElevation) { + ViewCompat.animate(sheetTitle) .translationZ(appBarElevation) .setStartDelay(0L) .setDuration(80L) - .setInterpolator(AnimUtils.getFastOutSlowInInterpolator - (PostNewDesignerNewsStory.this)) + .setInterpolator(AnimUtils.getFastOutSlowInInterpolator()) .start(); - } else if (scrollY == 0 && sheetTitle.getTranslationZ() == appBarElevation) { - sheetTitle.animate() + } else if (scrollY == 0 && ViewCompat.getTranslationZ(sheetTitle) == appBarElevation) { + ViewCompat.animate(sheetTitle) .translationZ(0f) .setStartDelay(0L) .setDuration(80L) - .setInterpolator(AnimUtils.getFastOutSlowInInterpolator - (PostNewDesignerNewsStory.this)) + .setInterpolator(AnimUtils.getFastOutSlowInInterpolator()) .start(); } } @@ -127,8 +127,7 @@ public boolean onPreDraw() { .translationY(0f) .setStartDelay(120L) .setDuration(240L) - .setInterpolator(AnimUtils.getLinearOutSlowInInterpolator - (PostNewDesignerNewsStory.this)); + .setInterpolator(AnimUtils.getLinearOutSlowInInterpolator()); return false; } }); @@ -148,12 +147,11 @@ public void onBackPressed() { bottomSheetContent.animate() .translationY(bottomSheetContent.getHeight()) .setDuration(160L) - .setInterpolator(AnimUtils.getFastOutLinearInInterpolator - (PostNewDesignerNewsStory.this)) + .setInterpolator(AnimUtils.getFastOutLinearInInterpolator()) .setListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - finishAfterTransition(); + ActivityCompat.finishAfterTransition(PostNewDesignerNewsStory.this); } }); } else { @@ -163,7 +161,7 @@ public void onAnimationEnd(Animator animation) { @OnClick(R.id.bottom_sheet) protected void dismiss() { - finishAfterTransition(); + ActivityCompat.finishAfterTransition(this); } @OnTextChanged(R.id.new_story_title) @@ -202,15 +200,15 @@ protected void postNewStory() { getIntent().getBooleanExtra(PostStoryService.EXTRA_BROADCAST_RESULT, false)); startService(postIntent); setResult(RESULT_POSTING); - finishAfterTransition(); + ActivityCompat.finishAfterTransition(this); } else { Intent login = new Intent(this, DesignerNewsLogin.class); login.putExtra(FabDialogMorphSetup.EXTRA_SHARED_ELEMENT_START_COLOR, ContextCompat.getColor(this, R.color.designer_news)); login.putExtra(FabDialogMorphSetup.EXTRA_SHARED_ELEMENT_START_CORNER_RADIUS, 0); - ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation( + ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation( this, post, getString(R.string.transition_designer_news_login)); - startActivity(login, options.toBundle()); + ActivityCompat.startActivity(this, login, options.toBundle()); } } diff --git a/app/src/main/java/io/plaidapp/ui/SearchActivity.java b/app/src/main/java/io/plaidapp/ui/SearchActivity.java index 52a8e00dd..90e239987 100644 --- a/app/src/main/java/io/plaidapp/ui/SearchActivity.java +++ b/app/src/main/java/io/plaidapp/ui/SearchActivity.java @@ -28,28 +28,26 @@ import android.graphics.Typeface; import android.graphics.drawable.AnimatedVectorDrawable; import android.os.Bundle; +import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; +import android.support.v4.view.ViewCompat; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.RecyclerView; -import android.text.InputType; +import android.support.v7.widget.SearchView; import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.TextUtils; import android.text.style.StyleSpan; import android.transition.Transition; import android.transition.TransitionInflater; -import android.transition.TransitionManager; import android.util.TypedValue; import android.view.View; -import android.view.ViewAnimationUtils; import android.view.ViewGroup; import android.view.ViewStub; import android.view.ViewTreeObserver; -import android.view.inputmethod.EditorInfo; import android.widget.CheckBox; import android.widget.ImageButton; import android.widget.ProgressBar; -import android.widget.SearchView; import java.util.List; @@ -67,6 +65,9 @@ import io.plaidapp.util.AnimUtils; import io.plaidapp.util.ImeUtils; import io.plaidapp.util.ViewUtils; +import io.plaidapp.util.compat.ObjectAnimatorCompat; +import io.plaidapp.util.compat.TransitionManagerCompat; +import io.plaidapp.util.compat.ViewAnimationUtilsCompat; public class SearchActivity extends Activity { @@ -122,7 +123,7 @@ protected void onCreate(Bundle savedInstanceState) { public void onDataLoaded(List data) { if (data != null && data.size() > 0) { if (results.getVisibility() != View.VISIBLE) { - TransitionManager.beginDelayedTransition(container, auto); + TransitionManagerCompat.beginDelayedTransition(container, auto); progress.setVisibility(View.GONE); results.setVisibility(View.VISIBLE); fab.setVisibility(View.VISIBLE); @@ -135,13 +136,11 @@ public void onDataLoaded(List data) { .scaleY(1f) .setStartDelay(800L) .setDuration(300L) - .setInterpolator(AnimUtils.getLinearOutSlowInInterpolator - (SearchActivity - .this)); + .setInterpolator(AnimUtils.getLinearOutSlowInInterpolator()); } adapter.addAndResort(data); } else { - TransitionManager.beginDelayedTransition(container, auto); + TransitionManagerCompat.beginDelayedTransition(container, auto); progress.setVisibility(View.GONE); setNoResultsVisibility(View.VISIBLE); } @@ -176,7 +175,7 @@ public void onLoadMore() { searchBackContainer.animate() .translationX(0f) .setDuration(650L) - .setInterpolator(AnimUtils.getFastOutSlowInInterpolator(this)); + .setInterpolator(AnimUtils.getFastOutSlowInInterpolator()); // transform from search icon to back icon AnimatedVectorDrawable searchToBack = (AnimatedVectorDrawable) ContextCompat .getDrawable(this, R.drawable.avd_search_to_back); @@ -198,12 +197,12 @@ public void run() { searchBackground.animate() .alpha(1f) .setDuration(300L) - .setInterpolator(AnimUtils.getLinearOutSlowInInterpolator(this)); + .setInterpolator(AnimUtils.getLinearOutSlowInInterpolator()); searchView.animate() .alpha(1f) .setStartDelay(400L) .setDuration(400L) - .setInterpolator(AnimUtils.getLinearOutSlowInInterpolator(this)) + .setInterpolator(AnimUtils.getLinearOutSlowInInterpolator()) .setListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -219,21 +218,20 @@ public boolean onPreDraw() { scrim.getViewTreeObserver().removeOnPreDrawListener(this); AnimatorSet showScrim = new AnimatorSet(); showScrim.playTogether( - ViewAnimationUtils.createCircularReveal( + ViewAnimationUtilsCompat.createCircularReveal( scrim, searchIconCenterX, searchBackground.getBottom(), 0, (float) Math.hypot(searchBackDistanceX, scrim.getHeight() - searchBackground.getBottom())), - ObjectAnimator.ofArgb( + ObjectAnimatorCompat.ofArgb( scrim, ViewUtils.BACKGROUND_COLOR, Color.TRANSPARENT, ContextCompat.getColor(SearchActivity.this, R.color.scrim))); showScrim.setDuration(400L); - showScrim.setInterpolator(AnimUtils.getLinearOutSlowInInterpolator(SearchActivity - .this)); + showScrim.setInterpolator(AnimUtils.getLinearOutSlowInInterpolator()); showScrim.start(); return false; } @@ -274,11 +272,11 @@ protected void dismiss() { searchBackContainer.animate() .translationX(searchBackDistanceX) .setDuration(600L) - .setInterpolator(AnimUtils.getFastOutSlowInInterpolator(this)) + .setInterpolator(AnimUtils.getFastOutSlowInInterpolator()) .setListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - finishAfterTransition(); + ActivityCompat.finishAfterTransition(SearchActivity.this); } }) .start(); @@ -287,42 +285,41 @@ public void onAnimationEnd(Animator animation) { .getDrawable(this, R.drawable.avd_back_to_search); searchBack.setImageDrawable(backToSearch); // clear the background else the touch ripple moves with the translation which looks bad - searchBack.setBackground(null); + ViewUtils.setBackground(searchBack, null); backToSearch.start(); // fade out the other search chrome searchView.animate() .alpha(0f) .setStartDelay(0L) .setDuration(120L) - .setInterpolator(AnimUtils.getFastOutLinearInInterpolator(this)) + .setInterpolator(AnimUtils.getFastOutLinearInInterpolator()) .setListener(null) .start(); searchBackground.animate() .alpha(0f) .setStartDelay(300L) .setDuration(160L) - .setInterpolator(AnimUtils.getFastOutLinearInInterpolator(this)) + .setInterpolator(AnimUtils.getFastOutLinearInInterpolator()) .setListener(null) .start(); - if (searchToolbar.getZ() != 0f) { - searchToolbar.animate() + if (ViewCompat.getZ(searchToolbar) != 0f) { + ViewCompat.animate(searchToolbar) .z(0f) .setDuration(600L) - .setInterpolator(AnimUtils.getFastOutLinearInInterpolator(this)) + .setInterpolator(AnimUtils.getFastOutLinearInInterpolator()) .start(); } // if we're showing search results, circular hide them if (resultsContainer.getHeight() > 0) { - Animator closeResults = ViewAnimationUtils.createCircularReveal( + Animator closeResults = ViewAnimationUtilsCompat.createCircularReveal( resultsContainer, searchIconCenterX, 0, (float) Math.hypot(searchIconCenterX, resultsContainer.getHeight()), 0f); closeResults.setDuration(500L); - closeResults.setInterpolator(AnimUtils.getFastOutSlowInInterpolator(SearchActivity - .this)); + closeResults.setInterpolator(AnimUtils.getFastOutSlowInInterpolator()); closeResults.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -336,7 +333,7 @@ public void onAnimationEnd(Animator animation) { scrim.animate() .alpha(0f) .setDuration(400L) - .setInterpolator(AnimUtils.getFastOutLinearInInterpolator(this)) + .setInterpolator(AnimUtils.getFastOutLinearInInterpolator()) .setListener(null) .start(); } @@ -355,35 +352,33 @@ protected void save() { public boolean onPreDraw() { // expand the confirmation confirmSaveContainer.getViewTreeObserver().removeOnPreDrawListener(this); - Animator reveal = ViewAnimationUtils.createCircularReveal(confirmSaveContainer, + Animator reveal = ViewAnimationUtilsCompat.createCircularReveal(confirmSaveContainer, confirmSaveContainer.getWidth() / 2, confirmSaveContainer.getHeight() / 2, fab.getWidth() / 2, confirmSaveContainer.getWidth() / 2); reveal.setDuration(250L); - reveal.setInterpolator(AnimUtils.getFastOutSlowInInterpolator(SearchActivity.this)); + reveal.setInterpolator(AnimUtils.getFastOutSlowInInterpolator()); reveal.start(); // show the scrim int centerX = (fab.getLeft() + fab.getRight()) / 2; int centerY = (fab.getTop() + fab.getBottom()) / 2; - Animator revealScrim = ViewAnimationUtils.createCircularReveal( + Animator revealScrim = ViewAnimationUtilsCompat.createCircularReveal( resultsScrim, centerX, centerY, 0, (float) Math.hypot(centerX, centerY)); revealScrim.setDuration(400L); - revealScrim.setInterpolator(AnimUtils.getLinearOutSlowInInterpolator(SearchActivity - .this)); + revealScrim.setInterpolator(AnimUtils.getLinearOutSlowInInterpolator()); revealScrim.start(); - ObjectAnimator fadeInScrim = ObjectAnimator.ofArgb(resultsScrim, + ObjectAnimator fadeInScrim = ObjectAnimatorCompat.ofArgb(resultsScrim, ViewUtils.BACKGROUND_COLOR, Color.TRANSPARENT, ContextCompat.getColor(SearchActivity.this, R.color.scrim)); fadeInScrim.setDuration(800L); - fadeInScrim.setInterpolator(AnimUtils.getLinearOutSlowInInterpolator(SearchActivity - .this)); + fadeInScrim.setInterpolator(AnimUtils.getLinearOutSlowInInterpolator()); fadeInScrim.start(); // ease in the checkboxes @@ -393,16 +388,14 @@ public boolean onPreDraw() { .alpha(1f) .translationY(0f) .setDuration(200L) - .setInterpolator(AnimUtils.getLinearOutSlowInInterpolator(SearchActivity - .this)); + .setInterpolator(AnimUtils.getLinearOutSlowInInterpolator()); saveDesignerNews.setAlpha(0.6f); saveDesignerNews.setTranslationY(saveDesignerNews.getHeight() * 0.5f); saveDesignerNews.animate() .alpha(1f) .translationY(0f) .setDuration(200L) - .setInterpolator(AnimUtils.getLinearOutSlowInInterpolator(SearchActivity - .this)); + .setInterpolator(AnimUtils.getLinearOutSlowInInterpolator()); return false; } }); @@ -424,17 +417,16 @@ protected void hideSaveConfimation() { // contract the bubble & hide the scrim AnimatorSet hideConfirmation = new AnimatorSet(); hideConfirmation.playTogether( - ViewAnimationUtils.createCircularReveal(confirmSaveContainer, + ViewAnimationUtilsCompat.createCircularReveal(confirmSaveContainer, confirmSaveContainer.getWidth() / 2, confirmSaveContainer.getHeight() / 2, confirmSaveContainer.getWidth() / 2, fab.getWidth() / 2), - ObjectAnimator.ofArgb(resultsScrim, + ObjectAnimatorCompat.ofArgb(resultsScrim, ViewUtils.BACKGROUND_COLOR, Color.TRANSPARENT)); hideConfirmation.setDuration(150L); - hideConfirmation.setInterpolator(AnimUtils.getFastOutSlowInInterpolator - (SearchActivity.this)); + hideConfirmation.setInterpolator(AnimUtils.getFastOutSlowInInterpolator()); hideConfirmation.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -450,11 +442,6 @@ public void onAnimationEnd(Animator animation) { private void setupSearchView() { SearchManager searchManager = (SearchManager) getSystemService(SEARCH_SERVICE); searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName())); - // hint, inputType & ime options seem to be ignored from XML! Set in code - searchView.setQueryHint(getString(R.string.search_hint)); - searchView.setInputType(InputType.TYPE_TEXT_FLAG_CAP_WORDS); - searchView.setImeOptions(searchView.getImeOptions() | EditorInfo.IME_ACTION_SEARCH | - EditorInfo.IME_FLAG_NO_EXTRACT_UI | EditorInfo.IME_FLAG_NO_FULLSCREEN); searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String query) { @@ -483,7 +470,7 @@ public void onFocusChange(View v, boolean hasFocus) { private void clearResults() { adapter.clear(); dataManager.clear(); - TransitionManager.beginDelayedTransition(container, auto); + TransitionManagerCompat.beginDelayedTransition(container, auto); results.setVisibility(View.GONE); progress.setVisibility(View.GONE); fab.setVisibility(View.GONE); diff --git a/app/src/main/java/io/plaidapp/ui/drawable/MorphDrawable.java b/app/src/main/java/io/plaidapp/ui/drawable/MorphDrawable.java index 12f6e53a0..65361661e 100644 --- a/app/src/main/java/io/plaidapp/ui/drawable/MorphDrawable.java +++ b/app/src/main/java/io/plaidapp/ui/drawable/MorphDrawable.java @@ -16,15 +16,19 @@ package io.plaidapp.ui.drawable; +import android.annotation.TargetApi; import android.graphics.Canvas; import android.graphics.ColorFilter; import android.graphics.Outline; import android.graphics.Paint; import android.graphics.drawable.Drawable; +import android.os.Build; import android.support.annotation.ColorInt; +import android.support.annotation.NonNull; import android.util.Property; import io.plaidapp.util.AnimUtils; +import io.plaidapp.util.compat.CanvasCompat; /** * A drawable that can morph size, shape (via it's corner radius) and color. Specifically this is @@ -87,12 +91,13 @@ public void setColor(int color) { @Override public void draw(Canvas canvas) { - canvas.drawRoundRect(getBounds().left, getBounds().top, getBounds().right, getBounds() + CanvasCompat.drawRoundRect(canvas, getBounds().left, getBounds().top, getBounds().right, getBounds() .bottom, cornerRadius, cornerRadius, paint); } + @TargetApi(Build.VERSION_CODES.LOLLIPOP) @Override - public void getOutline(Outline outline) { + public void getOutline(@NonNull Outline outline) { outline.setRoundRect(getBounds(), cornerRadius); } diff --git a/app/src/main/java/io/plaidapp/ui/transitions/FabDialogMorphSetup.java b/app/src/main/java/io/plaidapp/ui/transitions/FabDialogMorphSetup.java index a1b3ec339..b864971d1 100644 --- a/app/src/main/java/io/plaidapp/ui/transitions/FabDialogMorphSetup.java +++ b/app/src/main/java/io/plaidapp/ui/transitions/FabDialogMorphSetup.java @@ -16,8 +16,10 @@ package io.plaidapp.ui.transitions; +import android.annotation.TargetApi; import android.app.Activity; import android.graphics.Color; +import android.os.Build; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.transition.ArcMotion; @@ -29,6 +31,7 @@ /** * Helper class for setting up Fab <-> Dialog shared element transitions. */ +@TargetApi(Build.VERSION_CODES.LOLLIPOP) public class FabDialogMorphSetup { public static final String EXTRA_SHARED_ELEMENT_START_COLOR = @@ -58,7 +61,7 @@ public static void setupSharedEelementTransitions(@NonNull Activity activity, int color = activity.getIntent(). getIntExtra(EXTRA_SHARED_ELEMENT_START_COLOR, Color.TRANSPARENT); Interpolator easeInOut = - AnimUtils.getFastOutSlowInInterpolator(activity); + AnimUtils.getFastOutSlowInInterpolator(); MorphFabToDialog sharedEnter = new MorphFabToDialog(color, dialogCornerRadius, startCornerRadius); sharedEnter.setPathMotion(arcMotion); diff --git a/app/src/main/java/io/plaidapp/ui/transitions/MorphDialogToFab.java b/app/src/main/java/io/plaidapp/ui/transitions/MorphDialogToFab.java index b34d5c8e4..e4cb5cc93 100644 --- a/app/src/main/java/io/plaidapp/ui/transitions/MorphDialogToFab.java +++ b/app/src/main/java/io/plaidapp/ui/transitions/MorphDialogToFab.java @@ -19,8 +19,10 @@ import android.animation.Animator; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; +import android.annotation.TargetApi; import android.content.Context; import android.graphics.Color; +import android.os.Build; import android.support.annotation.ColorInt; import android.support.v4.content.ContextCompat; import android.transition.ChangeBounds; @@ -38,6 +40,7 @@ /** * A transition that morphs a rectangle into a circle, changing it's background color. */ +@TargetApi(Build.VERSION_CODES.LOLLIPOP) public class MorphDialogToFab extends ChangeBounds { private static final String PROPERTY_COLOR = "io.plaidapp:rectMorph:color"; @@ -138,7 +141,7 @@ public Animator createAnimator(final ViewGroup sceneRoot, .translationY(v.getHeight() / 3) .setStartDelay(0L) .setDuration(50L) - .setInterpolator(getFastOutLinearInInterpolator(vg.getContext())) + .setInterpolator(getFastOutLinearInInterpolator()) .start(); } } @@ -146,7 +149,7 @@ public Animator createAnimator(final ViewGroup sceneRoot, AnimatorSet transition = new AnimatorSet(); transition.playTogether(changeBounds, corners, color); transition.setDuration(300); - transition.setInterpolator(getFastOutSlowInInterpolator(sceneRoot.getContext())); + transition.setInterpolator(getFastOutSlowInInterpolator()); return transition; } } diff --git a/app/src/main/java/io/plaidapp/ui/transitions/MorphFabToDialog.java b/app/src/main/java/io/plaidapp/ui/transitions/MorphFabToDialog.java index e2d70356c..df870d60a 100644 --- a/app/src/main/java/io/plaidapp/ui/transitions/MorphFabToDialog.java +++ b/app/src/main/java/io/plaidapp/ui/transitions/MorphFabToDialog.java @@ -19,8 +19,10 @@ import android.animation.Animator; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; +import android.annotation.TargetApi; import android.content.Context; import android.graphics.Color; +import android.os.Build; import android.support.annotation.ColorInt; import android.support.v4.content.ContextCompat; import android.transition.ChangeBounds; @@ -36,6 +38,7 @@ /** * A transition that morphs a circle into a rectangle, changing it's background color. */ +@TargetApi(Build.VERSION_CODES.LOLLIPOP) public class MorphFabToDialog extends ChangeBounds { private static final String PROPERTY_COLOR = "plaid:circleMorph:color"; @@ -143,7 +146,7 @@ public Animator createAnimator(final ViewGroup sceneRoot, .translationY(0f) .setDuration(150) .setStartDelay(150) - .setInterpolator(AnimUtils.getFastOutSlowInInterpolator(vg.getContext())); + .setInterpolator(AnimUtils.getFastOutSlowInInterpolator()); offset *= 1.8f; } } @@ -151,7 +154,7 @@ public Animator createAnimator(final ViewGroup sceneRoot, AnimatorSet transition = new AnimatorSet(); transition.playTogether(changeBounds, corners, color); transition.setDuration(300); - transition.setInterpolator(AnimUtils.getFastOutSlowInInterpolator(sceneRoot.getContext())); + transition.setInterpolator(AnimUtils.getFastOutSlowInInterpolator()); return transition; } diff --git a/app/src/main/java/io/plaidapp/ui/transitions/Pop.java b/app/src/main/java/io/plaidapp/ui/transitions/Pop.java index 49dd3c162..edc341b87 100644 --- a/app/src/main/java/io/plaidapp/ui/transitions/Pop.java +++ b/app/src/main/java/io/plaidapp/ui/transitions/Pop.java @@ -19,7 +19,9 @@ import android.animation.Animator; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; +import android.annotation.TargetApi; import android.content.Context; +import android.os.Build; import android.transition.TransitionValues; import android.transition.Visibility; import android.util.AttributeSet; @@ -31,6 +33,7 @@ /** * A transition that animates the alpha & scale X & Y of a view simultaneously. */ +@TargetApi(Build.VERSION_CODES.LOLLIPOP) public class Pop extends Visibility { private static final String PROPNAME_ALPHA = "plaid:pop:alpha"; diff --git a/app/src/main/java/io/plaidapp/ui/transitions/ShotSharedEnter.java b/app/src/main/java/io/plaidapp/ui/transitions/ShotSharedEnter.java index 44eae8f7d..027562f25 100644 --- a/app/src/main/java/io/plaidapp/ui/transitions/ShotSharedEnter.java +++ b/app/src/main/java/io/plaidapp/ui/transitions/ShotSharedEnter.java @@ -16,8 +16,10 @@ package io.plaidapp.ui.transitions; +import android.annotation.TargetApi; import android.content.Context; import android.graphics.Rect; +import android.os.Build; import android.transition.ChangeBounds; import android.transition.TransitionValues; import android.util.AttributeSet; @@ -27,6 +29,7 @@ * Shared element transitions do not seem to like transitioning from a single view to two separate * views so we need to alter the ChangeBounds transition to compensate */ +@TargetApi(Build.VERSION_CODES.LOLLIPOP) public class ShotSharedEnter extends ChangeBounds { private static final String PROPNAME_BOUNDS = "android:changeBounds:bounds"; diff --git a/app/src/main/java/io/plaidapp/ui/transitions/StoryTitleSharedEnter.java b/app/src/main/java/io/plaidapp/ui/transitions/StoryTitleSharedEnter.java index f5453e036..27e464c52 100644 --- a/app/src/main/java/io/plaidapp/ui/transitions/StoryTitleSharedEnter.java +++ b/app/src/main/java/io/plaidapp/ui/transitions/StoryTitleSharedEnter.java @@ -16,8 +16,10 @@ package io.plaidapp.ui.transitions; +import android.annotation.TargetApi; import android.content.Context; import android.graphics.Rect; +import android.os.Build; import android.transition.ChangeBounds; import android.transition.TransitionValues; import android.util.AttributeSet; @@ -27,6 +29,7 @@ * Shared element transitions do not seem to like transitioning from a single view to two separate * views so we need to alter the ChangeBounds transition to compensate */ +@TargetApi(Build.VERSION_CODES.LOLLIPOP) public class StoryTitleSharedEnter extends ChangeBounds { private static final String PROPNAME_BOUNDS = "android:changeBounds:bounds"; diff --git a/app/src/main/java/io/plaidapp/ui/transitions/TextResizeTransition.java b/app/src/main/java/io/plaidapp/ui/transitions/TextResizeTransition.java index 1b8e6c391..c408ac712 100644 --- a/app/src/main/java/io/plaidapp/ui/transitions/TextResizeTransition.java +++ b/app/src/main/java/io/plaidapp/ui/transitions/TextResizeTransition.java @@ -3,7 +3,9 @@ import android.animation.Animator; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; +import android.annotation.TargetApi; import android.content.Context; +import android.os.Build; import android.transition.Transition; import android.transition.TransitionValues; import android.util.AttributeSet; @@ -13,6 +15,7 @@ import io.plaidapp.util.ViewUtils; +@TargetApi(Build.VERSION_CODES.KITKAT) public class TextResizeTransition extends Transition { private static final String PROPNAME_TEXT = diff --git a/app/src/main/java/io/plaidapp/ui/widget/BadgedFourThreeImageView.java b/app/src/main/java/io/plaidapp/ui/widget/BadgedFourThreeImageView.java index 55044cdba..f66bd2aed 100644 --- a/app/src/main/java/io/plaidapp/ui/widget/BadgedFourThreeImageView.java +++ b/app/src/main/java/io/plaidapp/ui/widget/BadgedFourThreeImageView.java @@ -35,6 +35,7 @@ import android.view.Gravity; import io.plaidapp.R; +import io.plaidapp.util.compat.CanvasCompat; /** * A view group that draws a badge drawable on top of it's contents. @@ -134,8 +135,7 @@ private static class GifBadge extends Drawable { final Canvas canvas = new Canvas(bitmap); final Paint backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); backgroundPaint.setColor(BACKGROUND_COLOR); - canvas.drawRoundRect(0, 0, width, height, cornerRadius, cornerRadius, - backgroundPaint); + CanvasCompat.drawRoundRect(canvas, 0, 0, width, height, cornerRadius, cornerRadius, backgroundPaint); // punch out the word 'GIF', leaving transparency textPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); canvas.drawText(GIF, padding, height - padding, textPaint); diff --git a/app/src/main/java/io/plaidapp/ui/widget/BaselineGridTextView.java b/app/src/main/java/io/plaidapp/ui/widget/BaselineGridTextView.java index a53d19c1c..866661f2b 100644 --- a/app/src/main/java/io/plaidapp/ui/widget/BaselineGridTextView.java +++ b/app/src/main/java/io/plaidapp/ui/widget/BaselineGridTextView.java @@ -19,6 +19,7 @@ import android.content.Context; import android.content.res.TypedArray; import android.graphics.Paint; +import android.os.Build; import android.util.AttributeSet; import android.util.TypedValue; @@ -59,7 +60,9 @@ public BaselineGridTextView(Context context, AttributeSet attrs, a.recycle(); setIncludeFontPadding(false); - setElegantTextHeight(false); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + setElegantTextHeight(false); + } } @Override diff --git a/app/src/main/java/io/plaidapp/ui/widget/BottomSheet.java b/app/src/main/java/io/plaidapp/ui/widget/BottomSheet.java index 83383b660..0b5d08123 100644 --- a/app/src/main/java/io/plaidapp/ui/widget/BottomSheet.java +++ b/app/src/main/java/io/plaidapp/ui/widget/BottomSheet.java @@ -157,7 +157,7 @@ protected void onAttachedToWindow() { @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); - if (dragView != null && dragView.isLaidOut()) { + if (dragView != null && ViewCompat.isLaidOut(dragView)) { dragViewLeft = dragView.getLeft(); dragViewExpandedTop = dragView.getTop(); dragViewBottom = dragView.getBottom(); @@ -207,7 +207,7 @@ public void computeScroll() { @Override public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) { - return (nestedScrollAxes & View.SCROLL_AXIS_VERTICAL) != 0; + return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0; } @Override @@ -292,7 +292,7 @@ private void animateSettle(final boolean dismiss) { dragView.getTop(), settleAt); settleAnim.setDuration(200L); - settleAnim.setInterpolator(AnimUtils.getFastOutSlowInInterpolator(getContext())); + settleAnim.setInterpolator(AnimUtils.getFastOutSlowInInterpolator()); settleAnim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { diff --git a/app/src/main/java/io/plaidapp/ui/widget/CircularImageView.java b/app/src/main/java/io/plaidapp/ui/widget/CircularImageView.java index 0393a5b36..bbc039ec1 100644 --- a/app/src/main/java/io/plaidapp/ui/widget/CircularImageView.java +++ b/app/src/main/java/io/plaidapp/ui/widget/CircularImageView.java @@ -1,18 +1,34 @@ package io.plaidapp.ui.widget; +import android.annotation.TargetApi; import android.content.Context; +import android.graphics.Outline; +import android.os.Build; import android.util.AttributeSet; - -import io.plaidapp.util.ViewUtils; +import android.view.View; +import android.view.ViewOutlineProvider; /** * An extension to image view that has a circular outline. + * + * // TODO use a backport of some sort for this */ +@TargetApi(Build.VERSION_CODES.LOLLIPOP) public class CircularImageView extends ForegroundImageView { + public static final ViewOutlineProvider CIRCULAR_OUTLINE = new ViewOutlineProvider() { + @Override + public void getOutline(View view, Outline outline) { + outline.setOval(view.getPaddingLeft(), + view.getPaddingTop(), + view.getWidth() - view.getPaddingRight(), + view.getHeight() - view.getPaddingBottom()); + } + }; + public CircularImageView(Context context, AttributeSet attrs) { super(context, attrs); - setOutlineProvider(ViewUtils.CIRCULAR_OUTLINE); + setOutlineProvider(CIRCULAR_OUTLINE); setClipToOutline(true); } } diff --git a/app/src/main/java/io/plaidapp/ui/widget/CollapsingTitleLayout.java b/app/src/main/java/io/plaidapp/ui/widget/CollapsingTitleLayout.java index dda0b1c48..3f96b9f92 100644 --- a/app/src/main/java/io/plaidapp/ui/widget/CollapsingTitleLayout.java +++ b/app/src/main/java/io/plaidapp/ui/widget/CollapsingTitleLayout.java @@ -85,12 +85,17 @@ public CollapsingTitleLayout(Context context, AttributeSet attrs) { } public CollapsingTitleLayout(Context context, AttributeSet attrs, int defStyleAttr) { - this(context, attrs, defStyleAttr, 0); + super(context, attrs, defStyleAttr); + init(context, attrs); } - public CollapsingTitleLayout(Context context, AttributeSet attrs, int defStyleAttr, - int defStyleRes) { + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + public CollapsingTitleLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); + init(context, attrs); + } + + private void init(Context context, AttributeSet attrs) { setWillNotDraw(false); paint = new TextPaint(Paint.ANTI_ALIAS_FLAG); @@ -219,9 +224,9 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { } private int getDesiredHeight() { - if (layout == null) return getMinimumHeight(); + if (layout == null) return ViewCompat.getMinimumHeight(this); return Math.max( - (int) (titleInsetTop + layout.getHeight() + titleInsetBottom), getMinimumHeight()); + (int) (titleInsetTop + layout.getHeight() + titleInsetBottom), ViewCompat.getMinimumHeight(this)); } private void recalculate(int width) { @@ -260,7 +265,7 @@ private void recalculate(int width) { collapsingText.setExpandedBounds(titleInsetStart, (int) titleInsetTop, width - titleInsetEnd, - getMinimumHeight() - titleInsetBottom); + ViewCompat.getMinimumHeight(this) - titleInsetBottom); collapsingText.setCollapsedTextColor(paint.getColor()); collapsingText.setExpandedTextColor(paint.getColor()); collapsingText.setCollapsedTextSize(collapsedTextSize); @@ -279,7 +284,7 @@ private void recalculate(int width) { fm = paint.getFontMetricsInt(); fontHeight = Math.abs(fm.ascent - fm.descent) + fm.leading; textTop = getHeight() - titleInsetBottom - fontHeight; - scrollRange = getMinimumHeight() - (int) collapsedHeight; + scrollRange = ViewCompat.getMinimumHeight(this) - (int) collapsedHeight; } else { // multi-line mode // bottom align the text textTop = getDesiredHeight() - titleInsetBottom - layout.getHeight(); diff --git a/app/src/main/java/io/plaidapp/ui/widget/DynamicTextView.java b/app/src/main/java/io/plaidapp/ui/widget/DynamicTextView.java index be7adc3fd..392911aa3 100644 --- a/app/src/main/java/io/plaidapp/ui/widget/DynamicTextView.java +++ b/app/src/main/java/io/plaidapp/ui/widget/DynamicTextView.java @@ -20,6 +20,9 @@ import android.content.res.TypedArray; import android.graphics.Color; import android.graphics.Typeface; +import android.os.Build; +import android.support.v4.view.GravityCompat; +import android.support.v4.widget.TextViewCompat; import android.text.Layout; import android.text.StaticLayout; import android.text.TextPaint; @@ -87,12 +90,12 @@ private void fitText() { // different methods for achieving this depending on whether we are snapping to the material // scale, and if multiple lines are allowed. 4 method for the permutations of this. - if (mSnapToMaterialScale && getMaxLines() == 1) { + if (mSnapToMaterialScale && TextViewCompat.getMaxLines(this) == 1) { // technically we could use the multi line algorithm here but this is more efficient fitSnappedSingleLine(); } else if (mSnapToMaterialScale) { fitSnappedMultiLine(); - } else if (!mSnapToMaterialScale && getMaxLines() == 1) { + } else if (!mSnapToMaterialScale && TextViewCompat.getMaxLines(this) == 1) { fitSingleLine(); } else if (!mSnapToMaterialScale) { fitMultiline(); @@ -109,9 +112,9 @@ private void fitSnappedMultiLine() { StaticLayout staticLayout = null; int currentHeight = Integer.MAX_VALUE; int lines = 0; - boolean maxLinesSet = getMaxLines() != Integer.MAX_VALUE; + boolean maxLinesSet = TextViewCompat.getMaxLines(this) != Integer.MAX_VALUE; - while ((currentHeight > targetHeight || (maxLinesSet && lines > getMaxLines())) + while ((currentHeight > targetHeight || (maxLinesSet && lines > TextViewCompat.getMaxLines(this))) && style <= mStyles.length - 1 && currentStyle.size * scaledDensity >= mMinTextSize && currentStyle.size * scaledDensity <= mMaxTextSize) { @@ -142,11 +145,19 @@ private void fitSnappedMultiLine() { lines))); setEllipsize(TextUtils.TruncateAt.END); } - setTextAlignment(TEXT_ALIGNMENT_TEXT_START); + setStartAlignment(); mCalculated = true; } } + private void setStartAlignment() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + setTextAlignment(TEXT_ALIGNMENT_TEXT_START); + } else { + setGravity(GravityCompat.START); + } + } + private void fitSnappedSingleLine() { int targetWidth = getWidth() - getPaddingLeft() - getPaddingRight(); if (targetWidth > 0) { @@ -198,7 +209,7 @@ private void fitMultiline() { currentHeight = staticLayout.getHeight(); } setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize); - setTextAlignment(TEXT_ALIGNMENT_TEXT_START); + setStartAlignment(); mCalculated = true; } } diff --git a/app/src/main/java/io/plaidapp/ui/widget/ElasticDragDismissFrameLayout.java b/app/src/main/java/io/plaidapp/ui/widget/ElasticDragDismissFrameLayout.java index 5bb6e3dfc..d6f80ff85 100644 --- a/app/src/main/java/io/plaidapp/ui/widget/ElasticDragDismissFrameLayout.java +++ b/app/src/main/java/io/plaidapp/ui/widget/ElasticDragDismissFrameLayout.java @@ -16,8 +16,10 @@ package io.plaidapp.ui.widget; +import android.annotation.TargetApi; import android.content.Context; import android.content.res.TypedArray; +import android.os.Build; import android.util.AttributeSet; import android.view.View; import android.view.Window; @@ -61,13 +63,18 @@ public ElasticDragDismissFrameLayout(Context context, AttributeSet attrs) { public ElasticDragDismissFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) { - this(context, attrs, defStyleAttr, 0); + super(context, attrs, defStyleAttr); + init(attrs); } + @TargetApi(Build.VERSION_CODES.LOLLIPOP) public ElasticDragDismissFrameLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); + init(attrs); + } + private void init(AttributeSet attrs) { final TypedArray a = getContext().obtainStyledAttributes( attrs, R.styleable.ElasticDragDismissFrameLayout, 0, 0); @@ -143,7 +150,7 @@ public void onStopNestedScroll(View child) { .scaleX(1f) .scaleY(1f) .setDuration(200L) - .setInterpolator(AnimUtils.getFastOutSlowInInterpolator(getContext())) + .setInterpolator(AnimUtils.getFastOutSlowInInterpolator()) .setListener(null) .start(); totalDrag = 0; @@ -248,28 +255,32 @@ private void dispatchDismissCallback() { public static abstract class SystemChromeFader implements ElasticDragDismissListener { private Window window; + private static final boolean SHOULD_OP = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; public SystemChromeFader(Window window) { this.window = window; } + @TargetApi(Build.VERSION_CODES.LOLLIPOP) @Override public void onDrag(float elasticOffset, float elasticOffsetPixels, float rawOffset, float rawOffsetPixels) { - if (elasticOffsetPixels < 0) { - // dragging upward, fade the navigation bar in proportion - // TODO don't fade nav bar on landscape phones? - window.setNavigationBarColor(ColorUtils.modifyAlpha(window.getNavigationBarColor(), - 1f - rawOffset)); - } else if (elasticOffsetPixels == 0) { - // reset - window.setStatusBarColor(ColorUtils.modifyAlpha(window.getStatusBarColor(), 1f)); - window.setNavigationBarColor( - ColorUtils.modifyAlpha(window.getNavigationBarColor(), 1f)); - } else { - // dragging downward, fade the status bar in proportion - window.setStatusBarColor(ColorUtils.modifyAlpha(window - .getStatusBarColor(), 1f - rawOffset)); + if (SHOULD_OP) { + if (elasticOffsetPixels < 0) { + // dragging upward, fade the navigation bar in proportion + // TODO don't fade nav bar on landscape phones? + window.setNavigationBarColor(ColorUtils.modifyAlpha(window.getNavigationBarColor(), + 1f - rawOffset)); + } else if (elasticOffsetPixels == 0) { + // reset + window.setStatusBarColor(ColorUtils.modifyAlpha(window.getStatusBarColor(), 1f)); + window.setNavigationBarColor( + ColorUtils.modifyAlpha(window.getNavigationBarColor(), 1f)); + } else { + // dragging downward, fade the status bar in proportion + window.setStatusBarColor(ColorUtils.modifyAlpha(window + .getStatusBarColor(), 1f - rawOffset)); + } } } diff --git a/app/src/main/java/io/plaidapp/ui/widget/FontTextView.java b/app/src/main/java/io/plaidapp/ui/widget/FontTextView.java index 693a1e0a4..f164843b2 100644 --- a/app/src/main/java/io/plaidapp/ui/widget/FontTextView.java +++ b/app/src/main/java/io/plaidapp/ui/widget/FontTextView.java @@ -16,9 +16,11 @@ package io.plaidapp.ui.widget; +import android.annotation.TargetApi; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Paint; +import android.os.Build; import android.util.AttributeSet; import android.widget.TextView; @@ -46,6 +48,7 @@ public FontTextView(Context context, AttributeSet attrs, int defStyleAttr) { init(context, attrs); } + @TargetApi(Build.VERSION_CODES.LOLLIPOP) public FontTextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); init(context, attrs); diff --git a/app/src/main/java/io/plaidapp/ui/widget/ForegroundImageView.java b/app/src/main/java/io/plaidapp/ui/widget/ForegroundImageView.java index 15e20dbb1..4d09ef1c4 100644 --- a/app/src/main/java/io/plaidapp/ui/widget/ForegroundImageView.java +++ b/app/src/main/java/io/plaidapp/ui/widget/ForegroundImageView.java @@ -20,6 +20,8 @@ import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.drawable.Drawable; +import android.os.Build; +import android.support.v4.graphics.drawable.DrawableCompat; import android.util.AttributeSet; import android.view.ViewOutlineProvider; import android.widget.ImageView; @@ -43,7 +45,10 @@ public ForegroundImageView(Context context, AttributeSet attrs) { setForeground(d); } a.recycle(); - setOutlineProvider(ViewOutlineProvider.BOUNDS); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + setOutlineProvider(ViewOutlineProvider.BOUNDS); + } } @Override @@ -128,7 +133,7 @@ public void draw(Canvas canvas) { public void drawableHotspotChanged(float x, float y) { super.drawableHotspotChanged(x, y); if (foreground != null) { - foreground.setHotspot(x, y); + DrawableCompat.setHotspot(foreground, x, y); } } } diff --git a/app/src/main/java/io/plaidapp/ui/widget/ForegroundLinearLayout.java b/app/src/main/java/io/plaidapp/ui/widget/ForegroundLinearLayout.java index b150f76a8..816b037dd 100644 --- a/app/src/main/java/io/plaidapp/ui/widget/ForegroundLinearLayout.java +++ b/app/src/main/java/io/plaidapp/ui/widget/ForegroundLinearLayout.java @@ -21,6 +21,7 @@ import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.support.v4.graphics.drawable.DrawableCompat; import android.util.AttributeSet; import android.view.Gravity; import android.widget.LinearLayout; @@ -218,7 +219,7 @@ public void draw(Canvas canvas) { public void drawableHotspotChanged(float x, float y) { super.drawableHotspotChanged(x, y); if (mForeground != null) { - mForeground.setHotspot(x, y); + DrawableCompat.setHotspot(mForeground, x, y); } } } diff --git a/app/src/main/java/io/plaidapp/ui/widget/ForegroundRelativeLayout.java b/app/src/main/java/io/plaidapp/ui/widget/ForegroundRelativeLayout.java index 7ddcf65f6..e326b307c 100644 --- a/app/src/main/java/io/plaidapp/ui/widget/ForegroundRelativeLayout.java +++ b/app/src/main/java/io/plaidapp/ui/widget/ForegroundRelativeLayout.java @@ -20,6 +20,8 @@ import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.drawable.Drawable; +import android.os.Build; +import android.support.v4.graphics.drawable.DrawableCompat; import android.util.AttributeSet; import android.view.ViewOutlineProvider; import android.widget.RelativeLayout; @@ -43,7 +45,9 @@ public ForegroundRelativeLayout(Context context, AttributeSet attrs) { setForeground(d); } a.recycle(); - setOutlineProvider(ViewOutlineProvider.BOUNDS); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + setOutlineProvider(ViewOutlineProvider.BOUNDS); + } } @Override @@ -131,7 +135,7 @@ public void draw(Canvas canvas) { public void drawableHotspotChanged(float x, float y) { super.drawableHotspotChanged(x, y); if (foreground != null) { - foreground.setHotspot(x, y); + DrawableCompat.setHotspot(foreground, x, y); } } } diff --git a/app/src/main/java/io/plaidapp/ui/widget/InkPageIndicator.java b/app/src/main/java/io/plaidapp/ui/widget/InkPageIndicator.java index 55933316b..d9c45de3c 100644 --- a/app/src/main/java/io/plaidapp/ui/widget/InkPageIndicator.java +++ b/app/src/main/java/io/plaidapp/ui/widget/InkPageIndicator.java @@ -27,6 +27,7 @@ import android.graphics.Paint; import android.graphics.Path; import android.graphics.RectF; +import android.support.v4.view.ViewCompat; import android.support.v4.view.ViewPager; import android.util.AttributeSet; import android.util.Log; @@ -150,7 +151,7 @@ public InkPageIndicator(Context context, AttributeSet attrs, int defStyle) { unselectedPaint.setColor(unselectedColour); selectedPaint = new Paint(Paint.ANTI_ALIAS_FLAG); selectedPaint.setColor(selectedColour); - interpolator = AnimUtils.getFastOutSlowInInterpolator(context); + interpolator = AnimUtils.getFastOutSlowInInterpolator(); // create paths & rect now – reuse & rewind later combinedUnselectedPath = new Path(); @@ -602,7 +603,7 @@ public void onAnimationUpdate(ValueAnimator valueAnimator) { // todo avoid autoboxing selectedDotX = (Float) valueAnimator.getAnimatedValue(); retreatAnimation.startIfNecessary(selectedDotX); - postInvalidateOnAnimation(); + ViewCompat.postInvalidateOnAnimation(InkPageIndicator.this); } }); moveSelected.addListener(new AnimatorListenerAdapter() { @@ -636,18 +637,18 @@ private void setJoiningFraction(int leftDot, float fraction) { } joiningFractions[leftDot] = fraction; - postInvalidateOnAnimation(); + ViewCompat.postInvalidateOnAnimation(this); } } private void clearJoiningFractions() { Arrays.fill(joiningFractions, 0f); - postInvalidateOnAnimation(); + ViewCompat.postInvalidateOnAnimation(this); } private void setDotRevealFraction(int dot, float fraction) { dotRevealFractions[dot] = fraction; - postInvalidateOnAnimation(); + ViewCompat.postInvalidateOnAnimation(this); } private void cancelJoiningAnimations() { @@ -720,7 +721,7 @@ public PendingRetreatAnimator(int was, int now, int steps, StartPredicate predic public void onAnimationUpdate(ValueAnimator valueAnimator) { // todo avoid autoboxing retreatingJoinX1 = (Float) valueAnimator.getAnimatedValue(); - postInvalidateOnAnimation(); + ViewCompat.postInvalidateOnAnimation(InkPageIndicator.this); // start any reveal animations if we've passed them for (PendingRevealAnimator pendingReveal : revealAnimations) { pendingReveal.startIfNecessary(retreatingJoinX1); @@ -740,7 +741,7 @@ public void onAnimationUpdate(ValueAnimator valueAnimator) { public void onAnimationUpdate(ValueAnimator valueAnimator) { // todo avoid autoboxing retreatingJoinX2 = (Float) valueAnimator.getAnimatedValue(); - postInvalidateOnAnimation(); + ViewCompat.postInvalidateOnAnimation(InkPageIndicator.this); // start any reveal animations if we've passed them for (PendingRevealAnimator pendingReveal : revealAnimations) { pendingReveal.startIfNecessary(retreatingJoinX2); @@ -760,13 +761,13 @@ public void onAnimationStart(Animator animation) { } retreatingJoinX1 = initialX1; retreatingJoinX2 = initialX2; - postInvalidateOnAnimation(); + ViewCompat.postInvalidateOnAnimation(InkPageIndicator.this); } @Override public void onAnimationEnd(Animator animation) { retreatingJoinX1 = INVALID_FRACTION; retreatingJoinX2 = INVALID_FRACTION; - postInvalidateOnAnimation(); + ViewCompat.postInvalidateOnAnimation(InkPageIndicator.this); } }); } @@ -797,7 +798,7 @@ public void onAnimationUpdate(ValueAnimator valueAnimator) { @Override public void onAnimationEnd(Animator animation) { setDotRevealFraction(PendingRevealAnimator.this.dot, 0f); - postInvalidateOnAnimation(); + ViewCompat.postInvalidateOnAnimation(InkPageIndicator.this); } }); } diff --git a/app/src/main/java/io/plaidapp/ui/widget/ParallaxScrimageView.java b/app/src/main/java/io/plaidapp/ui/widget/ParallaxScrimageView.java index 079bdf07d..18617acae 100644 --- a/app/src/main/java/io/plaidapp/ui/widget/ParallaxScrimageView.java +++ b/app/src/main/java/io/plaidapp/ui/widget/ParallaxScrimageView.java @@ -95,7 +95,7 @@ public void setOffset(float offset) { if (offset != getTranslationY()) { setTranslationY(offset); imageOffset = (int) (offset * parallaxFactor); - setScrimAlpha(Math.min((-offset / getMinimumHeight()) * maxScrimAlpha, maxScrimAlpha)); + setScrimAlpha(Math.min((-offset / ViewCompat.getMinimumHeight(this)) * maxScrimAlpha, maxScrimAlpha)); ViewCompat.postInvalidateOnAnimation(this); } setPinned(offset == minOffset); @@ -119,8 +119,8 @@ public void setScrimAlpha(@FloatRange(from = 0f, to = 1f) float alpha) { @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); - if (h > getMinimumHeight()) { - minOffset = getMinimumHeight() - h; + if (h > ViewCompat.getMinimumHeight(this)) { + minOffset = ViewCompat.getMinimumHeight(this) - h; } } diff --git a/app/src/main/java/io/plaidapp/ui/widget/PinnedOffsetView.java b/app/src/main/java/io/plaidapp/ui/widget/PinnedOffsetView.java index d044ce8b2..444b8e757 100644 --- a/app/src/main/java/io/plaidapp/ui/widget/PinnedOffsetView.java +++ b/app/src/main/java/io/plaidapp/ui/widget/PinnedOffsetView.java @@ -16,7 +16,9 @@ package io.plaidapp.ui.widget; +import android.annotation.TargetApi; import android.content.Context; +import android.os.Build; import android.support.v4.view.ViewCompat; import android.util.AttributeSet; import android.view.View; @@ -42,6 +44,7 @@ public PinnedOffsetView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } + @TargetApi(Build.VERSION_CODES.LOLLIPOP) public PinnedOffsetView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); @@ -59,7 +62,7 @@ public void setOffset(int offset) { @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); - minOffset = -(h - getMinimumHeight()); + minOffset = -(h - ViewCompat.getMinimumHeight(this)); } @Override diff --git a/app/src/main/java/io/plaidapp/util/AnimUtils.java b/app/src/main/java/io/plaidapp/util/AnimUtils.java index f5806c3b5..5e69d4885 100644 --- a/app/src/main/java/io/plaidapp/util/AnimUtils.java +++ b/app/src/main/java/io/plaidapp/util/AnimUtils.java @@ -18,11 +18,14 @@ import android.animation.Animator; import android.animation.TimeInterpolator; -import android.content.Context; +import android.annotation.TargetApi; +import android.os.Build; +import android.support.v4.util.ArrayMap; +import android.support.v4.view.animation.FastOutLinearInInterpolator; +import android.support.v4.view.animation.FastOutSlowInInterpolator; +import android.support.v4.view.animation.LinearOutSlowInInterpolator; import android.transition.Transition; -import android.util.ArrayMap; import android.util.Property; -import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; import java.util.ArrayList; @@ -38,26 +41,23 @@ private AnimUtils() { } private static Interpolator fastOutLinearIn; private static Interpolator linearOutSlowIn; - public static Interpolator getFastOutSlowInInterpolator(Context context) { + public static Interpolator getFastOutSlowInInterpolator() { if (fastOutSlowIn == null) { - fastOutSlowIn = AnimationUtils.loadInterpolator(context, - android.R.interpolator.fast_out_slow_in); + fastOutSlowIn = new FastOutSlowInInterpolator(); } return fastOutSlowIn; } - public static Interpolator getFastOutLinearInInterpolator(Context context) { + public static Interpolator getFastOutLinearInInterpolator() { if (fastOutLinearIn == null) { - fastOutLinearIn = AnimationUtils.loadInterpolator(context, - android.R.interpolator.fast_out_linear_in); + fastOutLinearIn = new FastOutLinearInInterpolator(); } return fastOutLinearIn; } - public static Interpolator getLinearOutSlowInInterpolator(Context context) { + public static Interpolator getLinearOutSlowInInterpolator() { if (linearOutSlowIn == null) { - linearOutSlowIn = AnimationUtils.loadInterpolator(context, - android.R.interpolator.linear_out_slow_in); + linearOutSlowIn = new LinearOutSlowInInterpolator(); } return linearOutSlowIn; } @@ -165,6 +165,7 @@ public long getDuration() { return mAnimator.getDuration(); } + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) @Override public TimeInterpolator getInterpolator() { return mAnimator.getInterpolator(); @@ -190,6 +191,7 @@ public void setStartDelay(long delayMS) { mAnimator.setStartDelay(delayMS); } + @TargetApi(Build.VERSION_CODES.KITKAT) @Override public boolean isPaused() { return mAnimator.isPaused(); @@ -288,6 +290,7 @@ public void onAnimationRepeat(Animator animator) { } } + @TargetApi(Build.VERSION_CODES.KITKAT) public static class TransitionListenerAdapter implements Transition.TransitionListener { @Override diff --git a/app/src/main/java/io/plaidapp/util/DribbbleUtils.java b/app/src/main/java/io/plaidapp/util/DribbbleUtils.java index e9999c47c..06be4e931 100644 --- a/app/src/main/java/io/plaidapp/util/DribbbleUtils.java +++ b/app/src/main/java/io/plaidapp/util/DribbbleUtils.java @@ -9,6 +9,7 @@ import in.uncod.android.bypass.style.TouchableUrlSpan; import io.plaidapp.ui.span.PlayerSpan; +import io.plaidapp.util.compat.LocalTextViewCompat; import okhttp3.HttpUrl; public class DribbbleUtils { @@ -55,6 +56,6 @@ public static Spanned parseDribbbleHtml(String input, public static void parseAndSetText(TextView textView, String input) { if (TextUtils.isEmpty(input)) return; HtmlUtils.setTextWithNiceLinks(textView, parseDribbbleHtml(input, - textView.getLinkTextColors(), textView.getHighlightColor())); + textView.getLinkTextColors(), LocalTextViewCompat.getHighlightColor(textView))); } } diff --git a/app/src/main/java/io/plaidapp/util/HtmlUtils.java b/app/src/main/java/io/plaidapp/util/HtmlUtils.java index ccf556951..522776582 100644 --- a/app/src/main/java/io/plaidapp/util/HtmlUtils.java +++ b/app/src/main/java/io/plaidapp/util/HtmlUtils.java @@ -29,6 +29,7 @@ import in.uncod.android.bypass.Bypass; import in.uncod.android.bypass.style.TouchableUrlSpan; +import io.plaidapp.util.compat.LocalTextViewCompat; /** * Utility methods for working with HTML. @@ -81,7 +82,7 @@ public static SpannableStringBuilder parseHtml(String input, public static void parseAndSetText(TextView textView, String input) { if (TextUtils.isEmpty(input)) return; setTextWithNiceLinks(textView, parseHtml(input, textView.getLinkTextColors(), - textView.getHighlightColor())); + LocalTextViewCompat.getHighlightColor(textView))); } /** @@ -110,7 +111,7 @@ public static CharSequence parseMarkdownAndPlainLinks( for (URLSpan urlSpan : urlSpans) { ssb.setSpan(new TouchableUrlSpan(urlSpan.getURL(), textView.getLinkTextColors(), - textView.getHighlightColor()), + LocalTextViewCompat.getHighlightColor(textView)), plainLinks.getSpanStart(urlSpan), plainLinks.getSpanEnd(urlSpan), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); diff --git a/app/src/main/java/io/plaidapp/util/ImageUtils.java b/app/src/main/java/io/plaidapp/util/ImageUtils.java index 42a282dcd..5d383ef3c 100644 --- a/app/src/main/java/io/plaidapp/util/ImageUtils.java +++ b/app/src/main/java/io/plaidapp/util/ImageUtils.java @@ -21,6 +21,7 @@ import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.support.annotation.DrawableRes; +import android.support.v4.content.ContextCompat; /** * Utility methods for working with images. @@ -40,6 +41,6 @@ public static Bitmap vectorToBitmap(Context context, Drawable vector) { } public static Bitmap vectorToBitmap(Context context, @DrawableRes int vectorDrawableId) { - return vectorToBitmap(context, context.getDrawable(vectorDrawableId)); + return vectorToBitmap(context, ContextCompat.getDrawable(context, vectorDrawableId)); } } diff --git a/app/src/main/java/io/plaidapp/util/MathUtils.java b/app/src/main/java/io/plaidapp/util/MathUtils.java index 26486a2a7..0be8abbdb 100644 --- a/app/src/main/java/io/plaidapp/util/MathUtils.java +++ b/app/src/main/java/io/plaidapp/util/MathUtils.java @@ -16,6 +16,9 @@ package io.plaidapp.util; +import android.support.annotation.FloatRange; +import android.support.annotation.IntRange; + /** * Borrowed from github.com/romannurik/muzei */ @@ -26,4 +29,18 @@ private MathUtils() { } public static float constrain(float min, float max, float v) { return Math.max(min, Math.min(max, v)); } + + /** + * Given the float value of an int (such as alpha), + * + * @param alpha + * @return + */ + public static int shiftedIntFloatToByteInt(@FloatRange(from = 0f, to = 1f) float alpha) { + return (int) (255f * alpha); + } + + public static float shiftedByteIntToIntFloat(@IntRange(from = 0, to = 255) int alpha) { + return (float) (alpha / 255); + } } diff --git a/app/src/main/java/io/plaidapp/util/ViewUtils.java b/app/src/main/java/io/plaidapp/util/ViewUtils.java index 124bbfba8..85216fe37 100644 --- a/app/src/main/java/io/plaidapp/util/ViewUtils.java +++ b/app/src/main/java/io/plaidapp/util/ViewUtils.java @@ -16,10 +16,10 @@ package io.plaidapp.util; +import android.annotation.TargetApi; import android.content.Context; import android.content.res.ColorStateList; import android.graphics.Color; -import android.graphics.Outline; import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; @@ -28,16 +28,20 @@ import android.support.annotation.ColorInt; import android.support.annotation.FloatRange; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.view.ViewCompat; import android.support.v7.graphics.Palette; import android.text.TextPaint; +import android.transition.ChangeBounds; import android.util.DisplayMetrics; import android.util.Property; import android.util.TypedValue; import android.view.View; -import android.view.ViewOutlineProvider; import android.widget.ImageView; import android.widget.TextView; +import io.plaidapp.util.compat.ImageViewCompat; + /** * Utility methods for working with Views. */ @@ -57,6 +61,7 @@ public static int getActionBarSize(Context context) { return actionBarSize; } + @TargetApi(Build.VERSION_CODES.LOLLIPOP) public static RippleDrawable createRipple(@ColorInt int color, @FloatRange(from = 0f, to = 1f) float alpha, boolean bounded) { @@ -65,6 +70,7 @@ public static RippleDrawable createRipple(@ColorInt int color, bounded ? new ColorDrawable(Color.WHITE) : null); } + @TargetApi(Build.VERSION_CODES.LOLLIPOP) public static RippleDrawable createRipple(@NonNull Palette palette, @FloatRange(from = 0f, to = 1f) float darkAlpha, @FloatRange(from = 0f, to = 1f) float lightAlpha, @@ -159,22 +165,12 @@ public Integer get(View view) { @Override public void setValue(ImageView imageView, int value) { - imageView.setImageAlpha(value); + ImageViewCompat.setImageAlpha(imageView, value); } @Override public Integer get(ImageView imageView) { - return imageView.getImageAlpha(); - } - }; - - public static final ViewOutlineProvider CIRCULAR_OUTLINE = new ViewOutlineProvider() { - @Override - public void getOutline(View view, Outline outline) { - outline.setOval(view.getPaddingLeft(), - view.getPaddingTop(), - view.getWidth() - view.getPaddingRight(), - view.getHeight() - view.getPaddingBottom()); + return ImageViewCompat.getImageAlpha(imageView); } }; @@ -205,7 +201,7 @@ public void setValue(TextView view, float textSize) { new AnimUtils.IntProperty("paddingStart") { @Override public Integer get(TextView view) { - return view.getPaddingStart(); + return ViewCompat.getPaddingStart(view); } @Override @@ -236,31 +232,44 @@ public static boolean viewsIntersect(View view1, View view2) { } public static void setPaddingStart(View view, int paddingStart) { - view.setPaddingRelative(paddingStart, + ViewCompat.setPaddingRelative(view, + paddingStart, view.getPaddingTop(), - view.getPaddingEnd(), + ViewCompat.getPaddingEnd(view), view.getPaddingBottom()); } public static void setPaddingTop(View view, int paddingTop) { - view.setPaddingRelative(view.getPaddingStart(), + ViewCompat.setPaddingRelative(view, + ViewCompat.getPaddingStart(view), paddingTop, - view.getPaddingEnd(), + ViewCompat.getPaddingEnd(view), view.getPaddingBottom()); } public static void setPaddingEnd(View view, int paddingEnd) { - view.setPaddingRelative(view.getPaddingStart(), + ViewCompat.setPaddingRelative(view, + ViewCompat.getPaddingStart(view), view.getPaddingTop(), paddingEnd, view.getPaddingBottom()); } public static void setPaddingBottom(View view, int paddingBottom) { - view.setPaddingRelative(view.getPaddingStart(), + ViewCompat.setPaddingRelative(view, + ViewCompat.getPaddingStart(view), view.getPaddingTop(), - view.getPaddingEnd(), + ViewCompat.getPaddingEnd(view), paddingBottom); } + public static void setBackground(@NonNull View view, @Nullable Drawable drawable) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + view.setBackground(drawable); + } else { + //noinspection deprecation + view.setBackgroundDrawable(drawable); + } + } + } diff --git a/app/src/main/java/io/plaidapp/util/compat/CanvasCompat.java b/app/src/main/java/io/plaidapp/util/compat/CanvasCompat.java new file mode 100644 index 000000000..fe52f897f --- /dev/null +++ b/app/src/main/java/io/plaidapp/util/compat/CanvasCompat.java @@ -0,0 +1,27 @@ +package io.plaidapp.util.compat; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.RectF; +import android.os.Build; +import android.support.annotation.NonNull; + +/** + * API compatibility functions for {@link Canvas} supporting API 15+ + */ +public final class CanvasCompat { + + private CanvasCompat() { + throw new AssertionError("No instances."); + } + + public static void drawRoundRect( + @NonNull Canvas canvas, float left, float top, float right, float bottom, float rx, + float ry, @NonNull Paint paint) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + canvas.drawRoundRect(left, top, right, bottom, rx, ry, paint); + } else { + canvas.drawRoundRect(new RectF(left, top, right, bottom), rx, ry, paint); + } + } +} diff --git a/app/src/main/java/io/plaidapp/util/compat/ImageViewCompat.java b/app/src/main/java/io/plaidapp/util/compat/ImageViewCompat.java new file mode 100644 index 000000000..971dc2d7b --- /dev/null +++ b/app/src/main/java/io/plaidapp/util/compat/ImageViewCompat.java @@ -0,0 +1,43 @@ +package io.plaidapp.util.compat; + +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.support.annotation.IntRange; +import android.support.annotation.NonNull; +import android.widget.ImageView; + +public final class ImageViewCompat { + + private ImageViewCompat() { + throw new AssertionError("No instances."); + } + + public static void setImageAlpha(@NonNull ImageView imageView, @IntRange(from = 0, to = 255) int alpha) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + imageView.setImageAlpha(alpha); + } else { + // Not ideal but better than nothing + Drawable imageDrawable = imageView.getDrawable(); + if (imageDrawable != null) { + imageDrawable = imageDrawable.mutate(); + imageDrawable.setAlpha(alpha); + imageView.setImageDrawable(imageDrawable); + } + } + } + + @IntRange(from = 0, to = 255) + public static int getImageAlpha(@NonNull ImageView imageView) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + return imageView.getImageAlpha(); + } else { + // Not ideal but better than nothing + Drawable imageDrawable = imageView.getDrawable(); + if (imageDrawable != null) { + return imageDrawable.getAlpha(); + } else { + return 255; + } + } + } +} diff --git a/app/src/main/java/io/plaidapp/util/compat/LocalTextViewCompat.java b/app/src/main/java/io/plaidapp/util/compat/LocalTextViewCompat.java new file mode 100644 index 000000000..2fddaf999 --- /dev/null +++ b/app/src/main/java/io/plaidapp/util/compat/LocalTextViewCompat.java @@ -0,0 +1,32 @@ +package io.plaidapp.util.compat; + +import android.os.Build; +import android.support.annotation.ColorInt; +import android.support.annotation.ColorRes; +import android.support.annotation.NonNull; +import android.support.v4.content.ContextCompat; +import android.widget.TextView; + +/** + * Local TextView compat functions that don't exist in {@link android.support.v4.widget.TextViewCompat} + */ +public final class LocalTextViewCompat { + + private LocalTextViewCompat() { + throw new AssertionError("No instances."); + } + + @ColorInt + public static int getHighlightColor(@NonNull TextView textView) { + return getHighlightColor(textView, android.R.color.transparent); + } + + @ColorInt + public static int getHighlightColor(@NonNull TextView textView, @ColorRes int defaultColor) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + return textView.getHighlightColor(); + } else { + return ContextCompat.getColor(textView.getContext(), defaultColor); + } + } +} diff --git a/app/src/main/java/io/plaidapp/util/compat/ObjectAnimatorCompat.java b/app/src/main/java/io/plaidapp/util/compat/ObjectAnimatorCompat.java new file mode 100644 index 000000000..600f56986 --- /dev/null +++ b/app/src/main/java/io/plaidapp/util/compat/ObjectAnimatorCompat.java @@ -0,0 +1,92 @@ +package io.plaidapp.util.compat; + +import android.animation.ArgbEvaluator; +import android.animation.ObjectAnimator; +import android.animation.PropertyValuesHolder; +import android.graphics.Path; +import android.graphics.PathMeasure; +import android.os.Build; +import android.support.annotation.Size; +import android.util.Property; + +/** + * Most or all of these are from From https://github.com/DreaminginCodeZH/MaterialProgressBar + */ +public final class ObjectAnimatorCompat { + + private static final int NUM_POINTS = 500; + + private ObjectAnimatorCompat() { + throw new AssertionError("No instances."); + } + + public static ObjectAnimator ofArgb(Object target, String propertyName, int... values) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + return ObjectAnimator.ofArgb(target, propertyName, values); + } else { + ObjectAnimator animator = ObjectAnimator.ofInt(target, propertyName, values); + animator.setEvaluator(new ArgbEvaluator()); + return animator; + } + } + + public static ObjectAnimator ofArgb(T target, Property property, + int... values) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + return ObjectAnimator.ofArgb(target, property, values); + } else { + ObjectAnimator animator = ObjectAnimator.ofInt(target, property, values); + animator.setEvaluator(new ArgbEvaluator()); + return animator; + } + } + + public static ObjectAnimator ofFloat(Object target, String xPropertyName, String yPropertyName, + Path path) { + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + return ObjectAnimator.ofFloat(target, xPropertyName, yPropertyName, path); + } else { + float[] xValues = new float[NUM_POINTS]; + float[] yValues = new float[NUM_POINTS]; + calculateXYValues(path, xValues, yValues); + + PropertyValuesHolder xPvh = PropertyValuesHolder.ofFloat(xPropertyName, xValues); + PropertyValuesHolder yPvh = PropertyValuesHolder.ofFloat(yPropertyName, yValues); + + return ObjectAnimator.ofPropertyValuesHolder(target, xPvh, yPvh); + } + } + + public static ObjectAnimator ofFloat(T target, Property xProperty, + Property yProperty, Path path) { + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + return ObjectAnimator.ofFloat(target, xProperty, yProperty, path); + } else { + float[] xValues = new float[NUM_POINTS]; + float[] yValues = new float[NUM_POINTS]; + calculateXYValues(path, xValues, yValues); + + PropertyValuesHolder xPvh = PropertyValuesHolder.ofFloat(xProperty, xValues); + PropertyValuesHolder yPvh = PropertyValuesHolder.ofFloat(yProperty, yValues); + + return ObjectAnimator.ofPropertyValuesHolder(target, xPvh, yPvh); + } + } + + private static void calculateXYValues(Path path, @Size(NUM_POINTS) float[] xValues, + @Size(NUM_POINTS) float[] yValues) { + + PathMeasure pathMeasure = new PathMeasure(path, false /* forceClosed */); + float pathLength = pathMeasure.getLength(); + + float[] position = new float[2]; + for (int i = 0; i < NUM_POINTS; ++i) { + float distance = (i * pathLength) / (NUM_POINTS - 1); + pathMeasure.getPosTan(distance, position, null /* tangent */); + xValues[i] = position[0]; + yValues[i] = position[1]; + } + } +} diff --git a/app/src/main/java/io/plaidapp/util/compat/TransitionManagerCompat.java b/app/src/main/java/io/plaidapp/util/compat/TransitionManagerCompat.java new file mode 100644 index 000000000..b9828de4a --- /dev/null +++ b/app/src/main/java/io/plaidapp/util/compat/TransitionManagerCompat.java @@ -0,0 +1,25 @@ +package io.plaidapp.util.compat; + +import android.os.Build; +import android.transition.Transition; +import android.transition.TransitionManager; +import android.view.ViewGroup; + +public final class TransitionManagerCompat { + + private TransitionManagerCompat() { + throw new AssertionError("No instances."); + } + + public static void beginDelayedTransition(ViewGroup sceneRoot) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + TransitionManager.beginDelayedTransition(sceneRoot); + } + } + + public static void beginDelayedTransition(ViewGroup sceneRoot, Transition transition) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + TransitionManager.beginDelayedTransition(sceneRoot, transition); + } + } +} diff --git a/app/src/main/java/io/plaidapp/util/compat/ViewAnimationUtilsCompat.java b/app/src/main/java/io/plaidapp/util/compat/ViewAnimationUtilsCompat.java new file mode 100644 index 000000000..e2fed355f --- /dev/null +++ b/app/src/main/java/io/plaidapp/util/compat/ViewAnimationUtilsCompat.java @@ -0,0 +1,55 @@ +package io.plaidapp.util.compat; + +import android.animation.Animator; +import android.animation.TimeInterpolator; +import android.os.Build; +import android.view.View; +import android.view.ViewAnimationUtils; + +public final class ViewAnimationUtilsCompat { + + private static final Animator NOOP = new Animator() { + @Override + public long getStartDelay() { + return 0; + } + + @Override + public void setStartDelay(long startDelay) { + + } + + @Override + public Animator setDuration(long duration) { + return null; + } + + @Override + public long getDuration() { + return 0; + } + + @Override + public void setInterpolator(TimeInterpolator value) { + + } + + @Override + public boolean isRunning() { + return false; + } + }; + + private ViewAnimationUtilsCompat() { + throw new AssertionError("No instances."); + } + + public static Animator createCircularReveal(View view, + int centerX, int centerY, float startRadius, float endRadius) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + return ViewAnimationUtils.createCircularReveal(view, centerX, centerY, startRadius, endRadius); + } else { + return NOOP; + } + } +} diff --git a/app/src/main/java/io/plaidapp/util/glide/GlideConfiguration.java b/app/src/main/java/io/plaidapp/util/glide/GlideConfiguration.java index dcc94b497..46b4e60c7 100644 --- a/app/src/main/java/io/plaidapp/util/glide/GlideConfiguration.java +++ b/app/src/main/java/io/plaidapp/util/glide/GlideConfiguration.java @@ -18,6 +18,7 @@ import android.app.ActivityManager; import android.content.Context; +import android.support.v4.app.ActivityManagerCompat; import com.bumptech.glide.Glide; import com.bumptech.glide.GlideBuilder; @@ -34,7 +35,7 @@ public void applyOptions(Context context, GlideBuilder builder) { // Prefer higher quality images unless we're on a low RAM device ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); - builder.setDecodeFormat(activityManager.isLowRamDevice() ? + builder.setDecodeFormat(ActivityManagerCompat.isLowRamDevice(activityManager) ? DecodeFormat.PREFER_RGB_565 : DecodeFormat.PREFER_ARGB_8888); } diff --git a/app/src/main/java/io/plaidapp/util/glide/ImageSpanTarget.java b/app/src/main/java/io/plaidapp/util/glide/ImageSpanTarget.java index 64c0117b1..43263c1ee 100644 --- a/app/src/main/java/io/plaidapp/util/glide/ImageSpanTarget.java +++ b/app/src/main/java/io/plaidapp/util/glide/ImageSpanTarget.java @@ -21,7 +21,6 @@ import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.style.ImageSpan; -import android.transition.TransitionManager; import android.view.ViewGroup; import android.widget.TextView; @@ -31,6 +30,7 @@ import java.lang.ref.WeakReference; import in.uncod.android.bypass.style.ImageLoadingSpan; +import io.plaidapp.util.compat.TransitionManagerCompat; /** * A target that puts a downloaded image into an ImageSpan in the provided TextView. It uses a @@ -68,7 +68,7 @@ public void onResourceReady(Bitmap bitmap, GlideAnimation glideA } ssb.removeSpan(loadingSpan); // animate the change - TransitionManager.beginDelayedTransition((ViewGroup) tv.getParent()); + TransitionManagerCompat.beginDelayedTransition((ViewGroup) tv.getParent()); tv.setText(ssb); } } diff --git a/app/src/main/res/drawable/designer_news_app_bar_background.xml b/app/src/main/res/drawable/designer_news_app_bar_background.xml index bdb2c29c8..3fbc23bf8 100644 --- a/app/src/main/res/drawable/designer_news_app_bar_background.xml +++ b/app/src/main/res/drawable/designer_news_app_bar_background.xml @@ -20,6 +20,6 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:color="@color/ripple_light"> - + - \ No newline at end of file + diff --git a/app/src/main/res/drawable/designer_news_custom_tab_placeholder.xml b/app/src/main/res/drawable/designer_news_custom_tab_placeholder.xml index 1f5beb242..ba523a355 100644 --- a/app/src/main/res/drawable/designer_news_custom_tab_placeholder.xml +++ b/app/src/main/res/drawable/designer_news_custom_tab_placeholder.xml @@ -21,7 +21,7 @@ - + diff --git a/app/src/main/res/drawable/fab.xml b/app/src/main/res/drawable/fab.xml index 0d4adaeba..127ad287a 100644 --- a/app/src/main/res/drawable/fab.xml +++ b/app/src/main/res/drawable/fab.xml @@ -22,7 +22,7 @@ - + - \ No newline at end of file + diff --git a/app/src/main/res/drawable/ic_add_light.xml b/app/src/main/res/drawable/ic_add_light.xml index 7d1393901..f7d29fb22 100644 --- a/app/src/main/res/drawable/ic_add_light.xml +++ b/app/src/main/res/drawable/ic_add_light.xml @@ -22,7 +22,7 @@ android:height="24dp" android:viewportWidth="48" android:viewportHeight="48" - android:tint="?android:colorControlNormal"> + android:tint="?colorControlNormal"> + android:tint="?colorControlNormal"> - + diff --git a/app/src/main/res/layout/about_icon.xml b/app/src/main/res/layout/about_icon.xml index 59231dc75..30c14122b 100644 --- a/app/src/main/res/layout/about_icon.xml +++ b/app/src/main/res/layout/about_icon.xml @@ -50,7 +50,7 @@ android:paddingEnd="@dimen/padding_normal" android:paddingBottom="@dimen/spacing_large" android:textColorLink="@color/plaid_links" - android:textColorHighlight="?android:colorPrimary" + android:textColorHighlight="?colorPrimary" style="@style/Widget.Plaid.About" /> diff --git a/app/src/main/res/layout/about_plaid.xml b/app/src/main/res/layout/about_plaid.xml index 495fe3185..86f853712 100644 --- a/app/src/main/res/layout/about_plaid.xml +++ b/app/src/main/res/layout/about_plaid.xml @@ -33,7 +33,7 @@ android:layout_width="match_parent" android:layout_height="@dimen/about_header_height" android:text="@string/app_name" - app:foregroundColor="?android:colorPrimary" + app:foregroundColor="?colorPrimary" app:font="roboto-mono-regular" /> diff --git a/app/src/main/res/layout/activity_designer_news_login.xml b/app/src/main/res/layout/activity_designer_news_login.xml index dd810bb8c..94acbadd2 100644 --- a/app/src/main/res/layout/activity_designer_news_login.xml +++ b/app/src/main/res/layout/activity_designer_news_login.xml @@ -105,9 +105,9 @@ android:layout_height="wrap_content" android:layout_gravity="start" android:text="@string/sign_up" - android:textColor="?android:colorAccent" + android:textColor="?colorAccent" android:onClick="signup" - style="?android:borderlessButtonStyle" /> + style="?borderlessButtonStyle" />