Commit 884b3a0d authored by Boris Sazonov's avatar Boris Sazonov Committed by Commit Bot

[Unity][Android] Extract AnimationLooper

This CL moves the code from SigninView startAnimations/stopAnimations
methods into separate AnimationLooper class that encapsulates animation
looping logic. This class is then used in ConsentBumpMoreOptionsFragment
to run the header animation.

Bug: 869426
Change-Id: Ie305e4535adf27662d7d6724afc419b7bcfd8084
Reviewed-on: https://chromium-review.googlesource.com/1172688Reviewed-by: default avatarTheresa <twellington@chromium.org>
Commit-Queue: Boris Sazonov <bsazonov@chromium.org>
Cr-Commit-Position: refs/heads/master@{#584402}
parent e1f05fe8
...@@ -11,10 +11,12 @@ import android.view.LayoutInflater; ...@@ -11,10 +11,12 @@ import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.Button; import android.widget.Button;
import android.widget.ImageView;
import org.chromium.base.ApiCompatibilityUtils; import org.chromium.base.ApiCompatibilityUtils;
import org.chromium.chrome.R; import org.chromium.chrome.R;
import org.chromium.chrome.browser.widget.RadioButtonWithDescription; import org.chromium.chrome.browser.widget.RadioButtonWithDescription;
import org.chromium.ui.drawable.AnimationLooper;
/** /**
* This fragment implements the advanced consent bump screen. This screen lets users to enable or * This fragment implements the advanced consent bump screen. This screen lets users to enable or
...@@ -23,6 +25,8 @@ import org.chromium.chrome.browser.widget.RadioButtonWithDescription; ...@@ -23,6 +25,8 @@ import org.chromium.chrome.browser.widget.RadioButtonWithDescription;
public class ConsentBumpMoreOptionsFragment extends Fragment { public class ConsentBumpMoreOptionsFragment extends Fragment {
private static final String TAG = "ConsentBumpMoreOptionsFragment"; private static final String TAG = "ConsentBumpMoreOptionsFragment";
private AnimationLooper mAnimationLooper;
public ConsentBumpMoreOptionsFragment() {} public ConsentBumpMoreOptionsFragment() {}
@Override @Override
...@@ -30,6 +34,9 @@ public class ConsentBumpMoreOptionsFragment extends Fragment { ...@@ -30,6 +34,9 @@ public class ConsentBumpMoreOptionsFragment extends Fragment {
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.consent_bump_more_options_view, container, false); View view = inflater.inflate(R.layout.consent_bump_more_options_view, container, false);
ImageView headerImage = view.findViewById(R.id.consent_bump_header_image);
mAnimationLooper = AnimationLooper.create(headerImage.getDrawable());
RadioButtonWithDescription noChanges = view.findViewById(R.id.consent_bump_no_changes); RadioButtonWithDescription noChanges = view.findViewById(R.id.consent_bump_no_changes);
noChanges.setDescriptionText(getText(R.string.consent_bump_no_changes_description)); noChanges.setDescriptionText(getText(R.string.consent_bump_no_changes_description));
RadioButtonWithDescription turnOn = view.findViewById(R.id.consent_bump_turn_on); RadioButtonWithDescription turnOn = view.findViewById(R.id.consent_bump_turn_on);
...@@ -43,4 +50,16 @@ public class ConsentBumpMoreOptionsFragment extends Fragment { ...@@ -43,4 +50,16 @@ public class ConsentBumpMoreOptionsFragment extends Fragment {
}); });
return view; return view;
} }
@Override
public void onResume() {
super.onResume();
mAnimationLooper.start();
}
@Override
public void onPause() {
super.onPause();
mAnimationLooper.stop();
}
} }
...@@ -4,15 +4,10 @@ ...@@ -4,15 +4,10 @@
package org.chromium.chrome.browser.signin; package org.chromium.chrome.browser.signin;
import android.annotation.TargetApi;
import android.content.Context; import android.content.Context;
import android.content.res.ColorStateList; import android.content.res.ColorStateList;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Animatable2;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.graphics.drawable.Animatable2Compat;
import android.support.v4.graphics.drawable.DrawableCompat; import android.support.v4.graphics.drawable.DrawableCompat;
import android.support.v7.content.res.AppCompatResources; import android.support.v7.content.res.AppCompatResources;
import android.util.AttributeSet; import android.util.AttributeSet;
...@@ -22,8 +17,8 @@ import android.widget.ImageView; ...@@ -22,8 +17,8 @@ import android.widget.ImageView;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import org.chromium.base.ThreadUtils;
import org.chromium.chrome.R; import org.chromium.chrome.R;
import org.chromium.ui.drawable.AnimationLooper;
import org.chromium.ui.widget.ButtonCompat; import org.chromium.ui.widget.ButtonCompat;
/** View that wraps signin screen and caches references to UI elements. */ /** View that wraps signin screen and caches references to UI elements. */
...@@ -44,8 +39,7 @@ public class SigninView extends LinearLayout { ...@@ -44,8 +39,7 @@ public class SigninView extends LinearLayout {
private Button mRefuseButton; private Button mRefuseButton;
private Button mMoreButton; private Button mMoreButton;
private View mAcceptButtonEndPadding; private View mAcceptButtonEndPadding;
private AnimationLooper mAnimationLooper;
private Runnable mStopAnimationsRunnable;
public SigninView(Context context, @Nullable AttributeSet attrs) { public SigninView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs); super(context, attrs);
...@@ -73,16 +67,14 @@ public class SigninView extends LinearLayout { ...@@ -73,16 +67,14 @@ public class SigninView extends LinearLayout {
mRefuseButton = (Button) findViewById(R.id.negative_button); mRefuseButton = (Button) findViewById(R.id.negative_button);
mMoreButton = (Button) findViewById(R.id.more_button); mMoreButton = (Button) findViewById(R.id.more_button);
mAcceptButtonEndPadding = findViewById(R.id.positive_button_end_padding); mAcceptButtonEndPadding = findViewById(R.id.positive_button_end_padding);
mAnimationLooper = AnimationLooper.create(mHeaderImage.getDrawable());
} }
SigninScrollView getScrollView() { SigninScrollView getScrollView() {
return mScrollView; return mScrollView;
} }
ImageView getHeaderImage() {
return mHeaderImage;
}
TextView getTitleView() { TextView getTitleView() {
return mTitle; return mTitle;
} }
...@@ -140,59 +132,11 @@ public class SigninView extends LinearLayout { ...@@ -140,59 +132,11 @@ public class SigninView extends LinearLayout {
} }
void startAnimations() { void startAnimations() {
Drawable headerImage = getHeaderImage().getDrawable(); mAnimationLooper.start();
if (headerImage instanceof Animatable2Compat) {
Animatable2Compat animatable = (Animatable2Compat) headerImage;
Animatable2Compat.AnimationCallback animationCallback =
new Animatable2Compat.AnimationCallback() {
@Override
public void onAnimationEnd(Drawable drawable) {
restartAnimations(animatable);
}
};
mStopAnimationsRunnable = () -> {
animatable.unregisterAnimationCallback(animationCallback);
animatable.stop();
};
animatable.registerAnimationCallback(animationCallback);
animatable.start();
return;
}
// Animatable2 was added in API level 23 (Android M).
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return;
if (headerImage instanceof Animatable2) {
Animatable2 animatable = (Animatable2) headerImage;
Animatable2.AnimationCallback animationCallback = new Animatable2.AnimationCallback() {
@Override
@TargetApi(Build.VERSION_CODES.M)
public void onAnimationEnd(Drawable drawable) {
restartAnimations(animatable);
}
};
mStopAnimationsRunnable = () -> {
animatable.unregisterAnimationCallback(animationCallback);
animatable.stop();
};
animatable.registerAnimationCallback(animationCallback);
animatable.start();
}
} }
void stopAnimations() { void stopAnimations() {
if (mStopAnimationsRunnable == null) return; mAnimationLooper.stop();
mStopAnimationsRunnable.run();
mStopAnimationsRunnable = null;
}
private void restartAnimations(Animatable animatable) {
// In some cases (Animatable2Compat from Support Library, etc.), onAnimationEnd is invoked
// before the animation is marked as ended, so calls to start() here may be ignored.
// This issue is worked around by deferring the call to start().
ThreadUtils.postOnUiThread(() -> {
if (mStopAnimationsRunnable == null) return;
animatable.start();
});
} }
static Drawable getExpandArrowDrawable(Context context) { static Drawable getExpandArrowDrawable(Context context) {
......
...@@ -277,6 +277,7 @@ android_library("ui_full_java") { ...@@ -277,6 +277,7 @@ android_library("ui_full_java") {
"java/src/org/chromium/ui/display/DisplayUtil.java", "java/src/org/chromium/ui/display/DisplayUtil.java",
"java/src/org/chromium/ui/display/PhysicalDisplayAndroid.java", "java/src/org/chromium/ui/display/PhysicalDisplayAndroid.java",
"java/src/org/chromium/ui/display/VirtualDisplayAndroid.java", "java/src/org/chromium/ui/display/VirtualDisplayAndroid.java",
"java/src/org/chromium/ui/drawable/AnimationLooper.java",
"java/src/org/chromium/ui/drawable/StateListDrawableBuilder.java", "java/src/org/chromium/ui/drawable/StateListDrawableBuilder.java",
"java/src/org/chromium/ui/events/devices/InputDeviceObserver.java", "java/src/org/chromium/ui/events/devices/InputDeviceObserver.java",
"java/src/org/chromium/ui/gfx/BitmapHelper.java", "java/src/org/chromium/ui/gfx/BitmapHelper.java",
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.ui.drawable;
import android.annotation.TargetApi;
import android.graphics.drawable.Animatable2;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.graphics.drawable.Animatable2Compat;
import org.chromium.base.ThreadUtils;
/**
* Encapsulates the logic to loop animated drawables from both Android Framework (using Animatable2)
* and Support Library (using Animatable2Compat). Should be instantiated by {@link #create}. The
* animation should be started and stopped using {@link #start()} and {@link #stop()}.
*/
public interface AnimationLooper {
/** Starts the animation of the associated drawable. */
void start();
/** Stops the animation of the associated drawable. */
void stop();
/**
* Instantiates proper implementation of AnimationLooper for the drawable.
* @param drawable The drawable to be animated. Created AnimationLooper instance will be
* associated with this drawable. The drawable should implement either
* {@link Animatable2} or {@link Animatable2Compat}.
* @return The new AnimationLooper.
*/
static AnimationLooper create(Drawable drawable) {
// Animatable2 was added in API level 23 (Android M).
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
return new Animatable2CompatImpl((Animatable2Compat) drawable);
}
if (drawable instanceof Animatable2Compat) {
return new Animatable2CompatImpl((Animatable2Compat) drawable);
}
return new Animatable2Impl((Animatable2) drawable);
}
/** Used with drawables that implement {@link Animatable2Compat}. */
class Animatable2CompatImpl implements AnimationLooper {
private final Animatable2Compat mAnimatable;
private final Animatable2Compat.AnimationCallback mAnimationCallback;
private boolean mRunning;
Animatable2CompatImpl(Animatable2Compat animatable) {
mAnimatable = animatable;
mAnimationCallback = new Animatable2Compat.AnimationCallback() {
@Override
public void onAnimationEnd(Drawable drawable) {
restartAnimation(animatable);
}
};
}
@Override
public void start() {
assert !mRunning : "The animation is already running!";
mRunning = true;
mAnimatable.registerAnimationCallback(mAnimationCallback);
mAnimatable.start();
}
@Override
public void stop() {
assert mRunning : "The animation isn't running!";
mRunning = false;
mAnimatable.unregisterAnimationCallback(mAnimationCallback);
mAnimatable.stop();
}
private void restartAnimation(Animatable2Compat animatable) {
ThreadUtils.postOnUiThread(() -> {
if (mRunning) animatable.start();
});
}
}
/** Used with drawables that implement {@link Animatable2}. */
@TargetApi(Build.VERSION_CODES.M)
class Animatable2Impl implements AnimationLooper {
private final Animatable2 mAnimatable;
private final Animatable2.AnimationCallback mAnimationCallback;
private boolean mRunning;
Animatable2Impl(Animatable2 animatable) {
mAnimatable = animatable;
mAnimationCallback = new Animatable2.AnimationCallback() {
@Override
public void onAnimationEnd(Drawable drawable) {
restartAnimation(animatable);
}
};
}
@Override
public void start() {
assert !mRunning : "The animation is already running!";
mRunning = true;
mAnimatable.registerAnimationCallback(mAnimationCallback);
mAnimatable.start();
}
@Override
public void stop() {
assert mRunning : "The animation isn't running!";
mRunning = false;
mAnimatable.unregisterAnimationCallback(mAnimationCallback);
mAnimatable.stop();
}
private void restartAnimation(Animatable2 animatable) {
ThreadUtils.postOnUiThread(() -> {
if (mRunning) animatable.start();
});
}
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment