Merge "Using a different transition for All-set activity" into sc-v2-dev

This commit is contained in:
TreeHugger Robot
2021-10-26 20:00:21 +00:00
committed by Android (Google) Code Review
10 changed files with 439 additions and 72 deletions
+80 -68
View File
@@ -14,81 +14,93 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingStart="@dimen/allset_page_margin_horizontal"
android:paddingEnd="@dimen/allset_page_margin_horizontal"
android:layoutDirection="locale"
android:textDirection="locale">
android:id="@+id/root_view"
android:background="@color/all_set_page_background" >
<ImageView
android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/allset_title_icon_margin_top"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:src="@drawable/ic_all_set"/>
<TextView
android:id="@+id/title"
style="@style/TextAppearance.GestureTutorial.Feedback.Title"
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/allset_title_margin_top"
app:layout_constraintTop_toBottomOf="@id/icon"
app:layout_constraintStart_toStartOf="parent"
android:gravity="start"
android:text="@string/allset_title"/>
android:layout_height="match_parent"
android:layout_marginStart="@dimen/allset_page_margin_horizontal"
android:layout_marginEnd="@dimen/allset_page_margin_horizontal"
android:layoutDirection="locale"
android:textDirection="locale"
android:id="@+id/content_view"
android:forceHasOverlappingRendering="false"
android:fitsSystemWindows="true" >
<TextView
android:id="@+id/subtitle"
style="@style/TextAppearance.GestureTutorial.Feedback.Subtitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/allset_subtitle_margin_top"
app:layout_constraintTop_toBottomOf="@id/title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintWidth_max="@dimen/allset_subtitle_width_max"
android:gravity="start"
android:text="@string/allset_description"/>
<ImageView
android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/allset_title_icon_margin_top"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:src="@drawable/ic_all_set"/>
<androidx.constraintlayout.widget.Guideline
android:id="@+id/navigation_settings_guideline_bottom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.83" />
<TextView
android:id="@+id/title"
style="@style/TextAppearance.GestureTutorial.Feedback.Title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/allset_title_margin_top"
app:layout_constraintTop_toBottomOf="@id/icon"
app:layout_constraintStart_toStartOf="parent"
android:gravity="start"
android:text="@string/allset_title"/>
<TextView
android:id="@+id/navigation_settings"
style="@style/TextAppearance.GestureTutorial.LinkText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="@id/navigation_settings_guideline_bottom"
android:minHeight="48dp"
android:background="?android:attr/selectableItemBackground"
android:text="@string/allset_navigation_settings" />
<TextView
android:id="@+id/subtitle"
style="@style/TextAppearance.GestureTutorial.Feedback.Subtitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/allset_subtitle_margin_top"
app:layout_constraintTop_toBottomOf="@id/title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintWidth_max="@dimen/allset_subtitle_width_max"
android:gravity="start"
android:text="@string/allset_description"/>
<androidx.constraintlayout.widget.Guideline
android:id="@+id/hint_guideline_bottom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.94" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/navigation_settings_guideline_bottom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.83" />
<TextView
android:id="@+id/hint"
style="@style/TextAppearance.GestureTutorial.Feedback.Subtitle"
android:textSize="14sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="@id/hint_guideline_bottom"
android:text="@string/allset_hint"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<TextView
android:id="@+id/navigation_settings"
style="@style/TextAppearance.GestureTutorial.LinkText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="@id/navigation_settings_guideline_bottom"
android:minHeight="48dp"
android:background="?android:attr/selectableItemBackground"
android:text="@string/allset_navigation_settings" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/hint_guideline_bottom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.94" />
<TextView
android:id="@+id/hint"
style="@style/TextAppearance.GestureTutorial.Feedback.Subtitle"
android:textSize="14sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="@id/hint_guideline_bottom"
android:text="@string/allset_hint"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>
+2
View File
@@ -22,4 +22,6 @@
<color name="mock_webpage_url_bar">#202124</color>
<color name="mock_webpage_url_bar_item">#3c4043</color>
<color name="all_set_page_background">#FF000000</color>
</resources>
+1 -1
View File
@@ -21,7 +21,7 @@
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:enforceNavigationBarContrast">false</item>
<item name="android:windowLightStatusBar">false</item>
<item name="android:windowBackground">#FF000000</item>
<item name="android:windowBackground">@android:color/transparent</item>
</style>
</resources>
+3
View File
@@ -72,4 +72,7 @@
<color name="mock_webpage_top_bar">#e8eaed</color>
<color name="mock_webpage_top_bar_item">#80868b</color>
<color name="mock_webpage_page_text">#bdc1c6</color>
<color name="all_set_page_background">#FFFFFFFF</color>
</resources>
+1
View File
@@ -161,6 +161,7 @@
<dimen name="allset_title_icon_margin_top">32dp</dimen>
<dimen name="allset_subtitle_margin_top">24dp</dimen>
<dimen name="allset_subtitle_width_max">348dp</dimen>
<dimen name="allset_swipe_up_shift">10dp</dimen>
<!-- All Apps Education tutorial -->
<dimen name="swipe_edu_padding">8dp</dimen>
+1 -1
View File
@@ -115,7 +115,7 @@
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:enforceNavigationBarContrast">false</item>
<item name="android:windowLightStatusBar">true</item>
<item name="android:windowBackground">#FFFFFFFF</item>
<item name="android:windowBackground">@android:color/transparent</item>
</style>
<!--
@@ -36,7 +36,7 @@ public interface InputConsumer {
int TYPE_SCREEN_PINNED = 1 << 6;
int TYPE_OVERVIEW_WITHOUT_FOCUS = 1 << 7;
int TYPE_RESET_GESTURE = 1 << 8;
int TYPE_OVERSCROLL = 1 << 9;
int TYPE_PROGRESS_DELEGATE = 1 << 9;
int TYPE_SYSUI_OVERLAY = 1 << 10;
int TYPE_ONE_HANDED = 1 << 11;
int TYPE_TASKBAR_STASH = 1 << 12;
@@ -51,7 +51,7 @@ public interface InputConsumer {
"TYPE_SCREEN_PINNED", // 6
"TYPE_OVERVIEW_WITHOUT_FOCUS", // 7
"TYPE_RESET_GESTURE", // 8
"TYPE_OVERSCROLL", // 9
"TYPE_PROGRESS_DELEGATE", // 9
"TYPE_SYSUI_OVERLAY", // 10
"TYPE_ONE_HANDED", // 11
"TYPE_TASKBAR_STASH", // 12
@@ -90,6 +90,7 @@ import com.android.quickstep.inputconsumers.OneHandedModeInputConsumer;
import com.android.quickstep.inputconsumers.OtherActivityInputConsumer;
import com.android.quickstep.inputconsumers.OverviewInputConsumer;
import com.android.quickstep.inputconsumers.OverviewWithoutFocusInputConsumer;
import com.android.quickstep.inputconsumers.ProgressDelegateInputConsumer;
import com.android.quickstep.inputconsumers.ResetGestureInputConsumer;
import com.android.quickstep.inputconsumers.ScreenPinnedInputConsumer;
import com.android.quickstep.inputconsumers.SysUiOverlayInputConsumer;
@@ -119,6 +120,7 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.function.Function;
/**
* Service connected by system-UI for handling touch interaction.
@@ -298,6 +300,13 @@ public class TouchInteractionService extends Service
public OverviewCommandHelper getOverviewCommandHelper() {
return mOverviewCommandHelper;
}
/**
* Sets a proxy to bypass swipe up behavior
*/
public void setSwipeUpProxy(Function<GestureState, AnimatedFloat> proxy) {
mSwipeUpProxyProvider = proxy != null ? proxy : (i -> null);
}
}
private static boolean sConnected = false;
@@ -336,6 +345,7 @@ public class TouchInteractionService extends Service
private DisplayManager mDisplayManager;
private TaskbarManager mTaskbarManager;
private Function<GestureState, AnimatedFloat> mSwipeUpProxyProvider = i -> null;
@Override
public void onCreate() {
@@ -653,6 +663,12 @@ public class TouchInteractionService extends Service
private InputConsumer newConsumer(GestureState previousGestureState,
GestureState newGestureState, MotionEvent event) {
AnimatedFloat progressProxy = mSwipeUpProxyProvider.apply(mGestureState);
if (progressProxy != null) {
return new ProgressDelegateInputConsumer(this, mTaskAnimationManager,
mGestureState, mInputMonitorCompat, progressProxy);
}
boolean canStartSystemGesture = mDeviceState.canStartSystemGesture();
if (!mDeviceState.isUserUnlocked()) {
@@ -0,0 +1,194 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.quickstep.inputconsumers;
import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
import static com.android.launcher3.touch.BaseSwipeDetector.calculateDuration;
import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_POSITIVE;
import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.Intent;
import android.graphics.Point;
import android.view.MotionEvent;
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.SingleAxisSwipeDetector;
import com.android.launcher3.util.DisplayController;
import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.GestureState;
import com.android.quickstep.InputConsumer;
import com.android.quickstep.MultiStateCallback;
import com.android.quickstep.RecentsAnimationCallbacks;
import com.android.quickstep.RecentsAnimationController;
import com.android.quickstep.RecentsAnimationTargets;
import com.android.quickstep.TaskAnimationManager;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.InputMonitorCompat;
/**
* Input consumer which delegates the swipe-progress handling
*/
public class ProgressDelegateInputConsumer implements InputConsumer,
RecentsAnimationCallbacks.RecentsAnimationListener,
SingleAxisSwipeDetector.Listener {
private static final float SWIPE_DISTANCE_THRESHOLD = 0.2f;
private static final String[] STATE_NAMES = DEBUG_STATES ? new String[3] : null;
private static int getFlagForIndex(int index, String name) {
if (DEBUG_STATES) {
STATE_NAMES[index] = name;
}
return 1 << index;
}
private static final int STATE_TARGET_RECEIVED =
getFlagForIndex(0, "STATE_TARGET_RECEIVED");
private static final int STATE_HANDLER_INVALIDATED =
getFlagForIndex(1, "STATE_HANDLER_INVALIDATED");
private static final int STATE_FLING_FINISHED =
getFlagForIndex(2, "STATE_FLING_FINISHED");
private final Context mContext;
private final TaskAnimationManager mTaskAnimationManager;
private final GestureState mGestureState;
private final InputMonitorCompat mInputMonitorCompat;
private final MultiStateCallback mStateCallback;
private final Point mDisplaySize;
private final SingleAxisSwipeDetector mSwipeDetector;
private final AnimatedFloat mProgress;
private boolean mDragStarted = false;
private RecentsAnimationController mRecentsAnimationController;
private Boolean mFlingEndsOnHome;
public ProgressDelegateInputConsumer(Context context,
TaskAnimationManager taskAnimationManager, GestureState gestureState,
InputMonitorCompat inputMonitorCompat, AnimatedFloat progress) {
mContext = context;
mTaskAnimationManager = taskAnimationManager;
mGestureState = gestureState;
mInputMonitorCompat = inputMonitorCompat;
mProgress = progress;
// Do not use DeviceProfile as the user data might be locked
mDisplaySize = DisplayController.INSTANCE.get(context).getInfo().currentSize;
// Init states
mStateCallback = new MultiStateCallback(STATE_NAMES);
mStateCallback.runOnceAtState(STATE_TARGET_RECEIVED | STATE_HANDLER_INVALIDATED,
this::endRemoteAnimation);
mStateCallback.runOnceAtState(STATE_TARGET_RECEIVED | STATE_FLING_FINISHED,
this::onFlingFinished);
mSwipeDetector = new SingleAxisSwipeDetector(mContext, this, VERTICAL);
mSwipeDetector.setDetectableScrollConditions(DIRECTION_POSITIVE, false);
}
@Override
public int getType() {
return TYPE_PROGRESS_DELEGATE;
}
@Override
public void onMotionEvent(MotionEvent ev) {
if (mFlingEndsOnHome == null) {
mSwipeDetector.onTouchEvent(ev);
}
}
@Override
public void onDragStart(boolean start, float startDisplacement) {
mDragStarted = true;
TestLogging.recordEvent(TestProtocol.SEQUENCE_PILFER, "pilferPointers");
mInputMonitorCompat.pilferPointers();
Intent intent = mGestureState.getHomeIntent()
.putExtra(INTENT_EXTRA_LOG_TRACE_ID, mGestureState.getGestureId());
mTaskAnimationManager.startRecentsAnimation(mGestureState, intent, this);
}
@Override
public boolean onDrag(float displacement) {
if (mDisplaySize.y > 0) {
mProgress.updateValue(displacement / -mDisplaySize.y);
}
return true;
}
@Override
public void onDragEnd(float velocity) {
final boolean willExit;
if (mSwipeDetector.isFling(velocity)) {
willExit = velocity < 0;
} else {
willExit = mProgress.value > SWIPE_DISTANCE_THRESHOLD;
}
float endValue = willExit ? 1 : 0;
long duration = calculateDuration(velocity, endValue - mProgress.value);
mFlingEndsOnHome = willExit;
ObjectAnimator anim = mProgress.animateToValue(endValue);
anim.setDuration(duration).setInterpolator(scrollInterpolatorForVelocity(velocity));
if (mRecentsAnimationController != null) {
anim.addListener(AnimatorListeners.forSuccessCallback(
() -> mStateCallback.setState(STATE_FLING_FINISHED)));
}
anim.start();
}
private void onFlingFinished() {
if (mRecentsAnimationController != null) {
boolean endToRecents = mFlingEndsOnHome == null ? true : mFlingEndsOnHome;
mRecentsAnimationController.finishController(endToRecents /* toRecents */,
null /* callback */, false /* sendUserLeaveHint */);
}
}
@Override
public void onRecentsAnimationStart(RecentsAnimationController controller,
RecentsAnimationTargets targets) {
mRecentsAnimationController = controller;
mStateCallback.setState(STATE_TARGET_RECEIVED);
}
@Override
public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
mRecentsAnimationController = null;
}
private void endRemoteAnimation() {
onDragEnd(Float.MIN_VALUE);
}
@Override
public void onConsumerAboutToBeSwitched() {
mStateCallback.setState(STATE_HANDLER_INVALIDATED);
}
@Override
public boolean allowInterceptByParent() {
return !mDragStarted;
}
}
@@ -15,10 +15,28 @@
*/
package com.android.quickstep.interaction;
import static com.android.launcher3.Utilities.mapBoundToRange;
import static com.android.launcher3.Utilities.mapRange;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import android.app.Activity;
import android.app.ActivityManager.RunningTaskInfo;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.PointF;
import android.graphics.RadialGradient;
import android.graphics.Rect;
import android.graphics.Shader.TileMode;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
@@ -29,9 +47,12 @@ import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.core.graphics.ColorUtils;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.GestureState;
import com.android.quickstep.TouchInteractionService.TISBinder;
import com.android.quickstep.util.TISBindHelper;
@@ -49,13 +70,23 @@ public class AllSetActivity extends Activity {
private static final String EXTRA_ACCENT_COLOR_DARK_MODE = "suwColorAccentDark";
private static final String EXTRA_ACCENT_COLOR_LIGHT_MODE = "suwColorAccentLight";
private static final float HINT_BOTTOM_FACTOR = 1 - .94f;
private TISBindHelper mTISBindHelper;
private TISBinder mBinder;
private final AnimatedFloat mSwipeProgress = new AnimatedFloat(this::onSwipeProgressUpdate);
private BgDrawable mBackground;
private View mContentView;
private float mSwipeUpShift;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_allset);
findViewById(R.id.root_view).setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
int mode = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
boolean isDarkTheme = mode == Configuration.UI_MODE_NIGHT_YES;
@@ -65,6 +96,11 @@ public class AllSetActivity extends Activity {
((ImageView) findViewById(R.id.icon)).getDrawable().mutate().setTint(accentColor);
mBackground = new BgDrawable(this);
findViewById(R.id.root_view).setBackground(mBackground);
mContentView = findViewById(R.id.content_view);
mSwipeUpShift = getResources().getDimension(R.dimen.allset_swipe_up_shift);
TextView tv = findViewById(R.id.navigation_settings);
tv.setTextColor(accentColor);
tv.setOnClickListener(v -> {
@@ -86,19 +122,26 @@ public class AllSetActivity extends Activity {
super.onResume();
if (mBinder != null) {
mBinder.getTaskbarManager().setSetupUIVisible(true);
mBinder.setSwipeUpProxy(this::createSwipeUpProxy);
}
}
private void onTISConnected(TISBinder binder) {
mBinder = binder;
mBinder.getTaskbarManager().setSetupUIVisible(isResumed());
mBinder.setSwipeUpProxy(isResumed() ? this::createSwipeUpProxy : null);
}
@Override
protected void onPause() {
super.onPause();
clearBinderOverride();
}
private void clearBinderOverride() {
if (mBinder != null) {
mBinder.getTaskbarManager().setSetupUIVisible(false);
mBinder.setSwipeUpProxy(null);
}
}
@@ -106,6 +149,27 @@ public class AllSetActivity extends Activity {
protected void onDestroy() {
super.onDestroy();
mTISBindHelper.onDestroy();
clearBinderOverride();
}
private AnimatedFloat createSwipeUpProxy(GestureState state) {
if (!state.getHomeIntent().getComponent().getPackageName().equals(getPackageName())) {
return null;
}
RunningTaskInfo rti = state.getRunningTask();
if (rti == null || !rti.topActivity.equals(getComponentName())) {
return null;
}
mSwipeProgress.updateValue(0);
return mSwipeProgress;
}
private void onSwipeProgressUpdate() {
mBackground.setProgress(mSwipeProgress.value);
float alpha = Utilities.mapBoundToRange(mSwipeProgress.value, 0, HINT_BOTTOM_FACTOR,
1, 0, LINEAR);
mContentView.setAlpha(alpha);
mContentView.setTranslationY((alpha - 1) * mSwipeUpShift);
}
/**
@@ -132,4 +196,79 @@ public class AllSetActivity extends Activity {
return super.performAccessibilityAction(host, action, args);
}
}
private static class BgDrawable extends Drawable {
private static final float START_SIZE_FACTOR = .5f;
private static final float END_SIZE_FACTOR = 2;
private static final float GRADIENT_END_PROGRESS = .5f;
private final Paint mPaint = new Paint();
private final RadialGradient mMaskGrad;
private final Matrix mMatrix = new Matrix();
private final ColorMatrix mColorMatrix = new ColorMatrix();
private final ColorMatrixColorFilter mColorFilter =
new ColorMatrixColorFilter(mColorMatrix);
private final int mColor;
private float mProgress = 0;
BgDrawable(Context context) {
mColor = context.getColor(R.color.all_set_page_background);
mMaskGrad = new RadialGradient(0, 0, 1,
new int[] {ColorUtils.setAlphaComponent(mColor, 0), mColor},
new float[]{0, 1}, TileMode.CLAMP);
mPaint.setShader(mMaskGrad);
mPaint.setColorFilter(mColorFilter);
}
@Override
public void draw(Canvas canvas) {
if (mProgress <= 0) {
canvas.drawColor(mColor);
return;
}
// Update the progress to half the size only.
float progress = mapBoundToRange(mProgress,
0, GRADIENT_END_PROGRESS, 0, 1, LINEAR);
Rect bounds = getBounds();
float x = bounds.exactCenterX();
float height = bounds.height();
float size = PointF.length(x, height);
float radius = size * mapRange(progress, START_SIZE_FACTOR, END_SIZE_FACTOR);
float y = mapRange(progress, height + radius , height / 2);
mMatrix.setTranslate(x, y);
mMatrix.postScale(radius, radius, x, y);
mMaskGrad.setLocalMatrix(mMatrix);
// Change the alpha-addition-component (index 19) so that every pixel is updated
// accordingly
mColorMatrix.getArray()[19] = mapBoundToRange(mProgress, 0, 1, 0, -255, LINEAR);
mColorFilter.setColorMatrix(mColorMatrix);
canvas.drawPaint(mPaint);
}
public void setProgress(float progress) {
if (mProgress != progress) {
mProgress = progress;
invalidateSelf();
}
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
@Override
public void setAlpha(int i) { }
@Override
public void setColorFilter(ColorFilter colorFilter) { }
}
}