update:2021.03.19

fix:重新提交
add:
This commit is contained in:
FHT
2021-03-19 18:14:04 +08:00
parent 23e93b0e31
commit d4399ffa61
1189 changed files with 0 additions and 149104 deletions

View File

@@ -1,141 +0,0 @@
/*
* Copyright (C) 2018 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.uiuios;
import static com.android.uiuios.Utilities.SINGLE_FRAME_MS;
import static com.android.uiuios.Utilities.postAsyncCallback;
import static com.android.systemui.shared.recents.utilities.Utilities
.postAtFrontOfQueueAsynchronously;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.annotation.TargetApi;
import android.os.Build;
import android.os.Handler;
import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import androidx.annotation.BinderThread;
import androidx.annotation.UiThread;
@TargetApi(Build.VERSION_CODES.P)
public abstract class LauncherAnimationRunner implements RemoteAnimationRunnerCompat {
private final Handler mHandler;
private final boolean mStartAtFrontOfQueue;
private AnimationResult mAnimationResult;
/**
* @param startAtFrontOfQueue If true, the animation start will be posted at the front of the
* queue to minimize latency.
*/
public LauncherAnimationRunner(Handler handler, boolean startAtFrontOfQueue) {
mHandler = handler;
mStartAtFrontOfQueue = startAtFrontOfQueue;
}
@BinderThread
@Override
public void onAnimationStart(RemoteAnimationTargetCompat[] targetCompats, Runnable runnable) {
Runnable r = () -> {
finishExistingAnimation();
mAnimationResult = new AnimationResult(runnable);
onCreateAnimation(targetCompats, mAnimationResult);
};
if (mStartAtFrontOfQueue) {
postAtFrontOfQueueAsynchronously(mHandler, r);
} else {
postAsyncCallback(mHandler, r);
}
}
/**
* Called on the UI thread when the animation targets are received. The implementation must
* call {@link AnimationResult#setAnimation(AnimatorSet)} with the target animation to be run.
*/
@UiThread
public abstract void onCreateAnimation(
RemoteAnimationTargetCompat[] targetCompats, AnimationResult result);
@UiThread
private void finishExistingAnimation() {
if (mAnimationResult != null) {
mAnimationResult.finish();
mAnimationResult = null;
}
}
/**
* Called by the system
*/
@BinderThread
@Override
public void onAnimationCancelled() {
postAsyncCallback(mHandler, this::finishExistingAnimation);
}
public static final class AnimationResult {
private final Runnable mFinishRunnable;
private AnimatorSet mAnimator;
private boolean mFinished = false;
private boolean mInitialized = false;
private AnimationResult(Runnable finishRunnable) {
mFinishRunnable = finishRunnable;
}
@UiThread
private void finish() {
if (!mFinished) {
mFinishRunnable.run();
mFinished = true;
}
}
@UiThread
public void setAnimation(AnimatorSet animation) {
if (mInitialized) {
throw new IllegalStateException("Animation already initialized");
}
mInitialized = true;
mAnimator = animation;
if (mAnimator == null) {
finish();
} else if (mFinished) {
// Animation callback was already finished, skip the animation.
mAnimator.start();
mAnimator.end();
} else {
// Start the animation
mAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
finish();
}
});
mAnimator.start();
// Because t=0 has the app icon in its original spot, we can skip the
// first frame and have the same movement one frame earlier.
mAnimator.setCurrentPlayTime(SINGLE_FRAME_MS);
}
}
}
}

View File

@@ -1,91 +0,0 @@
/*
* Copyright (C) 2018 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.uiuios;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Handler;
import com.android.uiuios.states.InternalStateHandler;
import com.android.quickstep.ActivityControlHelper.ActivityInitListener;
import com.android.quickstep.OverviewCallbacks;
import com.android.quickstep.util.RemoteAnimationProvider;
import java.util.function.BiPredicate;
@TargetApi(Build.VERSION_CODES.P)
public class LauncherInitListener extends InternalStateHandler implements ActivityInitListener {
private final BiPredicate<Launcher, Boolean> mOnInitListener;
private RemoteAnimationProvider mRemoteAnimationProvider;
public LauncherInitListener(BiPredicate<Launcher, Boolean> onInitListener) {
mOnInitListener = onInitListener;
}
@Override
protected boolean init(Launcher launcher, boolean alreadyOnHome) {
if (mRemoteAnimationProvider != null) {
QuickstepAppTransitionManagerImpl appTransitionManager =
(QuickstepAppTransitionManagerImpl) launcher.getAppTransitionManager();
// Set a one-time animation provider. After the first call, this will get cleared.
// TODO: Probably also check the intended target id.
CancellationSignal cancellationSignal = new CancellationSignal();
appTransitionManager.setRemoteAnimationProvider((targets) -> {
// On the first call clear the reference.
cancellationSignal.cancel();
RemoteAnimationProvider provider = mRemoteAnimationProvider;
mRemoteAnimationProvider = null;
if (provider != null && launcher.getStateManager().getState().overviewUi) {
return provider.createWindowAnimation(targets);
}
return null;
}, cancellationSignal);
}
OverviewCallbacks.get(launcher).onInitOverviewTransition();
return mOnInitListener.test(launcher, alreadyOnHome);
}
@Override
public void register() {
initWhenReady();
}
@Override
public void unregister() {
mRemoteAnimationProvider = null;
clearReference();
}
@Override
public void registerAndStartActivity(Intent intent, RemoteAnimationProvider animProvider,
Context context, Handler handler, long duration) {
mRemoteAnimationProvider = animProvider;
register();
Bundle options = animProvider.toActivityOptions(handler, duration).toBundle();
context.startActivity(addToIntent(new Intent((intent))), options);
}
}

View File

@@ -1,828 +0,0 @@
/*
* Copyright (C) 2018 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.uiuios;
import static com.android.uiuios.BaseActivity.INVISIBLE_ALL;
import static com.android.uiuios.BaseActivity.INVISIBLE_BY_APP_TRANSITIONS;
import static com.android.uiuios.BaseActivity.INVISIBLE_BY_PENDING_FLAGS;
import static com.android.uiuios.BaseActivity.PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION;
import static com.android.uiuios.LauncherState.ALL_APPS;
import static com.android.uiuios.LauncherState.OVERVIEW;
import static com.android.uiuios.Utilities.postAsyncCallback;
import static com.android.uiuios.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
import static com.android.uiuios.anim.Interpolators.AGGRESSIVE_EASE;
import static com.android.uiuios.anim.Interpolators.DEACCEL_1_7;
import static com.android.uiuios.anim.Interpolators.EXAGGERATED_EASE;
import static com.android.uiuios.anim.Interpolators.LINEAR;
import static com.android.uiuios.dragndrop.DragLayer.ALPHA_INDEX_TRANSITIONS;
import static com.android.uiuios.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
import static com.android.systemui.shared.system.QuickStepContract.getWindowCornerRadius;
import static com.android.systemui.shared.system.QuickStepContract.supportsRoundedCornersOnWindows;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.app.ActivityOptions;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.Looper;
import android.util.Pair;
import android.view.View;
import com.android.uiuios.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.uiuios.allapps.AllAppsTransitionController;
import com.android.uiuios.anim.Interpolators;
import com.android.uiuios.config.FeatureFlags;
import com.android.uiuios.dragndrop.DragLayer;
import com.android.uiuios.shortcuts.DeepShortcutView;
import com.android.uiuios.util.MultiValueAlpha;
import com.android.uiuios.util.MultiValueAlpha.AlphaProperty;
import com.android.uiuios.views.FloatingIconView;
import com.android.quickstep.util.MultiValueUpdateListener;
import com.android.quickstep.util.RemoteAnimationProvider;
import com.android.quickstep.util.RemoteAnimationTargetSet;
import com.android.systemui.shared.system.ActivityCompat;
import com.android.systemui.shared.system.ActivityOptionsCompat;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
import com.android.systemui.shared.system.RemoteAnimationDefinitionCompat;
import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
import com.android.systemui.shared.system.WindowManagerWrapper;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
/**
* {@link LauncherAppTransitionManager} with Quickstep-specific app transitions for launching from
* home and/or all-apps.
*/
@TargetApi(Build.VERSION_CODES.O)
@SuppressWarnings("unused")
public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTransitionManager
implements OnDeviceProfileChangeListener {
private static final String TAG = "QuickstepTransition";
/** Duration of status bar animations. */
public static final int STATUS_BAR_TRANSITION_DURATION = 120;
/**
* Since our animations decelerate heavily when finishing, we want to start status bar animations
* x ms before the ending.
*/
public static final int STATUS_BAR_TRANSITION_PRE_DELAY = 96;
private static final String CONTROL_REMOTE_APP_TRANSITION_PERMISSION =
"android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS";
private static final long APP_LAUNCH_DURATION = 450;
// Use a shorter duration for x or y translation to create a curve effect
private static final long APP_LAUNCH_CURVED_DURATION = 250;
private static final long APP_LAUNCH_ALPHA_DURATION = 50;
private static final long APP_LAUNCH_ALPHA_START_DELAY = 25;
// We scale the durations for the downward app launch animations (minus the scale animation).
private static final float APP_LAUNCH_DOWN_DUR_SCALE_FACTOR = 0.8f;
private static final long APP_LAUNCH_DOWN_DURATION =
(long) (APP_LAUNCH_DURATION * APP_LAUNCH_DOWN_DUR_SCALE_FACTOR);
private static final long APP_LAUNCH_DOWN_CURVED_DURATION =
(long) (APP_LAUNCH_CURVED_DURATION * APP_LAUNCH_DOWN_DUR_SCALE_FACTOR);
private static final long APP_LAUNCH_ALPHA_DOWN_DURATION =
(long) (APP_LAUNCH_ALPHA_DURATION * APP_LAUNCH_DOWN_DUR_SCALE_FACTOR);
private static final long CROP_DURATION = 375;
private static final long RADIUS_DURATION = 375;
public static final int RECENTS_LAUNCH_DURATION = 336;
private static final int LAUNCHER_RESUME_START_DELAY = 100;
private static final int CLOSING_TRANSITION_DURATION_MS = 250;
protected static final int CONTENT_ALPHA_DURATION = 217;
protected static final int CONTENT_TRANSLATION_DURATION = 350;
// Progress = 0: All apps is fully pulled up, Progress = 1: All apps is fully pulled down.
public static final float ALL_APPS_PROGRESS_OFF_SCREEN = 1.3059858f;
protected final Launcher mLauncher;
private final DragLayer mDragLayer;
private final AlphaProperty mDragLayerAlpha;
final Handler mHandler;
private final boolean mIsRtl;
private final float mContentTransY;
private final float mWorkspaceTransY;
private final float mClosingWindowTransY;
private DeviceProfile mDeviceProfile;
private RemoteAnimationProvider mRemoteAnimationProvider;
private final AnimatorListenerAdapter mForceInvisibleListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
mLauncher.addForceInvisibleFlag(INVISIBLE_BY_APP_TRANSITIONS);
}
@Override
public void onAnimationEnd(Animator animation) {
mLauncher.clearForceInvisibleFlag(INVISIBLE_BY_APP_TRANSITIONS);
}
};
public QuickstepAppTransitionManagerImpl(Context context) {
mLauncher = Launcher.getLauncher(context);
mDragLayer = mLauncher.getDragLayer();
mDragLayerAlpha = mDragLayer.getAlphaProperty(ALPHA_INDEX_TRANSITIONS);
mHandler = new Handler(Looper.getMainLooper());
mIsRtl = Utilities.isRtl(mLauncher.getResources());
mDeviceProfile = mLauncher.getDeviceProfile();
Resources res = mLauncher.getResources();
mContentTransY = res.getDimensionPixelSize(R.dimen.content_trans_y);
mWorkspaceTransY = res.getDimensionPixelSize(R.dimen.workspace_trans_y);
mClosingWindowTransY = res.getDimensionPixelSize(R.dimen.closing_window_trans_y);
mLauncher.addOnDeviceProfileChangeListener(this);
registerRemoteAnimations();
}
@Override
public void onDeviceProfileChanged(DeviceProfile dp) {
mDeviceProfile = dp;
}
@Override
public boolean supportsAdaptiveIconAnimation() {
return hasControlRemoteAppTransitionPermission()
&& FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM.get();
}
/**
* @return ActivityOptions with remote animations that controls how the window of the opening
* targets are displayed.
*/
@Override
public ActivityOptions getActivityLaunchOptions(Launcher launcher, View v) {
if (hasControlRemoteAppTransitionPermission()) {
boolean fromRecents = isLaunchingFromRecents(v, null /* targets */);
RemoteAnimationRunnerCompat runner = new LauncherAnimationRunner(mHandler,
true /* startAtFrontOfQueue */) {
@Override
public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats,
AnimationResult result) {
AnimatorSet anim = new AnimatorSet();
boolean launcherClosing =
launcherIsATargetWithMode(targetCompats, MODE_CLOSING);
if (isLaunchingFromRecents(v, targetCompats)) {
composeRecentsLaunchAnimator(anim, v, targetCompats, launcherClosing);
} else {
composeIconLaunchAnimator(anim, v, targetCompats, launcherClosing);
}
if (launcherClosing) {
anim.addListener(mForceInvisibleListener);
}
result.setAnimation(anim);
}
};
// Note that this duration is a guess as we do not know if the animation will be a
// recents launch or not for sure until we know the opening app targets.
long duration = fromRecents
? RECENTS_LAUNCH_DURATION
: APP_LAUNCH_DURATION;
long statusBarTransitionDelay = duration - STATUS_BAR_TRANSITION_DURATION
- STATUS_BAR_TRANSITION_PRE_DELAY;
return ActivityOptionsCompat.makeRemoteAnimation(new RemoteAnimationAdapterCompat(
runner, duration, statusBarTransitionDelay));
}
return super.getActivityLaunchOptions(launcher, v);
}
/**
* Whether the launch is a recents app transition and we should do a launch animation
* from the recents view. Note that if the remote animation targets are not provided, this
* may not always be correct as we may resolve the opening app to a task when the animation
* starts.
*
* @param v the view to launch from
* @param targets apps that are opening/closing
* @return true if the app is launching from recents, false if it most likely is not
*/
protected abstract boolean isLaunchingFromRecents(@NonNull View v,
@Nullable RemoteAnimationTargetCompat[] targets);
/**
* Composes the animations for a launch from the recents list.
*
* @param anim the animator set to add to
* @param v the launching view
* @param targets the apps that are opening/closing
* @param launcherClosing true if the launcher app is closing
*/
protected abstract void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
@NonNull RemoteAnimationTargetCompat[] targets, boolean launcherClosing);
/**
* Compose the animations for a launch from the app icon.
*
* @param anim the animation to add to
* @param v the launching view with the icon
* @param targets the list of opening/closing apps
* @param launcherClosing true if launcher is closing
*/
private void composeIconLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
@NonNull RemoteAnimationTargetCompat[] targets, boolean launcherClosing) {
// Set the state animation first so that any state listeners are called
// before our internal listeners.
mLauncher.getStateManager().setCurrentAnimation(anim);
Rect windowTargetBounds = getWindowTargetBounds(targets);
boolean isAllOpeningTargetTrs = true;
for (int i = 0; i < targets.length; i++) {
RemoteAnimationTargetCompat target = targets[i];
if (target.mode == MODE_OPENING) {
isAllOpeningTargetTrs &= target.isTranslucent;
}
if (!isAllOpeningTargetTrs) break;
}
anim.play(getOpeningWindowAnimators(v, targets, windowTargetBounds,
!isAllOpeningTargetTrs));
if (launcherClosing) {
Pair<AnimatorSet, Runnable> launcherContentAnimator =
getLauncherContentAnimator(true /* isAppOpening */,
new float[] {0, -mContentTransY});
anim.play(launcherContentAnimator.first);
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
launcherContentAnimator.second.run();
}
});
}
}
/**
* Return the window bounds of the opening target.
* In multiwindow mode, we need to get the final size of the opening app window target to help
* figure out where the floating view should animate to.
*/
private Rect getWindowTargetBounds(RemoteAnimationTargetCompat[] targets) {
Rect bounds = new Rect(0, 0, mDeviceProfile.widthPx, mDeviceProfile.heightPx);
if (mLauncher.isInMultiWindowMode()) {
for (RemoteAnimationTargetCompat target : targets) {
if (target.mode == MODE_OPENING) {
bounds.set(target.sourceContainerBounds);
bounds.offsetTo(target.position.x, target.position.y);
return bounds;
}
}
}
return bounds;
}
public void setRemoteAnimationProvider(final RemoteAnimationProvider animationProvider,
CancellationSignal cancellationSignal) {
mRemoteAnimationProvider = animationProvider;
cancellationSignal.setOnCancelListener(() -> {
if (animationProvider == mRemoteAnimationProvider) {
mRemoteAnimationProvider = null;
}
});
}
/**
* Content is everything on screen except the background and the floating view (if any).
*
* @param isAppOpening True when this is called when an app is opening.
* False when this is called when an app is closing.
* @param trans Array that contains the start and end translation values for the content.
*/
private Pair<AnimatorSet, Runnable> getLauncherContentAnimator(boolean isAppOpening,
float[] trans) {
AnimatorSet launcherAnimator = new AnimatorSet();
Runnable endListener;
float[] alphas = isAppOpening
? new float[] {1, 0}
: new float[] {0, 1};
if (mLauncher.isInState(ALL_APPS)) {
// All Apps in portrait mode is full screen, so we only animate AllAppsContainerView.
final View appsView = mLauncher.getAppsView();
final float startAlpha = appsView.getAlpha();
final float startY = appsView.getTranslationY();
appsView.setAlpha(alphas[0]);
appsView.setTranslationY(trans[0]);
ObjectAnimator alpha = ObjectAnimator.ofFloat(appsView, View.ALPHA, alphas);
alpha.setDuration(CONTENT_ALPHA_DURATION);
alpha.setInterpolator(LINEAR);
appsView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
alpha.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
appsView.setLayerType(View.LAYER_TYPE_NONE, null);
}
});
ObjectAnimator transY = ObjectAnimator.ofFloat(appsView, View.TRANSLATION_Y, trans);
transY.setInterpolator(AGGRESSIVE_EASE);
transY.setDuration(CONTENT_TRANSLATION_DURATION);
launcherAnimator.play(alpha);
launcherAnimator.play(transY);
endListener = () -> {
appsView.setAlpha(startAlpha);
appsView.setTranslationY(startY);
appsView.setLayerType(View.LAYER_TYPE_NONE, null);
};
} else if (mLauncher.isInState(OVERVIEW)) {
AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
launcherAnimator.play(ObjectAnimator.ofFloat(allAppsController, ALL_APPS_PROGRESS,
allAppsController.getProgress(), ALL_APPS_PROGRESS_OFF_SCREEN));
endListener = composeViewContentAnimator(launcherAnimator, alphas, trans);
} else {
mDragLayerAlpha.setValue(alphas[0]);
ObjectAnimator alpha =
ObjectAnimator.ofFloat(mDragLayerAlpha, MultiValueAlpha.VALUE, alphas);
alpha.setDuration(CONTENT_ALPHA_DURATION);
alpha.setInterpolator(LINEAR);
launcherAnimator.play(alpha);
mDragLayer.setTranslationY(trans[0]);
ObjectAnimator transY = ObjectAnimator.ofFloat(mDragLayer, View.TRANSLATION_Y, trans);
transY.setInterpolator(AGGRESSIVE_EASE);
transY.setDuration(CONTENT_TRANSLATION_DURATION);
launcherAnimator.play(transY);
mDragLayer.getScrim().hideSysUiScrim(true);
// Pause page indicator animations as they lead to layer trashing.
mLauncher.getWorkspace().getPageIndicator().pauseAnimations();
mDragLayer.setLayerType(View.LAYER_TYPE_HARDWARE, null);
endListener = this::resetContentView;
}
return new Pair<>(launcherAnimator, endListener);
}
/**
* Compose recents view alpha and translation Y animation when launcher opens/closes apps.
*
* @param anim the animator set to add to
* @param alphas the alphas to animate to over time
* @param trans the translation Y values to animator to over time
* @return listener to run when the animation ends
*/
protected abstract Runnable composeViewContentAnimator(@NonNull AnimatorSet anim,
float[] alphas, float[] trans);
/**
* @return Animator that controls the window of the opening targets.
*/
private ValueAnimator getOpeningWindowAnimators(View v, RemoteAnimationTargetCompat[] targets,
Rect windowTargetBounds, boolean toggleVisibility) {
RectF bounds = new RectF();
FloatingIconView floatingView = FloatingIconView.getFloatingIconView(mLauncher, v,
toggleVisibility, bounds, true /* isOpening */);
Rect crop = new Rect();
Matrix matrix = new Matrix();
RemoteAnimationTargetSet openingTargets = new RemoteAnimationTargetSet(targets,
MODE_OPENING);
SyncRtSurfaceTransactionApplierCompat surfaceApplier =
new SyncRtSurfaceTransactionApplierCompat(floatingView);
openingTargets.addDependentTransactionApplier(surfaceApplier);
// Scale the app icon to take up the entire screen. This simplifies the math when
// animating the app window position / scale.
float smallestSize = Math.min(windowTargetBounds.height(), windowTargetBounds.width());
float maxScaleX = smallestSize / bounds.width();
float maxScaleY = smallestSize / bounds.height();
float scale = Math.max(maxScaleX, maxScaleY);
float startScale = 1f;
if (v instanceof BubbleTextView && !(v.getParent() instanceof DeepShortcutView)) {
Drawable dr = ((BubbleTextView) v).getIcon();
if (dr instanceof FastBitmapDrawable) {
startScale = ((FastBitmapDrawable) dr).getAnimatedScale();
}
}
final float initialStartScale = startScale;
int[] dragLayerBounds = new int[2];
mDragLayer.getLocationOnScreen(dragLayerBounds);
// Animate the app icon to the center of the window bounds in screen coordinates.
float centerX = windowTargetBounds.centerX() - dragLayerBounds[0];
float centerY = windowTargetBounds.centerY() - dragLayerBounds[1];
float dX = centerX - bounds.centerX();
float dY = centerY - bounds.centerY();
boolean useUpwardAnimation = bounds.top > centerY
|| Math.abs(dY) < mLauncher.getDeviceProfile().cellHeightPx;
final long xDuration = useUpwardAnimation ? APP_LAUNCH_CURVED_DURATION
: APP_LAUNCH_DOWN_DURATION;
final long yDuration = useUpwardAnimation ? APP_LAUNCH_DURATION
: APP_LAUNCH_DOWN_CURVED_DURATION;
final long alphaDuration = useUpwardAnimation ? APP_LAUNCH_ALPHA_DURATION
: APP_LAUNCH_ALPHA_DOWN_DURATION;
RectF targetBounds = new RectF(windowTargetBounds);
RectF currentBounds = new RectF();
RectF temp = new RectF();
ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
appAnimator.setDuration(APP_LAUNCH_DURATION);
appAnimator.setInterpolator(LINEAR);
appAnimator.addListener(floatingView);
appAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (v instanceof BubbleTextView) {
((BubbleTextView) v).setStayPressed(false);
}
openingTargets.release();
}
});
float shapeRevealDuration = APP_LAUNCH_DURATION * SHAPE_PROGRESS_DURATION;
final float startCrop;
final float endCrop;
if (mDeviceProfile.isVerticalBarLayout()) {
startCrop = windowTargetBounds.height();
endCrop = windowTargetBounds.width();
} else {
startCrop = windowTargetBounds.width();
endCrop = windowTargetBounds.height();
}
final float initialWindowRadius = supportsRoundedCornersOnWindows(mLauncher.getResources())
? startCrop / 2f : 0f;
final float windowRadius = mDeviceProfile.isMultiWindowMode
? 0 : getWindowCornerRadius(mLauncher.getResources());
appAnimator.addUpdateListener(new MultiValueUpdateListener() {
FloatProp mDx = new FloatProp(0, dX, 0, xDuration, AGGRESSIVE_EASE);
FloatProp mDy = new FloatProp(0, dY, 0, yDuration, AGGRESSIVE_EASE);
FloatProp mIconScale = new FloatProp(initialStartScale, scale, 0, APP_LAUNCH_DURATION,
EXAGGERATED_EASE);
FloatProp mIconAlpha = new FloatProp(1f, 0f, APP_LAUNCH_ALPHA_START_DELAY,
alphaDuration, LINEAR);
FloatProp mCroppedSize = new FloatProp(startCrop, endCrop, 0, CROP_DURATION,
EXAGGERATED_EASE);
FloatProp mWindowRadius = new FloatProp(initialWindowRadius, windowRadius, 0,
RADIUS_DURATION, EXAGGERATED_EASE);
@Override
public void onUpdate(float percent) {
// Calculate app icon size.
float iconWidth = bounds.width() * mIconScale.value;
float iconHeight = bounds.height() * mIconScale.value;
// Animate the window crop so that it starts off as a square.
final int windowWidth;
final int windowHeight;
if (mDeviceProfile.isVerticalBarLayout()) {
windowWidth = (int) mCroppedSize.value;
windowHeight = windowTargetBounds.height();
} else {
windowWidth = windowTargetBounds.width();
windowHeight = (int) mCroppedSize.value;
}
crop.set(0, 0, windowWidth, windowHeight);
// Scale the app window to match the icon size.
float scaleX = iconWidth / windowWidth;
float scaleY = iconHeight / windowHeight;
float scale = Math.min(1f, Math.max(scaleX, scaleY));
float scaledWindowWidth = windowWidth * scale;
float scaledWindowHeight = windowHeight * scale;
float offsetX = (scaledWindowWidth - iconWidth) / 2;
float offsetY = (scaledWindowHeight - iconHeight) / 2;
// Calculate the window position
temp.set(bounds);
temp.offset(dragLayerBounds[0], dragLayerBounds[1]);
temp.offset(mDx.value, mDy.value);
Utilities.scaleRectFAboutCenter(temp, mIconScale.value);
float transX0 = temp.left - offsetX;
float transY0 = temp.top - offsetY;
float croppedHeight = (windowTargetBounds.height() - crop.height()) * scale;
float croppedWidth = (windowTargetBounds.width() - crop.width()) * scale;
SurfaceParams[] params = new SurfaceParams[targets.length];
for (int i = targets.length - 1; i >= 0; i--) {
RemoteAnimationTargetCompat target = targets[i];
Rect targetCrop;
final float alpha;
final float cornerRadius;
if (target.mode == MODE_OPENING) {
matrix.setScale(scale, scale);
matrix.postTranslate(transX0, transY0);
targetCrop = crop;
alpha = 1f - mIconAlpha.value;
cornerRadius = mWindowRadius.value;
matrix.mapRect(currentBounds, targetBounds);
if (mDeviceProfile.isVerticalBarLayout()) {
currentBounds.right -= croppedWidth;
} else {
currentBounds.bottom -= croppedHeight;
}
floatingView.update(currentBounds, mIconAlpha.value, percent, 0f,
cornerRadius * scale, true /* isOpening */);
} else {
matrix.setTranslate(target.position.x, target.position.y);
targetCrop = target.sourceContainerBounds;
alpha = 1f;
cornerRadius = 0;
}
params[i] = new SurfaceParams(target.leash, alpha, matrix, targetCrop,
RemoteAnimationProvider.getLayer(target, MODE_OPENING),
cornerRadius);
}
surfaceApplier.scheduleApply(params);
}
});
return appAnimator;
}
/**
* Registers remote animations used when closing apps to home screen.
*/
private void registerRemoteAnimations() {
// Unregister this
if (hasControlRemoteAppTransitionPermission()) {
RemoteAnimationDefinitionCompat definition = new RemoteAnimationDefinitionCompat();
definition.addRemoteAnimation(WindowManagerWrapper.TRANSIT_WALLPAPER_OPEN,
WindowManagerWrapper.ACTIVITY_TYPE_STANDARD,
new RemoteAnimationAdapterCompat(getWallpaperOpenRunner(false /* fromUnlock */),
CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */));
new ActivityCompat(mLauncher).registerRemoteAnimations(definition);
}
}
private boolean launcherIsATargetWithMode(RemoteAnimationTargetCompat[] targets, int mode) {
return taskIsATargetWithMode(targets, mLauncher.getTaskId(), mode);
}
/**
* @return Runner that plays when user goes to Launcher
* ie. pressing home, swiping up from nav bar.
*/
RemoteAnimationRunnerCompat getWallpaperOpenRunner(boolean fromUnlock) {
return new WallpaperOpenLauncherAnimationRunner(mHandler, false /* startAtFrontOfQueue */,
fromUnlock);
}
/**
* Animator that controls the transformations of the windows when unlocking the device.
*/
private Animator getUnlockWindowAnimator(RemoteAnimationTargetCompat[] targets) {
SyncRtSurfaceTransactionApplierCompat surfaceApplier =
new SyncRtSurfaceTransactionApplierCompat(mDragLayer);
ValueAnimator unlockAnimator = ValueAnimator.ofFloat(0, 1);
unlockAnimator.setDuration(CLOSING_TRANSITION_DURATION_MS);
float cornerRadius = mDeviceProfile.isMultiWindowMode ? 0 :
QuickStepContract.getWindowCornerRadius(mLauncher.getResources());
unlockAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
SurfaceParams[] params = new SurfaceParams[targets.length];
for (int i = targets.length - 1; i >= 0; i--) {
RemoteAnimationTargetCompat target = targets[i];
params[i] = new SurfaceParams(target.leash, 1f, null,
target.sourceContainerBounds,
RemoteAnimationProvider.getLayer(target, MODE_OPENING), cornerRadius);
}
surfaceApplier.scheduleApply(params);
}
});
return unlockAnimator;
}
/**
* Animator that controls the transformations of the windows the targets that are closing.
*/
private Animator getClosingWindowAnimators(RemoteAnimationTargetCompat[] targets) {
SyncRtSurfaceTransactionApplierCompat surfaceApplier =
new SyncRtSurfaceTransactionApplierCompat(mDragLayer);
Matrix matrix = new Matrix();
ValueAnimator closingAnimator = ValueAnimator.ofFloat(0, 1);
int duration = CLOSING_TRANSITION_DURATION_MS;
float windowCornerRadius = mDeviceProfile.isMultiWindowMode
? 0 : getWindowCornerRadius(mLauncher.getResources());
closingAnimator.setDuration(duration);
closingAnimator.addUpdateListener(new MultiValueUpdateListener() {
FloatProp mDy = new FloatProp(0, mClosingWindowTransY, 0, duration, DEACCEL_1_7);
FloatProp mScale = new FloatProp(1f, 1f, 0, duration, DEACCEL_1_7);
FloatProp mAlpha = new FloatProp(1f, 0f, 25, 125, LINEAR);
@Override
public void onUpdate(float percent) {
SurfaceParams[] params = new SurfaceParams[targets.length];
for (int i = targets.length - 1; i >= 0; i--) {
RemoteAnimationTargetCompat target = targets[i];
final float alpha;
final float cornerRadius;
if (target.mode == MODE_CLOSING) {
matrix.setScale(mScale.value, mScale.value,
target.sourceContainerBounds.centerX(),
target.sourceContainerBounds.centerY());
matrix.postTranslate(0, mDy.value);
matrix.postTranslate(target.position.x, target.position.y);
alpha = mAlpha.value;
cornerRadius = windowCornerRadius;
} else {
matrix.setTranslate(target.position.x, target.position.y);
alpha = 1f;
cornerRadius = 0f;
}
params[i] = new SurfaceParams(target.leash, alpha, matrix,
target.sourceContainerBounds,
RemoteAnimationProvider.getLayer(target, MODE_CLOSING),
cornerRadius);
}
surfaceApplier.scheduleApply(params);
}
});
return closingAnimator;
}
/**
* Creates an animator that modifies Launcher as a result from {@link #getWallpaperOpenRunner}.
*/
private void createLauncherResumeAnimation(AnimatorSet anim) {
if (mLauncher.isInState(LauncherState.ALL_APPS)) {
Pair<AnimatorSet, Runnable> contentAnimator =
getLauncherContentAnimator(false /* isAppOpening */,
new float[] {-mContentTransY, 0});
contentAnimator.first.setStartDelay(LAUNCHER_RESUME_START_DELAY);
anim.play(contentAnimator.first);
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
contentAnimator.second.run();
}
});
} else {
AnimatorSet workspaceAnimator = new AnimatorSet();
mDragLayer.setTranslationY(-mWorkspaceTransY);;
workspaceAnimator.play(ObjectAnimator.ofFloat(mDragLayer, View.TRANSLATION_Y,
-mWorkspaceTransY, 0));
mDragLayerAlpha.setValue(0);
workspaceAnimator.play(ObjectAnimator.ofFloat(
mDragLayerAlpha, MultiValueAlpha.VALUE, 0, 1f));
workspaceAnimator.setStartDelay(LAUNCHER_RESUME_START_DELAY);
workspaceAnimator.setDuration(333);
workspaceAnimator.setInterpolator(Interpolators.DEACCEL_1_7);
mDragLayer.getScrim().hideSysUiScrim(true);
// Pause page indicator animations as they lead to layer trashing.
mLauncher.getWorkspace().getPageIndicator().pauseAnimations();
mDragLayer.setLayerType(View.LAYER_TYPE_HARDWARE, null);
workspaceAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
resetContentView();
}
});
anim.play(workspaceAnimator);
}
}
private void resetContentView() {
mLauncher.getWorkspace().getPageIndicator().skipAnimationsToEnd();
mDragLayerAlpha.setValue(1f);
mDragLayer.setLayerType(View.LAYER_TYPE_NONE, null);
mDragLayer.setTranslationY(0f);
mDragLayer.getScrim().hideSysUiScrim(false);
}
private boolean hasControlRemoteAppTransitionPermission() {
return mLauncher.checkSelfPermission(CONTROL_REMOTE_APP_TRANSITION_PERMISSION)
== PackageManager.PERMISSION_GRANTED;
}
/**
* Remote animation runner for animation from the app to Launcher, including recents.
*/
class WallpaperOpenLauncherAnimationRunner extends LauncherAnimationRunner {
private final boolean mFromUnlock;
public WallpaperOpenLauncherAnimationRunner(Handler handler, boolean startAtFrontOfQueue,
boolean fromUnlock) {
super(handler, startAtFrontOfQueue);
mFromUnlock = fromUnlock;
}
@Override
public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats,
LauncherAnimationRunner.AnimationResult result) {
if (!mLauncher.hasBeenResumed()) {
// If launcher is not resumed, wait until new async-frame after resume
mLauncher.addOnResumeCallback(() ->
postAsyncCallback(mHandler, () ->
onCreateAnimation(targetCompats, result)));
return;
}
if (mLauncher.hasSomeInvisibleFlag(PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION)) {
mLauncher.addForceInvisibleFlag(INVISIBLE_BY_PENDING_FLAGS);
mLauncher.getStateManager().moveToRestState();
}
AnimatorSet anim = null;
RemoteAnimationProvider provider = mRemoteAnimationProvider;
if (provider != null) {
anim = provider.createWindowAnimation(targetCompats);
}
if (anim == null) {
anim = new AnimatorSet();
anim.play(mFromUnlock
? getUnlockWindowAnimator(targetCompats)
: getClosingWindowAnimators(targetCompats));
// Normally, we run the launcher content animation when we are transitioning
// home, but if home is already visible, then we don't want to animate the
// contents of launcher unless we know that we are animating home as a result
// of the home button press with quickstep, which will result in launcher being
// started on touch down, prior to the animation home (and won't be in the
// targets list because it is already visible). In that case, we force
// invisibility on touch down, and only reset it after the animation to home
// is initialized.
if (launcherIsATargetWithMode(targetCompats, MODE_OPENING)
|| mLauncher.isForceInvisible()) {
// Only register the content animation for cancellation when state changes
mLauncher.getStateManager().setCurrentAnimation(anim);
if (mFromUnlock) {
Pair<AnimatorSet, Runnable> contentAnimator =
getLauncherContentAnimator(false /* isAppOpening */,
new float[] {mContentTransY, 0});
contentAnimator.first.setStartDelay(0);
anim.play(contentAnimator.first);
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
contentAnimator.second.run();
}
});
} else {
createLauncherResumeAnimation(anim);
}
}
}
mLauncher.clearForceInvisibleFlag(INVISIBLE_ALL);
result.setAnimation(anim);
}
}
}

View File

@@ -1,82 +0,0 @@
/*
* Copyright (C) 2019 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.uiuios.proxy;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender.SendIntentException;
import android.os.Bundle;
import android.util.Log;
public class ProxyActivityStarter extends Activity {
private static final String TAG = "ProxyActivityStarter";
public static final String EXTRA_PARAMS = "start-activity-params";
private StartActivityParams mParams;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setVisible(false);
mParams = getIntent().getParcelableExtra(EXTRA_PARAMS);
if (mParams == null) {
Log.d(TAG, "Proxy activity started without params");
finishAndRemoveTask();
return;
}
if (savedInstanceState != null) {
// Already started the activity. Just wait for the result.
return;
}
if (mParams.intent != null) {
startActivityForResult(mParams.intent, mParams.requestCode, mParams.options);
return;
} else if (mParams.intentSender != null) {
try {
startIntentSenderForResult(mParams.intentSender, mParams.requestCode,
mParams.fillInIntent, mParams.flagsMask, mParams.flagsValues,
mParams.extraFlags,
mParams.options);
return;
} catch (SendIntentException e) {
mParams.deliverResult(this, RESULT_CANCELED, null);
}
}
finishAndRemoveTask();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == mParams.requestCode) {
mParams.deliverResult(this, resultCode, data);
}
finishAndRemoveTask();
}
public static Intent getLaunchIntent(Context context, StartActivityParams params) {
return new Intent(context, ProxyActivityStarter.class)
.putExtra(EXTRA_PARAMS, params)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
| Intent.FLAG_ACTIVITY_CLEAR_TASK);
}
}

View File

@@ -1,109 +0,0 @@
/*
* Copyright (C) 2019 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.uiuios.proxy;
import android.app.Activity;
import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
public class StartActivityParams implements Parcelable {
private static final String TAG = "StartActivityParams";
private final PendingIntent mPICallback;
public final int requestCode;
public Intent intent;
public IntentSender intentSender;
public Intent fillInIntent;
public int flagsMask;
public int flagsValues;
public int extraFlags;
public Bundle options;
public StartActivityParams(Activity activity, int requestCode) {
this(activity.createPendingResult(requestCode, new Intent(),
PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT), requestCode);
}
public StartActivityParams(PendingIntent pendingIntent, int requestCode) {
this.mPICallback = pendingIntent;
this.requestCode = requestCode;
}
private StartActivityParams(Parcel parcel) {
mPICallback = parcel.readTypedObject(PendingIntent.CREATOR);
requestCode = parcel.readInt();
intent = parcel.readTypedObject(Intent.CREATOR);
intentSender = parcel.readTypedObject(IntentSender.CREATOR);
fillInIntent = parcel.readTypedObject(Intent.CREATOR);
flagsMask = parcel.readInt();
flagsValues = parcel.readInt();
extraFlags = parcel.readInt();
options = parcel.readBundle();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeTypedObject(mPICallback, flags);
parcel.writeInt(requestCode);
parcel.writeTypedObject(intent, flags);
parcel.writeTypedObject(intentSender, flags);
parcel.writeTypedObject(fillInIntent, flags);
parcel.writeInt(flagsMask);
parcel.writeInt(flagsValues);
parcel.writeInt(extraFlags);
parcel.writeBundle(options);
}
public void deliverResult(Context context, int resultCode, Intent data) {
try {
if (mPICallback != null) {
mPICallback.send(context, resultCode, data);
}
} catch (CanceledException e) {
Log.e(TAG, "Unable to send back result", e);
}
}
public static final Parcelable.Creator<StartActivityParams> CREATOR =
new Parcelable.Creator<StartActivityParams>() {
public StartActivityParams createFromParcel(Parcel source) {
return new StartActivityParams(source);
}
public StartActivityParams[] newArray(int size) {
return new StartActivityParams[size];
}
};
}

View File

@@ -1,71 +0,0 @@
/*
* Copyright (C) 2018 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.uiuios.uioverrides;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import com.android.uiuios.Launcher;
import com.android.uiuios.LauncherState;
import com.android.uiuios.LauncherStateManager;
import com.android.uiuios.anim.AnimatorSetBuilder;
import com.android.quickstep.OverviewInteractionState;
public class BackButtonAlphaHandler implements LauncherStateManager.StateHandler {
private static final String TAG = "BackButtonAlphaHandler";
private final Launcher mLauncher;
private final OverviewInteractionState mOverviewInteractionState;
public BackButtonAlphaHandler(Launcher launcher) {
mLauncher = launcher;
mOverviewInteractionState = OverviewInteractionState.INSTANCE.get(mLauncher);
}
@Override
public void setState(LauncherState state) {
UiFactory.onLauncherStateOrFocusChanged(mLauncher);
}
@Override
public void setStateWithAnimation(LauncherState toState,
AnimatorSetBuilder builder, LauncherStateManager.AnimationConfig config) {
if (!config.playNonAtomicComponent()) {
return;
}
float fromAlpha = mOverviewInteractionState.getBackButtonAlpha();
float toAlpha = toState.hideBackButton ? 0 : 1;
if (Float.compare(fromAlpha, toAlpha) != 0) {
ValueAnimator anim = ValueAnimator.ofFloat(fromAlpha, toAlpha);
anim.setDuration(config.duration);
anim.addUpdateListener(valueAnimator -> {
final float alpha = (float) valueAnimator.getAnimatedValue();
mOverviewInteractionState.setBackButtonAlpha(alpha, false);
});
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
// Reapply the final alpha in case some state (e.g. window focus) changed.
UiFactory.onLauncherStateOrFocusChanged(mLauncher);
}
});
builder.play(anim);
}
}
}

View File

@@ -1,121 +0,0 @@
/*
* Copyright (C) 2019 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.uiuios.uioverrides;
import static com.android.uiuios.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.uiuios.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
import static com.android.uiuios.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE;
import static com.android.uiuios.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_X;
import static com.android.uiuios.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_Y;
import static com.android.uiuios.anim.AnimatorSetBuilder.FLAG_DONT_ANIMATE_OVERVIEW;
import static com.android.uiuios.anim.Interpolators.AGGRESSIVE_EASE_IN_OUT;
import static com.android.uiuios.anim.Interpolators.LINEAR;
import android.util.FloatProperty;
import android.view.View;
import android.view.animation.Interpolator;
import com.android.uiuios.Launcher;
import com.android.uiuios.LauncherState;
import com.android.uiuios.LauncherState.ScaleAndTranslation;
import com.android.uiuios.LauncherStateManager.AnimationConfig;
import com.android.uiuios.LauncherStateManager.StateHandler;
import com.android.uiuios.anim.AnimatorSetBuilder;
import com.android.uiuios.anim.PropertySetter;
import androidx.annotation.NonNull;
/**
* State handler for recents view. Manages UI changes and animations for recents view based off the
* current {@link LauncherState}.
*
* @param <T> the recents view
*/
public abstract class BaseRecentsViewStateController<T extends View>
implements StateHandler {
protected final T mRecentsView;
protected final Launcher mLauncher;
public BaseRecentsViewStateController(@NonNull Launcher launcher) {
mLauncher = launcher;
mRecentsView = launcher.getOverviewPanel();
}
@Override
public void setState(@NonNull LauncherState state) {
ScaleAndTranslation scaleAndTranslation = state
.getOverviewScaleAndTranslation(mLauncher);
SCALE_PROPERTY.set(mRecentsView, scaleAndTranslation.scale);
float translationX = scaleAndTranslation.translationX;
if (mRecentsView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
translationX = -translationX;
}
mRecentsView.setTranslationX(translationX);
mRecentsView.setTranslationY(scaleAndTranslation.translationY);
getContentAlphaProperty().set(mRecentsView, state.overviewUi ? 1f : 0);
}
@Override
public final void setStateWithAnimation(@NonNull final LauncherState toState,
@NonNull AnimatorSetBuilder builder, @NonNull AnimationConfig config) {
boolean playAtomicOverviewComponent = config.playAtomicOverviewScaleComponent()
|| config.playAtomicOverviewPeekComponent();
if (!playAtomicOverviewComponent) {
// The entire recents animation is played atomically.
return;
}
if (builder.hasFlag(FLAG_DONT_ANIMATE_OVERVIEW)) {
return;
}
setStateWithAnimationInternal(toState, builder, config);
}
/**
* Core logic for animating the recents view UI.
*
* @param toState state to animate to
* @param builder animator set builder
* @param config current animation config
*/
void setStateWithAnimationInternal(@NonNull final LauncherState toState,
@NonNull AnimatorSetBuilder builder, @NonNull AnimationConfig config) {
PropertySetter setter = config.getPropertySetter(builder);
ScaleAndTranslation scaleAndTranslation = toState.getOverviewScaleAndTranslation(mLauncher);
Interpolator scaleInterpolator = builder.getInterpolator(ANIM_OVERVIEW_SCALE, LINEAR);
setter.setFloat(mRecentsView, SCALE_PROPERTY, scaleAndTranslation.scale, scaleInterpolator);
Interpolator translateXInterpolator = builder.getInterpolator(
ANIM_OVERVIEW_TRANSLATE_X, LINEAR);
Interpolator translateYInterpolator = builder.getInterpolator(
ANIM_OVERVIEW_TRANSLATE_Y, LINEAR);
float translationX = scaleAndTranslation.translationX;
if (mRecentsView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
translationX = -translationX;
}
setter.setFloat(mRecentsView, View.TRANSLATION_X, translationX, translateXInterpolator);
setter.setFloat(mRecentsView, View.TRANSLATION_Y, scaleAndTranslation.translationY,
translateYInterpolator);
setter.setFloat(mRecentsView, getContentAlphaProperty(), toState.overviewUi ? 1 : 0,
builder.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT));
}
/**
* Get property for content alpha for the recents view.
*
* @return the float property for the view's content alpha
*/
abstract FloatProperty getContentAlphaProperty();
}

View File

@@ -1,48 +0,0 @@
/*
* Copyright (C) 2018 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.uiuios.uioverrides;
import android.content.Context;
import android.os.Handler;
import com.android.systemui.shared.system.RotationWatcher;
/**
* Utility class for listening for rotation changes
*/
public class DisplayRotationListener extends RotationWatcher {
private final Runnable mCallback;
private Handler mHandler;
public DisplayRotationListener(Context context, Runnable callback) {
super(context);
mCallback = callback;
}
@Override
public void enable() {
if (mHandler == null) {
mHandler = new Handler();
}
super.enable();
}
@Override
protected void onRotationChanged(int i) {
mHandler.post(mCallback);
}
}

View File

@@ -1,247 +0,0 @@
/*
* Copyright (C) 2017 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.uiuios.uioverrides;
import static android.app.Activity.RESULT_CANCELED;
import static com.android.uiuios.AbstractFloatingView.TYPE_ALL;
import static com.android.uiuios.AbstractFloatingView.TYPE_HIDE_BACK_BUTTON;
import static com.android.uiuios.LauncherState.ALL_APPS;
import static com.android.uiuios.LauncherState.NORMAL;
import static com.android.uiuios.LauncherState.OVERVIEW;
import static com.android.uiuios.allapps.DiscoveryBounce.BOUNCE_MAX_COUNT;
import static com.android.uiuios.allapps.DiscoveryBounce.HOME_BOUNCE_COUNT;
import static com.android.uiuios.allapps.DiscoveryBounce.HOME_BOUNCE_SEEN;
import static com.android.uiuios.allapps.DiscoveryBounce.SHELF_BOUNCE_COUNT;
import static com.android.uiuios.allapps.DiscoveryBounce.SHELF_BOUNCE_SEEN;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.util.Base64;
import com.android.uiuios.AbstractFloatingView;
import com.android.uiuios.Launcher;
import com.android.uiuios.LauncherState;
import com.android.uiuios.LauncherState.ScaleAndTranslation;
import com.android.uiuios.LauncherStateManager;
import com.android.uiuios.LauncherStateManager.StateHandler;
import com.android.uiuios.QuickstepAppTransitionManagerImpl;
import com.android.uiuios.Utilities;
import com.android.uiuios.proxy.ProxyActivityStarter;
import com.android.uiuios.proxy.StartActivityParams;
import com.android.quickstep.OverviewInteractionState;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
import com.android.quickstep.util.RemoteFadeOutAnimationListener;
import com.android.systemui.shared.system.ActivityCompat;
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.util.zip.Deflater;
public class UiFactory extends RecentsUiFactory {
public static Runnable enableLiveUIChanges(Launcher launcher) {
NavigationModeChangeListener listener = m -> {
launcher.getDragLayer().recreateControllers();
launcher.getRotationHelper().setRotationHadDifferentUI(m != Mode.NO_BUTTON);
};
SysUINavigationMode mode = SysUINavigationMode.INSTANCE.get(launcher);
SysUINavigationMode.Mode m = mode.addModeChangeListener(listener);
launcher.getRotationHelper().setRotationHadDifferentUI(m != Mode.NO_BUTTON);
return () -> mode.removeModeChangeListener(listener);
}
public static StateHandler[] getStateHandler(Launcher launcher) {
return new StateHandler[] {
launcher.getAllAppsController(),
launcher.getWorkspace(),
createRecentsViewStateController(launcher),
new BackButtonAlphaHandler(launcher)};
}
/**
* Sets the back button visibility based on the current state/window focus.
*/
public static void onLauncherStateOrFocusChanged(Launcher launcher) {
boolean shouldBackButtonBeHidden = launcher != null
&& launcher.getStateManager().getState().hideBackButton
&& launcher.hasWindowFocus();
if (shouldBackButtonBeHidden) {
// Show the back button if there is a floating view visible.
shouldBackButtonBeHidden = AbstractFloatingView.getTopOpenViewWithType(launcher,
TYPE_ALL & ~TYPE_HIDE_BACK_BUTTON) == null;
}
OverviewInteractionState.INSTANCE.get(launcher)
.setBackButtonAlpha(shouldBackButtonBeHidden ? 0 : 1, true /* animate */);
if (launcher != null && launcher.getDragLayer() != null) {
launcher.getRootView().setDisallowBackGesture(shouldBackButtonBeHidden);
}
}
public static void onCreate(Launcher launcher) {
if (!launcher.getSharedPrefs().getBoolean(HOME_BOUNCE_SEEN, false)) {
launcher.getStateManager().addStateListener(new LauncherStateManager.StateListener() {
@Override
public void onStateTransitionStart(LauncherState toState) {
}
@Override
public void onStateTransitionComplete(LauncherState finalState) {
boolean swipeUpEnabled = SysUINavigationMode.INSTANCE.get(launcher).getMode()
.hasGestures;
LauncherState prevState = launcher.getStateManager().getLastState();
if (((swipeUpEnabled && finalState == OVERVIEW) || (!swipeUpEnabled
&& finalState == ALL_APPS && prevState == NORMAL) || BOUNCE_MAX_COUNT <=
launcher.getSharedPrefs().getInt(HOME_BOUNCE_COUNT, 0))) {
launcher.getSharedPrefs().edit().putBoolean(HOME_BOUNCE_SEEN, true).apply();
launcher.getStateManager().removeStateListener(this);
}
}
});
}
if (!launcher.getSharedPrefs().getBoolean(SHELF_BOUNCE_SEEN, false)) {
launcher.getStateManager().addStateListener(new LauncherStateManager.StateListener() {
@Override
public void onStateTransitionStart(LauncherState toState) {
}
@Override
public void onStateTransitionComplete(LauncherState finalState) {
LauncherState prevState = launcher.getStateManager().getLastState();
if ((finalState == ALL_APPS && prevState == OVERVIEW) || BOUNCE_MAX_COUNT <=
launcher.getSharedPrefs().getInt(SHELF_BOUNCE_COUNT, 0)) {
launcher.getSharedPrefs().edit().putBoolean(SHELF_BOUNCE_SEEN, true).apply();
launcher.getStateManager().removeStateListener(this);
}
}
});
}
}
public static void onEnterAnimationComplete(Context context) {
// After the transition to home, enable the high-res thumbnail loader if it wasn't enabled
// as a part of quickstep, so that high-res thumbnails can load the next time we enter
// overview
RecentsModel.INSTANCE.get(context).getThumbnailCache()
.getHighResLoadingState().setVisible(true);
}
public static void onTrimMemory(Context context, int level) {
RecentsModel model = RecentsModel.INSTANCE.get(context);
if (model != null) {
model.onTrimMemory(level);
}
}
public static void useFadeOutAnimationForLauncherStart(Launcher launcher,
CancellationSignal cancellationSignal) {
QuickstepAppTransitionManagerImpl appTransitionManager =
(QuickstepAppTransitionManagerImpl) launcher.getAppTransitionManager();
appTransitionManager.setRemoteAnimationProvider((targets) -> {
// On the first call clear the reference.
cancellationSignal.cancel();
ValueAnimator fadeAnimation = ValueAnimator.ofFloat(1, 0);
fadeAnimation.addUpdateListener(new RemoteFadeOutAnimationListener(targets));
AnimatorSet anim = new AnimatorSet();
anim.play(fadeAnimation);
return anim;
}, cancellationSignal);
}
public static boolean dumpActivity(Activity activity, PrintWriter writer) {
if (!Utilities.IS_DEBUG_DEVICE) {
return false;
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
if (!(new ActivityCompat(activity).encodeViewHierarchy(out))) {
return false;
}
Deflater deflater = new Deflater();
deflater.setInput(out.toByteArray());
deflater.finish();
out.reset();
byte[] buffer = new byte[1024];
while (!deflater.finished()) {
int count = deflater.deflate(buffer); // returns the generated code... index
out.write(buffer, 0, count);
}
writer.println("--encoded-view-dump-v0--");
writer.println(Base64.encodeToString(
out.toByteArray(), Base64.NO_WRAP | Base64.NO_PADDING));
return true;
}
public static boolean startIntentSenderForResult(Activity activity, IntentSender intent,
int requestCode, Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags,
Bundle options) {
StartActivityParams params = new StartActivityParams(activity, requestCode);
params.intentSender = intent;
params.fillInIntent = fillInIntent;
params.flagsMask = flagsMask;
params.flagsValues = flagsValues;
params.extraFlags = extraFlags;
params.options = options;
((Context) activity).startActivity(ProxyActivityStarter.getLaunchIntent(activity, params));
return true;
}
public static boolean startActivityForResult(Activity activity, Intent intent, int requestCode,
Bundle options) {
StartActivityParams params = new StartActivityParams(activity, requestCode);
params.intent = intent;
params.options = options;
activity.startActivity(ProxyActivityStarter.getLaunchIntent(activity, params));
return true;
}
/**
* Removes any active ProxyActivityStarter task and sends RESULT_CANCELED to Launcher.
*
* ProxyActivityStarter is started with clear task to reset the task after which it removes the
* task itself.
*/
public static void resetPendingActivityResults(Launcher launcher, int requestCode) {
launcher.onActivityResult(requestCode, RESULT_CANCELED, null);
launcher.startActivity(ProxyActivityStarter.getLaunchIntent(launcher, null));
}
public static ScaleAndTranslation getOverviewScaleAndTranslationForNormalState(Launcher l) {
if (SysUINavigationMode.getMode(l) == Mode.NO_BUTTON) {
float offscreenTranslationX = l.getDeviceProfile().widthPx
- l.getOverviewPanel().getPaddingStart();
return new ScaleAndTranslation(1f, offscreenTranslationX, 0f);
}
return new ScaleAndTranslation(1.1f, 0f, 0f);
}
}

View File

@@ -1,123 +0,0 @@
/*
* Copyright (C) 2018 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.uiuios.uioverrides;
import static android.app.WallpaperManager.FLAG_SYSTEM;
import android.annotation.TargetApi;
import android.app.WallpaperColors;
import android.app.WallpaperManager;
import android.app.WallpaperManager.OnColorsChangedListener;
import android.content.Context;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import com.android.systemui.shared.system.TonalCompat;
import com.android.systemui.shared.system.TonalCompat.ExtractionInfo;
import java.util.ArrayList;
@TargetApi(Build.VERSION_CODES.P)
public class WallpaperColorInfo implements OnColorsChangedListener {
private static final int MAIN_COLOR_LIGHT = 0xffdadce0;
private static final int MAIN_COLOR_DARK = 0xff202124;
private static final int MAIN_COLOR_REGULAR = 0xff000000;
private static final Object sInstanceLock = new Object();
private static WallpaperColorInfo sInstance;
public static WallpaperColorInfo getInstance(Context context) {
synchronized (sInstanceLock) {
if (sInstance == null) {
sInstance = new WallpaperColorInfo(context.getApplicationContext());
}
return sInstance;
}
}
private final ArrayList<OnChangeListener> mListeners = new ArrayList<>();
private final WallpaperManager mWallpaperManager;
private final TonalCompat mTonalCompat;
private ExtractionInfo mExtractionInfo;
private OnChangeListener[] mTempListeners = new OnChangeListener[0];
private WallpaperColorInfo(Context context) {
mWallpaperManager = context.getSystemService(WallpaperManager.class);
mTonalCompat = new TonalCompat(context);
mWallpaperManager.addOnColorsChangedListener(this, new Handler(Looper.getMainLooper()));
update(mWallpaperManager.getWallpaperColors(FLAG_SYSTEM));
}
public int getMainColor() {
return mExtractionInfo.mainColor;
}
public int getSecondaryColor() {
return mExtractionInfo.secondaryColor;
}
public boolean isDark() {
return mExtractionInfo.supportsDarkTheme;
}
public boolean supportsDarkText() {
return mExtractionInfo.supportsDarkText;
}
public boolean isMainColorDark() {
return mExtractionInfo.mainColor == MAIN_COLOR_DARK;
}
@Override
public void onColorsChanged(WallpaperColors colors, int which) {
if ((which & FLAG_SYSTEM) != 0) {
update(colors);
notifyChange();
}
}
private void update(WallpaperColors wallpaperColors) {
mExtractionInfo = mTonalCompat.extractDarkColors(wallpaperColors);
}
public void addOnChangeListener(OnChangeListener listener) {
mListeners.add(listener);
}
public void removeOnChangeListener(OnChangeListener listener) {
mListeners.remove(listener);
}
private void notifyChange() {
// Create a new array to avoid concurrent modification when the activity destroys itself.
mTempListeners = mListeners.toArray(mTempListeners);
for (OnChangeListener listener : mTempListeners) {
if (listener != null) {
listener.onExtractedColorsChanged(this);
}
}
}
public interface OnChangeListener {
void onExtractedColorsChanged(WallpaperColorInfo wallpaperColorInfo);
}
}

View File

@@ -1,73 +0,0 @@
/*
* Copyright (C) 2018 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.uiuios.uioverrides.plugins;
import android.content.ComponentName;
import android.content.Context;
import android.content.SharedPreferences;
import com.android.uiuios.Utilities;
import com.android.systemui.shared.plugins.PluginEnabler;
import androidx.preference.PreferenceDataStore;
public class PluginEnablerImpl extends PreferenceDataStore implements PluginEnabler {
private static final String PREFIX_PLUGIN_ENABLED = "PLUGIN_ENABLED_";
final private SharedPreferences mSharedPrefs;
public PluginEnablerImpl(Context context) {
mSharedPrefs = Utilities.getDevicePrefs(context);
}
@Override
public void setEnabled(ComponentName component) {
setState(component, true);
}
@Override
public void setDisabled(ComponentName component, int reason) {
setState(component, reason == ENABLED);
}
private void setState(ComponentName component, boolean enabled) {
putBoolean(pluginEnabledKey(component), enabled);
}
@Override
public boolean isEnabled(ComponentName component) {
return getBoolean(pluginEnabledKey(component), true);
}
@Override
public int getDisableReason(ComponentName componentName) {
return isEnabled(componentName) ? ENABLED : DISABLED_MANUALLY;
}
@Override
public void putBoolean(String key, boolean value) {
mSharedPrefs.edit().putBoolean(key, value).apply();
}
@Override
public boolean getBoolean(String key, boolean defValue) {
return mSharedPrefs.getBoolean(key, defValue);
}
static String pluginEnabledKey(ComponentName cn) {
return PREFIX_PLUGIN_ENABLED + cn.flattenToString();
}
}

View File

@@ -1,46 +0,0 @@
/*
* Copyright (C) 2018 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.uiuios.uioverrides.plugins;
import android.content.Context;
import android.os.Looper;
import com.android.uiuios.LauncherModel;
import com.android.systemui.shared.plugins.PluginInitializer;
public class PluginInitializerImpl implements PluginInitializer {
@Override
public Looper getBgLooper() {
return LauncherModel.getWorkerLooper();
}
@Override
public void onPluginManagerInit() {
}
@Override
public String[] getWhitelistedPlugins(Context context) {
return new String[0];
}
@Override
public PluginEnablerImpl getPluginEnabler(Context context) {
return new PluginEnablerImpl(context);
}
@Override
public void handleWtfs() {
}
}

View File

@@ -1,78 +0,0 @@
/*
* Copyright (C) 2018 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.uiuios.uioverrides.plugins;
import android.content.ComponentName;
import android.content.Context;
import com.android.uiuios.util.MainThreadInitializedObject;
import com.android.systemui.plugins.Plugin;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.shared.plugins.PluginManagerImpl;
import com.android.systemui.shared.plugins.PluginPrefs;
import java.util.Set;
public class PluginManagerWrapper {
public static final MainThreadInitializedObject<PluginManagerWrapper> INSTANCE =
new MainThreadInitializedObject<>(PluginManagerWrapper::new);
public static final String PLUGIN_CHANGED = PluginManager.PLUGIN_CHANGED;
private final Context mContext;
private final PluginManager mPluginManager;
private final PluginEnablerImpl mPluginEnabler;
private PluginManagerWrapper(Context c) {
mContext = c;
PluginInitializerImpl pluginInitializer = new PluginInitializerImpl();
mPluginManager = new PluginManagerImpl(c, pluginInitializer);
mPluginEnabler = pluginInitializer.getPluginEnabler(c);
}
public PluginEnablerImpl getPluginEnabler() {
return mPluginEnabler;
}
public void addPluginListener(PluginListener<? extends Plugin> listener, Class<?> pluginClass) {
addPluginListener(listener, pluginClass, false);
}
public void addPluginListener(PluginListener<? extends Plugin> listener, Class<?> pluginClass,
boolean allowMultiple) {
mPluginManager.addPluginListener(listener, pluginClass, allowMultiple);
}
public void removePluginListener(PluginListener<? extends Plugin> listener) {
mPluginManager.removePluginListener(listener);
}
public Set<String> getPluginActions() {
return new PluginPrefs(mContext).getPluginList();
}
/**
* Returns the string key used to store plugin enabled/disabled setting
*/
public static String pluginEnabledKey(ComponentName cn) {
return PluginEnablerImpl.pluginEnabledKey(cn);
}
public static boolean hasPlugins(Context context) {
return PluginPrefs.hasPlugins(context);
}
}

View File

@@ -1,97 +0,0 @@
/*
* Copyright (C) 2017 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.uiuios.uioverrides.states;
import static com.android.uiuios.LauncherAnimUtils.ALL_APPS_TRANSITION_MS;
import static com.android.uiuios.anim.Interpolators.DEACCEL_2;
import com.android.uiuios.AbstractFloatingView;
import com.android.uiuios.Launcher;
import com.android.uiuios.LauncherState;
import com.android.uiuios.allapps.AllAppsContainerView;
import com.android.uiuios.userevent.nano.LauncherLogProto.ContainerType;
import com.android.quickstep.SysUINavigationMode;
/**
* Definition for AllApps state
*/
public class AllAppsState extends LauncherState {
private static final int STATE_FLAGS = FLAG_DISABLE_ACCESSIBILITY;
private static final PageAlphaProvider PAGE_ALPHA_PROVIDER = new PageAlphaProvider(DEACCEL_2) {
@Override
public float getPageAlpha(int pageIndex) {
return 0;
}
};
public AllAppsState(int id) {
super(id, ContainerType.ALLAPPS, ALL_APPS_TRANSITION_MS, STATE_FLAGS);
}
@Override
public void onStateEnabled(Launcher launcher) {
AbstractFloatingView.closeAllOpenViews(launcher);
dispatchWindowStateChanged(launcher);
}
@Override
public String getDescription(Launcher launcher) {
AllAppsContainerView appsView = launcher.getAppsView();
return appsView.getDescription();
}
@Override
public float getVerticalProgress(Launcher launcher) {
return 0f;
}
@Override
public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) {
ScaleAndTranslation scaleAndTranslation = LauncherState.OVERVIEW
.getWorkspaceScaleAndTranslation(launcher);
if (SysUINavigationMode.getMode(launcher) == SysUINavigationMode.Mode.NO_BUTTON) {
float normalScale = 1;
// Scale down halfway to where we'd be in overview, to prepare for a potential pause.
scaleAndTranslation.scale = (scaleAndTranslation.scale + normalScale) / 2;
} else {
scaleAndTranslation.scale = 1;
}
return scaleAndTranslation;
}
@Override
public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) {
return PAGE_ALPHA_PROVIDER;
}
@Override
public int getVisibleElements(Launcher launcher) {
return ALL_APPS_HEADER | ALL_APPS_HEADER_EXTRA | ALL_APPS_CONTENT;
}
@Override
public ScaleAndTranslation getOverviewScaleAndTranslation(Launcher launcher) {
float slightParallax = -launcher.getDeviceProfile().allAppsCellHeightPx * 0.3f;
return new ScaleAndTranslation(0.9f, 0f, slightParallax);
}
@Override
public LauncherState getHistoryForState(LauncherState previousState) {
return previousState == OVERVIEW ? OVERVIEW : NORMAL;
}
}

View File

@@ -1,79 +0,0 @@
package com.android.uiuios.uioverrides.touchcontrollers;
import static com.android.uiuios.LauncherState.NORMAL;
import static com.android.uiuios.LauncherState.OVERVIEW;
import static com.android.uiuios.Utilities.EDGE_NAV_BAR;
import android.view.MotionEvent;
import com.android.uiuios.AbstractFloatingView;
import com.android.uiuios.Launcher;
import com.android.uiuios.LauncherState;
import com.android.uiuios.LauncherStateManager.AnimationComponents;
import com.android.uiuios.touch.AbstractStateChangeTouchController;
import com.android.uiuios.touch.SwipeDetector;
import com.android.uiuios.userevent.nano.LauncherLogProto;
import com.android.uiuios.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.quickstep.RecentsModel;
/**
* Touch controller for handling edge swipes in landscape/seascape UI
*/
public class LandscapeEdgeSwipeController extends AbstractStateChangeTouchController {
private static final String TAG = "LandscapeEdgeSwipeCtrl";
public LandscapeEdgeSwipeController(Launcher l) {
super(l, SwipeDetector.HORIZONTAL);
}
@Override
protected boolean canInterceptTouch(MotionEvent ev) {
if (mCurrentAnimation != null) {
// If we are already animating from a previous state, we can intercept.
return true;
}
if (AbstractFloatingView.getTopOpenView(mLauncher) != null) {
return false;
}
return mLauncher.isInState(NORMAL) && (ev.getEdgeFlags() & EDGE_NAV_BAR) != 0;
}
@Override
protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) {
boolean draggingFromNav = mLauncher.getDeviceProfile().isSeascape() == isDragTowardPositive;
return draggingFromNav ? OVERVIEW : NORMAL;
}
@Override
protected int getLogContainerTypeForNormalState() {
return LauncherLogProto.ContainerType.NAVBAR;
}
@Override
protected float getShiftRange() {
return mLauncher.getDragLayer().getWidth();
}
@Override
protected float initCurrentAnimation(@AnimationComponents int animComponent) {
float range = getShiftRange();
long maxAccuracy = (long) (2 * range);
mCurrentAnimation = mLauncher.getStateManager().createAnimationToNewWorkspace(mToState,
maxAccuracy, animComponent);
return (mLauncher.getDeviceProfile().isSeascape() ? 2 : -2) / range;
}
@Override
protected int getDirectionForLog() {
return mLauncher.getDeviceProfile().isSeascape() ? Direction.RIGHT : Direction.LEFT;
}
@Override
protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) {
super.onSwipeInteractionCompleted(targetState, logAction);
if (mStartState == NORMAL && targetState == OVERVIEW) {
RecentsModel.INSTANCE.get(mLauncher).onOverviewShown(true, TAG);
}
}
}

View File

@@ -1,313 +0,0 @@
/*
* Copyright (C) 2018 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.uiuios.uioverrides.touchcontrollers;
import static com.android.uiuios.AbstractFloatingView.TYPE_ACCESSIBLE;
import static com.android.uiuios.LauncherState.ALL_APPS;
import static com.android.uiuios.LauncherState.NORMAL;
import static com.android.uiuios.LauncherState.OVERVIEW;
import static com.android.uiuios.anim.AnimatorSetBuilder.ANIM_ALL_APPS_FADE;
import static com.android.uiuios.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
import static com.android.uiuios.anim.AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS;
import static com.android.uiuios.anim.Interpolators.ACCEL;
import static com.android.uiuios.anim.Interpolators.DEACCEL;
import static com.android.uiuios.anim.Interpolators.LINEAR;
import static com.android.uiuios.config.FeatureFlags.QUICKSTEP_SPRINGS;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.view.MotionEvent;
import android.view.animation.Interpolator;
import com.android.uiuios.AbstractFloatingView;
import com.android.uiuios.DeviceProfile;
import com.android.uiuios.Launcher;
import com.android.uiuios.LauncherState;
import com.android.uiuios.LauncherStateManager.AnimationComponents;
import com.android.uiuios.allapps.AllAppsTransitionController;
import com.android.uiuios.anim.AnimatorPlaybackController;
import com.android.uiuios.anim.AnimatorSetBuilder;
import com.android.uiuios.anim.Interpolators;
import com.android.uiuios.touch.AbstractStateChangeTouchController;
import com.android.uiuios.touch.SwipeDetector;
import com.android.uiuios.uioverrides.states.OverviewState;
import com.android.uiuios.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.uiuios.userevent.nano.LauncherLogProto.ContainerType;
import com.android.quickstep.OverviewInteractionState;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.TouchInteractionService;
import com.android.quickstep.util.LayoutUtils;
import com.android.systemui.shared.system.QuickStepContract;
/**
* Touch controller for handling various state transitions in portrait UI.
*/
public class PortraitStatesTouchController extends AbstractStateChangeTouchController {
private static final String TAG = "PortraitStatesTouchCtrl";
/**
* The progress at which all apps content will be fully visible when swiping up from overview.
*/
private static final float ALL_APPS_CONTENT_FADE_THRESHOLD = 0.08f;
/**
* The progress at which recents will begin fading out when swiping up from overview.
*/
private static final float RECENTS_FADE_THRESHOLD = 0.88f;
private final PortraitOverviewStateTouchHelper mOverviewPortraitStateTouchHelper;
private final InterpolatorWrapper mAllAppsInterpolatorWrapper = new InterpolatorWrapper();
private final boolean mAllowDragToOverview;
// If true, we will finish the current animation instantly on second touch.
private boolean mFinishFastOnSecondTouch;
public PortraitStatesTouchController(Launcher l, boolean allowDragToOverview) {
super(l, SwipeDetector.VERTICAL);
mOverviewPortraitStateTouchHelper = new PortraitOverviewStateTouchHelper(l);
mAllowDragToOverview = allowDragToOverview;
}
@Override
protected boolean canInterceptTouch(MotionEvent ev) {
if (mCurrentAnimation != null) {
if (mFinishFastOnSecondTouch) {
// TODO: Animate to finish instead.
mCurrentAnimation.skipToEnd();
}
AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
if (ev.getY() >= allAppsController.getShiftRange() * allAppsController.getProgress()) {
// If we are already animating from a previous state, we can intercept as long as
// the touch is below the current all apps progress (to allow for double swipe).
return true;
}
// Otherwise, make sure everything is settled and don't intercept so they can scroll
// recents, dismiss a task, etc.
if (mAtomicAnim != null) {
mAtomicAnim.end();
}
return false;
}
if (mLauncher.isInState(ALL_APPS)) {
// In all-apps only listen if the container cannot scroll itself
if (!mLauncher.getAppsView().shouldContainerScroll(ev)) {
return false;
}
} else if (mLauncher.isInState(OVERVIEW)) {
if (!mOverviewPortraitStateTouchHelper.canInterceptTouch(ev)) {
return false;
}
} else {
// If we are swiping to all apps instead of overview, allow it from anywhere.
boolean interceptAnywhere = mLauncher.isInState(NORMAL) && !mAllowDragToOverview;
// For all other states, only listen if the event originated below the hotseat height
if (!interceptAnywhere && !isTouchOverHotseat(mLauncher, ev)) {
return false;
}
}
if (AbstractFloatingView.getTopOpenViewWithType(mLauncher, TYPE_ACCESSIBLE) != null) {
return false;
}
return true;
}
@Override
protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) {
if (fromState == ALL_APPS && !isDragTowardPositive) {
// Should swipe down go to OVERVIEW instead?
return TouchInteractionService.isConnected() ?
mLauncher.getStateManager().getLastState() : NORMAL;
} else if (fromState == OVERVIEW) {
return isDragTowardPositive ? ALL_APPS : NORMAL;
} else if (fromState == NORMAL && isDragTowardPositive) {
int stateFlags = OverviewInteractionState.INSTANCE.get(mLauncher)
.getSystemUiStateFlags();
return mAllowDragToOverview && TouchInteractionService.isConnected()
&& (stateFlags & SYSUI_STATE_OVERVIEW_DISABLED) == 0
? OVERVIEW : ALL_APPS;
}
return fromState;
}
@Override
protected int getLogContainerTypeForNormalState() {
return ContainerType.HOTSEAT;
}
private AnimatorSetBuilder getNormalToOverviewAnimation() {
mAllAppsInterpolatorWrapper.baseInterpolator = LINEAR;
AnimatorSetBuilder builder = new AnimatorSetBuilder();
builder.setInterpolator(ANIM_VERTICAL_PROGRESS, mAllAppsInterpolatorWrapper);
return builder;
}
public static AnimatorSetBuilder getOverviewToAllAppsAnimation() {
AnimatorSetBuilder builder = new AnimatorSetBuilder();
builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(ACCEL,
0, ALL_APPS_CONTENT_FADE_THRESHOLD));
builder.setInterpolator(ANIM_OVERVIEW_FADE, Interpolators.clampToProgress(DEACCEL,
RECENTS_FADE_THRESHOLD, 1));
return builder;
}
private AnimatorSetBuilder getAllAppsToOverviewAnimation() {
AnimatorSetBuilder builder = new AnimatorSetBuilder();
builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(DEACCEL,
1 - ALL_APPS_CONTENT_FADE_THRESHOLD, 1));
builder.setInterpolator(ANIM_OVERVIEW_FADE, Interpolators.clampToProgress(ACCEL,
0f, 1 - RECENTS_FADE_THRESHOLD));
return builder;
}
@Override
protected AnimatorSetBuilder getAnimatorSetBuilderForStates(LauncherState fromState,
LauncherState toState) {
AnimatorSetBuilder builder = new AnimatorSetBuilder();
if (fromState == NORMAL && toState == OVERVIEW) {
builder = getNormalToOverviewAnimation();
} else if (fromState == OVERVIEW && toState == ALL_APPS) {
builder = getOverviewToAllAppsAnimation();
} else if (fromState == ALL_APPS && toState == OVERVIEW) {
builder = getAllAppsToOverviewAnimation();
}
return builder;
}
@Override
protected float initCurrentAnimation(@AnimationComponents int animComponents) {
float range = getShiftRange();
long maxAccuracy = (long) (2 * range);
float startVerticalShift = mFromState.getVerticalProgress(mLauncher) * range;
float endVerticalShift = mToState.getVerticalProgress(mLauncher) * range;
float totalShift = endVerticalShift - startVerticalShift;
final AnimatorSetBuilder builder = totalShift == 0 ? new AnimatorSetBuilder()
: getAnimatorSetBuilderForStates(mFromState, mToState);
updateAnimatorBuilderOnReinit(builder);
cancelPendingAnim();
if (mFromState == OVERVIEW && mToState == NORMAL
&& mOverviewPortraitStateTouchHelper.shouldSwipeDownReturnToApp()) {
// Reset the state manager, when changing the interaction mode
mLauncher.getStateManager().goToState(OVERVIEW, false /* animate */);
mPendingAnimation = mOverviewPortraitStateTouchHelper
.createSwipeDownToTaskAppAnimation(maxAccuracy);
mPendingAnimation.anim.setInterpolator(Interpolators.LINEAR);
Runnable onCancelRunnable = () -> {
cancelPendingAnim();
clearState();
};
mCurrentAnimation = AnimatorPlaybackController.wrap(mPendingAnimation.anim, maxAccuracy,
onCancelRunnable);
mLauncher.getStateManager().setCurrentUserControlledAnimation(mCurrentAnimation);
totalShift = LayoutUtils.getShelfTrackingDistance(mLauncher,
mLauncher.getDeviceProfile());
} else {
mCurrentAnimation = mLauncher.getStateManager()
.createAnimationToNewWorkspace(mToState, builder, maxAccuracy, this::clearState,
animComponents);
}
if (totalShift == 0) {
totalShift = Math.signum(mFromState.ordinal - mToState.ordinal)
* OverviewState.getDefaultSwipeHeight(mLauncher);
}
return 1 / totalShift;
}
/**
* Give subclasses the chance to update the animation when we re-initialize towards a new state.
*/
protected void updateAnimatorBuilderOnReinit(AnimatorSetBuilder builder) {
}
private void cancelPendingAnim() {
if (mPendingAnimation != null) {
mPendingAnimation.finish(false, Touch.SWIPE);
mPendingAnimation = null;
}
}
@Override
protected void updateSwipeCompleteAnimation(ValueAnimator animator, long expectedDuration,
LauncherState targetState, float velocity, boolean isFling) {
super.updateSwipeCompleteAnimation(animator, expectedDuration, targetState,
velocity, isFling);
handleFirstSwipeToOverview(animator, expectedDuration, targetState, velocity, isFling);
}
private void handleFirstSwipeToOverview(final ValueAnimator animator,
final long expectedDuration, final LauncherState targetState, final float velocity,
final boolean isFling) {
if (QUICKSTEP_SPRINGS.get() && mFromState == OVERVIEW && mToState == ALL_APPS
&& targetState == OVERVIEW) {
mFinishFastOnSecondTouch = true;
} else if (mFromState == NORMAL && mToState == OVERVIEW && targetState == OVERVIEW) {
mFinishFastOnSecondTouch = true;
if (isFling && expectedDuration != 0) {
// Update all apps interpolator to add a bit of overshoot starting from currFraction
final float currFraction = mCurrentAnimation.getProgressFraction();
mAllAppsInterpolatorWrapper.baseInterpolator = Interpolators.clampToProgress(
Interpolators.overshootInterpolatorForVelocity(velocity), currFraction, 1);
animator.setDuration(Math.min(expectedDuration, ATOMIC_DURATION))
.setInterpolator(LINEAR);
}
} else {
mFinishFastOnSecondTouch = false;
}
}
@Override
protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) {
super.onSwipeInteractionCompleted(targetState, logAction);
if (mStartState == NORMAL && targetState == OVERVIEW) {
RecentsModel.INSTANCE.get(mLauncher).onOverviewShown(true, TAG);
}
}
/**
* Whether the motion event is over the hotseat.
*
* @param launcher the launcher activity
* @param ev the event to check
* @return true if the event is over the hotseat
*/
static boolean isTouchOverHotseat(Launcher launcher, MotionEvent ev) {
DeviceProfile dp = launcher.getDeviceProfile();
int hotseatHeight = dp.hotseatBarSizePx + dp.getInsets().bottom;
return (ev.getY() >= (launcher.getDragLayer().getHeight() - hotseatHeight));
}
private static class InterpolatorWrapper implements Interpolator {
public TimeInterpolator baseInterpolator = LINEAR;
@Override
public float getInterpolation(float v) {
return baseInterpolator.getInterpolation(v);
}
}
}

View File

@@ -1,135 +0,0 @@
/*
* Copyright (C) 2018 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.uiuios.uioverrides.touchcontrollers;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_MOVE;
import android.os.RemoteException;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
import com.android.uiuios.AbstractFloatingView;
import com.android.uiuios.DeviceProfile;
import com.android.uiuios.Launcher;
import com.android.uiuios.LauncherState;
import com.android.uiuios.touch.TouchEventTranslator;
import com.android.uiuios.util.TouchController;
import com.android.quickstep.RecentsModel;
import com.android.systemui.shared.recents.ISystemUiProxy;
import java.io.PrintWriter;
/**
* TouchController for handling touch events that get sent to the StatusBar. Once the
* Once the event delta y passes the touch slop, the events start getting forwarded.
* All events are offset by initial Y value of the pointer.
*/
public class StatusBarTouchController implements TouchController {
private static final String TAG = "StatusBarController";
protected final Launcher mLauncher;
protected final TouchEventTranslator mTranslator;
private final float mTouchSlop;
private ISystemUiProxy mSysUiProxy;
private int mLastAction;
/* If {@code false}, this controller should not handle the input {@link MotionEvent}.*/
private boolean mCanIntercept;
public StatusBarTouchController(Launcher l) {
mLauncher = l;
// Guard against TAPs by increasing the touch slop.
mTouchSlop = 2 * ViewConfiguration.get(l).getScaledTouchSlop();
mTranslator = new TouchEventTranslator((MotionEvent ev)-> dispatchTouchEvent(ev));
}
@Override
public void dump(String prefix, PrintWriter writer) {
writer.println(prefix + "mCanIntercept:" + mCanIntercept);
writer.println(prefix + "mLastAction:" + MotionEvent.actionToString(mLastAction));
writer.println(prefix + "mSysUiProxy available:" + (mSysUiProxy != null));
}
private void dispatchTouchEvent(MotionEvent ev) {
try {
if (mSysUiProxy != null) {
mLastAction = ev.getActionMasked();
mSysUiProxy.onStatusBarMotionEvent(ev);
}
} catch (RemoteException e) {
Log.e(TAG, "Remote exception on sysUiProxy.", e);
}
}
@Override
public final boolean onControllerInterceptTouchEvent(MotionEvent ev) {
int action = ev.getActionMasked();
if (action == ACTION_DOWN) {
mCanIntercept = canInterceptTouch(ev);
if (!mCanIntercept) {
return false;
}
mTranslator.reset();
mTranslator.setDownParameters(0, ev);
} else if (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) {
// Check!! should only set it only when threshold is not entered.
mTranslator.setDownParameters(ev.getActionIndex(), ev);
}
if (!mCanIntercept) {
return false;
}
if (action == ACTION_MOVE) {
float dy = ev.getY() - mTranslator.getDownY();
float dx = ev.getX() - mTranslator.getDownX();
if (dy > mTouchSlop && dy > Math.abs(dx)) {
mTranslator.dispatchDownEvents(ev);
mTranslator.processMotionEvent(ev);
return true;
}
if (Math.abs(dx) > mTouchSlop) {
mCanIntercept = false;
}
}
return false;
}
@Override
public final boolean onControllerTouchEvent(MotionEvent ev) {
mTranslator.processMotionEvent(ev);
return true;
}
private boolean canInterceptTouch(MotionEvent ev) {
if (!mLauncher.isInState(LauncherState.NORMAL) ||
AbstractFloatingView.getTopOpenViewWithType(mLauncher,
AbstractFloatingView.TYPE_STATUS_BAR_SWIPE_DOWN_DISALLOW) != null) {
return false;
} else {
// For NORMAL state, only listen if the event originated above the navbar height
DeviceProfile dp = mLauncher.getDeviceProfile();
if (ev.getY() > (mLauncher.getDragLayer().getHeight() - dp.getInsets().bottom)) {
return false;
}
}
mSysUiProxy = RecentsModel.INSTANCE.get(mLauncher).getSystemUiProxy();
return mSysUiProxy != null;
}
}

View File

@@ -1,156 +0,0 @@
/*
* Copyright (C) 2018 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;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
import android.os.Build;
import android.os.Handler;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Interpolator;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import com.android.uiuios.BaseDraggingActivity;
import com.android.uiuios.DeviceProfile;
import com.android.uiuios.anim.AnimatorPlaybackController;
import com.android.quickstep.util.RemoteAnimationProvider;
import com.android.quickstep.util.RemoteAnimationTargetSet;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
/**
* Utility class which abstracts out the logical differences between Launcher and RecentsActivity.
*/
@TargetApi(Build.VERSION_CODES.P)
public interface ActivityControlHelper<T extends BaseDraggingActivity> {
void onTransitionCancelled(T activity, boolean activityVisible);
int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect);
void onSwipeUpToRecentsComplete(T activity);
default void onSwipeUpToHomeComplete(T activity) { }
void onAssistantVisibilityChanged(float visibility);
@NonNull HomeAnimationFactory prepareHomeUI(T activity);
AnimationFactory prepareRecentsUI(T activity, boolean activityVisible,
boolean animateActivity, Consumer<AnimatorPlaybackController> callback);
ActivityInitListener createActivityInitListener(BiPredicate<T, Boolean> onInitListener);
@Nullable
T getCreatedActivity();
default boolean isResumed() {
BaseDraggingActivity activity = getCreatedActivity();
return activity != null && activity.hasBeenResumed();
}
@UiThread
@Nullable
<T extends View> T getVisibleRecentsView();
@UiThread
boolean switchToRecentsIfVisible(Runnable onCompleteCallback);
Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTargetCompat target);
boolean shouldMinimizeSplitScreen();
default boolean deferStartingActivity(Region activeNavBarRegion, MotionEvent ev) {
return true;
}
/**
* Used for containerType in {@link com.android.uiuios.logging.UserEventDispatcher}
*/
int getContainerType();
boolean isInLiveTileMode();
void onLaunchTaskFailed(T activity);
void onLaunchTaskSuccess(T activity);
interface ActivityInitListener {
void register();
void unregister();
void registerAndStartActivity(Intent intent, RemoteAnimationProvider animProvider,
Context context, Handler handler, long duration);
}
interface AnimationFactory {
enum ShelfAnimState {
HIDE(true), PEEK(true), OVERVIEW(false), CANCEL(false);
ShelfAnimState(boolean shouldPreformHaptic) {
this.shouldPreformHaptic = shouldPreformHaptic;
}
public final boolean shouldPreformHaptic;
}
default void onRemoteAnimationReceived(RemoteAnimationTargetSet targets) { }
void createActivityController(long transitionLength);
default void adjustActivityControllerInterpolators() { }
default void onTransitionCancelled() { }
default void setShelfState(ShelfAnimState animState, Interpolator interpolator,
long duration) { }
/**
* @param attached Whether to show RecentsView alongside the app window. If false, recents
* will be hidden by some property we can animate, e.g. alpha.
* @param animate Whether to animate recents to/from its new attached state.
*/
default void setRecentsAttachedToAppWindow(boolean attached, boolean animate) { }
}
interface HomeAnimationFactory {
/** Return the floating view that will animate in sync with the closing window. */
default @Nullable View getFloatingView() {
return null;
}
@NonNull RectF getWindowTargetRect();
@NonNull AnimatorPlaybackController createActivityAnimationToHome();
default void playAtomicAnimation(float velocity) {
// No-op
}
}
}

View File

@@ -1,89 +0,0 @@
/*
* Copyright (C) 2017 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;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.util.FloatProperty;
/**
* A mutable float which allows animating the value
*/
public class AnimatedFloat {
public static FloatProperty<AnimatedFloat> VALUE = new FloatProperty<AnimatedFloat>("value") {
@Override
public void setValue(AnimatedFloat obj, float v) {
obj.updateValue(v);
}
@Override
public Float get(AnimatedFloat obj) {
return obj.value;
}
};
private final Runnable mUpdateCallback;
private ObjectAnimator mValueAnimator;
public float value;
public AnimatedFloat(Runnable updateCallback) {
mUpdateCallback = updateCallback;
}
public ObjectAnimator animateToValue(float start, float end) {
cancelAnimation();
mValueAnimator = ObjectAnimator.ofFloat(this, VALUE, start, end);
mValueAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animator) {
if (mValueAnimator == animator) {
mValueAnimator = null;
}
}
});
return mValueAnimator;
}
/**
* Changes the value and calls the callback.
* Note that the value can be directly accessed as well to avoid notifying the callback.
*/
public void updateValue(float v) {
if (Float.compare(v, value) != 0) {
value = v;
mUpdateCallback.run();
}
}
public void cancelAnimation() {
if (mValueAnimator != null) {
mValueAnimator.cancel();
}
}
public void finishAnimation() {
if (mValueAnimator != null && mValueAnimator.isRunning()) {
mValueAnimator.end();
}
}
public ObjectAnimator getCurrentAnimation() {
return mValueAnimator;
}
}

View File

@@ -1,162 +0,0 @@
/*
* Copyright (C) 2019 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;
import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
import com.android.uiuios.AbstractFloatingView;
import com.android.uiuios.BaseDraggingActivity;
import com.android.uiuios.DeviceProfile;
import com.android.uiuios.InvariantDeviceProfile;
import com.android.uiuios.R;
import com.android.uiuios.uioverrides.UiFactory;
import com.android.uiuios.util.SystemUiController;
import com.android.uiuios.util.Themes;
import java.io.FileDescriptor;
import java.io.PrintWriter;
/**
* A base fallback recents activity that provides support for device profile changes, activity
* lifecycle tracking, and basic input handling from recents.
*
* This class is only used as a fallback in case the default launcher does not have a recents
* implementation.
*/
public abstract class BaseRecentsActivity extends BaseDraggingActivity {
private Configuration mOldConfig;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mOldConfig = new Configuration(getResources().getConfiguration());
initDeviceProfile();
initViews();
getSystemUiController().updateUiState(SystemUiController.UI_STATE_BASE_WINDOW,
Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText));
RecentsActivityTracker.onRecentsActivityCreate(this);
}
/**
* Init drag layer and overview panel views.
*/
abstract protected void initViews();
@Override
public void onConfigurationChanged(Configuration newConfig) {
int diff = newConfig.diff(mOldConfig);
if ((diff & (CONFIG_ORIENTATION | CONFIG_SCREEN_SIZE)) != 0) {
onHandleConfigChanged();
}
mOldConfig.setTo(newConfig);
super.onConfigurationChanged(newConfig);
}
/**
* Logic for when device configuration changes (rotation, screen size change, multi-window,
* etc.)
*/
protected void onHandleConfigChanged() {
mUserEventDispatcher = null;
initDeviceProfile();
AbstractFloatingView.closeOpenViews(this, true,
AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE);
dispatchDeviceProfileChanged();
reapplyUi();
}
/**
* Initialize/update the device profile.
*/
private void initDeviceProfile() {
mDeviceProfile = createDeviceProfile();
onDeviceProfileInitiated();
}
/**
* Generate the device profile to use in this activity.
* @return device profile
*/
protected DeviceProfile createDeviceProfile() {
DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(this).getDeviceProfile(this);
// In case we are reusing IDP, create a copy so that we don't conflict with Launcher
// activity.
return dp.copy(this);
}
@Override
protected void onStop() {
super.onStop();
// Workaround for b/78520668, explicitly trim memory once UI is hidden
onTrimMemory(TRIM_MEMORY_UI_HIDDEN);
}
@Override
public void onEnterAnimationComplete() {
super.onEnterAnimationComplete();
UiFactory.onEnterAnimationComplete(this);
}
@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
UiFactory.onTrimMemory(this, level);
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
RecentsActivityTracker.onRecentsActivityNewIntent(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
RecentsActivityTracker.onRecentsActivityDestroy(this);
}
@Override
public void onBackPressed() {
// TODO: Launch the task we came from
startHome();
}
public void startHome() {
startActivity(new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_HOME)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
}
@Override
public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
super.dump(prefix, fd, writer, args);
writer.println(prefix + "Misc:");
dumpMisc(writer);
}
}

View File

@@ -1,52 +0,0 @@
/*
* Copyright (C) 2018 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;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import com.android.uiuios.AppInfo;
import com.android.uiuios.util.InstantAppResolver;
/**
* Implementation of InstantAppResolver using platform APIs
*/
@SuppressWarnings("unused")
public class InstantAppResolverImpl extends InstantAppResolver {
private static final String TAG = "InstantAppResolverImpl";
public static final String COMPONENT_CLASS_MARKER = "@instantapp";
private final PackageManager mPM;
public InstantAppResolverImpl(Context context) {
mPM = context.getPackageManager();
}
@Override
public boolean isInstantApp(ApplicationInfo info) {
return info.isInstantApp();
}
@Override
public boolean isInstantApp(AppInfo info) {
ComponentName cn = info.getTargetComponent();
return cn != null && cn.getClassName().equals(COMPONENT_CLASS_MARKER);
}
}

View File

@@ -1,93 +0,0 @@
/*
* Copyright (C) 2018 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;
import android.annotation.TargetApi;
import android.content.Intent;
import android.content.pm.LauncherApps;
import android.content.pm.ResolveInfo;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.os.Build;
import android.provider.SearchIndexablesContract.XmlResource;
import android.provider.SearchIndexablesProvider;
import android.util.Xml;
import com.android.uiuios.R;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import static android.provider.SearchIndexablesContract.INDEXABLES_RAW_COLUMNS;
import static android.provider.SearchIndexablesContract.INDEXABLES_XML_RES_COLUMNS;
import static android.provider.SearchIndexablesContract.NON_INDEXABLES_KEYS_COLUMNS;
@TargetApi(Build.VERSION_CODES.O)
public class LauncherSearchIndexablesProvider extends SearchIndexablesProvider {
@Override
public boolean onCreate() {
return true;
}
@Override
public Cursor queryXmlResources(String[] strings) {
MatrixCursor cursor = new MatrixCursor(INDEXABLES_XML_RES_COLUMNS);
ResolveInfo settingsActivity = getContext().getPackageManager().resolveActivity(
new Intent(Intent.ACTION_APPLICATION_PREFERENCES)
.setPackage(getContext().getPackageName()), 0);
cursor.newRow()
.add(XmlResource.COLUMN_XML_RESID, R.xml.indexable_launcher_prefs)
.add(XmlResource.COLUMN_INTENT_ACTION, Intent.ACTION_APPLICATION_PREFERENCES)
.add(XmlResource.COLUMN_INTENT_TARGET_PACKAGE, getContext().getPackageName())
.add(XmlResource.COLUMN_INTENT_TARGET_CLASS, settingsActivity.activityInfo.name);
return cursor;
}
@Override
public Cursor queryRawData(String[] projection) {
return new MatrixCursor(INDEXABLES_RAW_COLUMNS);
}
@Override
public Cursor queryNonIndexableKeys(String[] projection) {
MatrixCursor cursor = new MatrixCursor(NON_INDEXABLES_KEYS_COLUMNS);
if (!getContext().getSystemService(LauncherApps.class).hasShortcutHostPermission()) {
// We are not the current launcher. Hide all preferences
try (XmlResourceParser parser = getContext().getResources()
.getXml(R.xml.indexable_launcher_prefs)) {
final int depth = parser.getDepth();
final int[] attrs = new int[] { android.R.attr.key };
int type;
while (((type = parser.next()) != XmlPullParser.END_TAG ||
parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
if (type == XmlPullParser.START_TAG) {
TypedArray a = getContext().obtainStyledAttributes(
Xml.asAttributeSet(parser), attrs);
cursor.addRow(new String[] {a.getString(0)});
a.recycle();
}
}
} catch (IOException |XmlPullParserException e) {
throw new RuntimeException(e);
}
}
return cursor;
}
}

View File

@@ -1,99 +0,0 @@
/*
* Copyright (C) 2018 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;
import android.annotation.TargetApi;
import android.app.ActivityManager.TaskDescription;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.UserHandle;
import android.util.LruCache;
import android.util.SparseArray;
import com.android.uiuios.FastBitmapDrawable;
import com.android.uiuios.icons.BitmapInfo;
import com.android.uiuios.graphics.DrawableFactory;
import com.android.uiuios.icons.LauncherIcons;
import com.android.systemui.shared.recents.model.IconLoader;
import com.android.systemui.shared.recents.model.TaskKeyLruCache;
/**
* Extension of {@link IconLoader} with icon normalization support
*/
@TargetApi(Build.VERSION_CODES.O)
public class NormalizedIconLoader extends IconLoader {
private final SparseArray<BitmapInfo> mDefaultIcons = new SparseArray<>();
private final DrawableFactory mDrawableFactory;
private final boolean mDisableColorExtraction;
public NormalizedIconLoader(Context context, TaskKeyLruCache<Drawable> iconCache,
LruCache<ComponentName, ActivityInfo> activityInfoCache,
boolean disableColorExtraction) {
super(context, iconCache, activityInfoCache);
mDrawableFactory = DrawableFactory.INSTANCE.get(context);
mDisableColorExtraction = disableColorExtraction;
}
@Override
public Drawable getDefaultIcon(int userId) {
synchronized (mDefaultIcons) {
BitmapInfo info = mDefaultIcons.get(userId);
if (info == null) {
info = getBitmapInfo(Resources.getSystem()
.getDrawable(android.R.drawable.sym_def_app_icon), userId, 0, false);
mDefaultIcons.put(userId, info);
}
return new FastBitmapDrawable(info);
}
}
@Override
protected Drawable createBadgedDrawable(Drawable drawable, int userId, TaskDescription desc) {
return new FastBitmapDrawable(getBitmapInfo(drawable, userId, desc.getPrimaryColor(),
false));
}
private BitmapInfo getBitmapInfo(Drawable drawable, int userId,
int primaryColor, boolean isInstantApp) {
try (LauncherIcons la = LauncherIcons.obtain(mContext)) {
if (mDisableColorExtraction) {
la.disableColorExtraction();
}
la.setWrapperBackgroundColor(primaryColor);
// User version code O, so that the icon is always wrapped in an adaptive icon container
return la.createBadgedIconBitmap(drawable, UserHandle.of(userId),
Build.VERSION_CODES.O, isInstantApp);
}
}
@Override
protected Drawable getBadgedActivityIcon(ActivityInfo activityInfo, int userId,
TaskDescription desc) {
BitmapInfo bitmapInfo = getBitmapInfo(
activityInfo.loadUnbadgedIcon(mContext.getPackageManager()),
userId,
desc.getPrimaryColor(),
activityInfo.applicationInfo.isInstantApp());
return mDrawableFactory.newIcon(mContext, bitmapInfo, activityInfo);
}
}

View File

@@ -1,43 +0,0 @@
/*
* Copyright (C) 2018 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;
import android.content.Context;
import com.android.uiuios.R;
import com.android.uiuios.util.Preconditions;
import com.android.uiuios.util.ResourceBasedOverride;
/**
* Callbacks related to overview/quicksteps.
*/
public class OverviewCallbacks implements ResourceBasedOverride {
private static OverviewCallbacks sInstance;
public static OverviewCallbacks get(Context context) {
Preconditions.assertUIThread();
if (sInstance == null) {
sInstance = Overrides.getObject(OverviewCallbacks.class,
context.getApplicationContext(), R.string.overview_callbacks_class);
}
return sInstance;
}
public void onInitOverviewTransition() { }
public void closeAllWindows() { }
}

View File

@@ -1,194 +0,0 @@
/*
* Copyright (C) 2019 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;
import static android.content.Intent.ACTION_PACKAGE_ADDED;
import static android.content.Intent.ACTION_PACKAGE_CHANGED;
import static android.content.Intent.ACTION_PACKAGE_REMOVED;
import static com.android.uiuios.util.PackageManagerHelper.getPackageFilter;
import static com.android.systemui.shared.system.PackageManagerWrapper.ACTION_PREFERRED_ACTIVITY_CHANGED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ResolveInfo;
import com.android.systemui.shared.system.PackageManagerWrapper;
import java.util.ArrayList;
/**
* Class to keep track of the current overview component based off user preferences and app updates
* and provide callers the relevant classes.
*/
public final class OverviewComponentObserver {
private final BroadcastReceiver mUserPreferenceChangeReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
updateOverviewTargets();
}
};
private final BroadcastReceiver mOtherHomeAppUpdateReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
updateOverviewTargets();
}
};
private final Context mContext;
private final ComponentName mMyHomeComponent;
private String mUpdateRegisteredPackage;
private ActivityControlHelper mActivityControlHelper;
private Intent mOverviewIntent;
private Intent mHomeIntent;
private int mSystemUiStateFlags;
private boolean mIsHomeAndOverviewSame;
public OverviewComponentObserver(Context context) {
mContext = context;
Intent myHomeIntent = new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_HOME)
.setPackage(mContext.getPackageName());
ResolveInfo info = context.getPackageManager().resolveActivity(myHomeIntent, 0);
mMyHomeComponent = new ComponentName(context.getPackageName(), info.activityInfo.name);
mContext.registerReceiver(mUserPreferenceChangeReceiver,
new IntentFilter(ACTION_PREFERRED_ACTIVITY_CHANGED));
updateOverviewTargets();
}
public void onSystemUiStateChanged(int stateFlags) {
boolean homeDisabledChanged = (mSystemUiStateFlags & SYSUI_STATE_HOME_DISABLED)
!= (stateFlags & SYSUI_STATE_HOME_DISABLED);
mSystemUiStateFlags = stateFlags;
if (homeDisabledChanged) {
updateOverviewTargets();
}
}
/**
* Update overview intent and {@link ActivityControlHelper} based off the current launcher home
* component.
*/
private void updateOverviewTargets() {
ComponentName defaultHome = PackageManagerWrapper.getInstance()
.getHomeActivities(new ArrayList<>());
final String overviewIntentCategory;
ComponentName overviewComponent;
mHomeIntent = null;
if ((mSystemUiStateFlags & SYSUI_STATE_HOME_DISABLED) == 0 &&
(defaultHome == null || mMyHomeComponent.equals(defaultHome))) {
// User default home is same as out home app. Use Overview integrated in Launcher.
overviewComponent = mMyHomeComponent;
mActivityControlHelper = new LauncherActivityControllerHelper();
mIsHomeAndOverviewSame = true;
overviewIntentCategory = Intent.CATEGORY_HOME;
if (mUpdateRegisteredPackage != null) {
// Remove any update listener as we don't care about other packages.
mContext.unregisterReceiver(mOtherHomeAppUpdateReceiver);
mUpdateRegisteredPackage = null;
}
} else {
// The default home app is a different launcher. Use the fallback Overview instead.
overviewComponent = new ComponentName(mContext, RecentsActivity.class);
mActivityControlHelper = new FallbackActivityControllerHelper();
mIsHomeAndOverviewSame = false;
overviewIntentCategory = Intent.CATEGORY_DEFAULT;
mHomeIntent = new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_HOME)
.setComponent(defaultHome);
// User's default home app can change as a result of package updates of this app (such
// as uninstalling the app or removing the "Launcher" feature in an update).
// Listen for package updates of this app (and remove any previously attached
// package listener).
if (defaultHome == null) {
if (mUpdateRegisteredPackage != null) {
mContext.unregisterReceiver(mOtherHomeAppUpdateReceiver);
}
} else if (!defaultHome.getPackageName().equals(mUpdateRegisteredPackage)) {
if (mUpdateRegisteredPackage != null) {
mContext.unregisterReceiver(mOtherHomeAppUpdateReceiver);
}
mUpdateRegisteredPackage = defaultHome.getPackageName();
mContext.registerReceiver(mOtherHomeAppUpdateReceiver, getPackageFilter(
mUpdateRegisteredPackage, ACTION_PACKAGE_ADDED, ACTION_PACKAGE_CHANGED,
ACTION_PACKAGE_REMOVED));
}
}
mOverviewIntent = new Intent(Intent.ACTION_MAIN)
.addCategory(overviewIntentCategory)
.setComponent(overviewComponent)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (mHomeIntent == null) {
mHomeIntent = mOverviewIntent;
}
}
/**
* Clean up any registered receivers.
*/
public void onDestroy() {
mContext.unregisterReceiver(mUserPreferenceChangeReceiver);
if (mUpdateRegisteredPackage != null) {
mContext.unregisterReceiver(mOtherHomeAppUpdateReceiver);
mUpdateRegisteredPackage = null;
}
}
/**
* Get the current intent for going to the overview activity.
*
* @return the overview intent
*/
public Intent getOverviewIntent() {
return mOverviewIntent;
}
/**
* Get the current intent for going to the home activity.
*/
public Intent getHomeIntent() {
return mHomeIntent;
}
/**
* Returns true if home and overview are same activity.
*/
public boolean isHomeAndOverviewSame() {
return mIsHomeAndOverviewSame;
}
/**
* Get the current activity control helper for managing interactions to the overview activity.
*
* @return the current activity control helper
*/
public ActivityControlHelper getActivityControlHelper() {
return mActivityControlHelper;
}
}

View File

@@ -1,145 +0,0 @@
/*
* Copyright (C) 2018 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;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
import com.android.uiuios.Utilities;
import com.android.uiuios.allapps.DiscoveryBounce;
import com.android.uiuios.util.MainThreadInitializedObject;
import com.android.uiuios.util.UiThreadHelper;
import com.android.systemui.shared.recents.ISystemUiProxy;
import androidx.annotation.WorkerThread;
/**
* Sets alpha for the back button
*/
public class OverviewInteractionState {
private static final String TAG = "OverviewFlags";
private static final String HAS_ENABLED_QUICKSTEP_ONCE = "launcher.has_enabled_quickstep_once";
// We do not need any synchronization for this variable as its only written on UI thread.
public static final MainThreadInitializedObject<OverviewInteractionState> INSTANCE =
new MainThreadInitializedObject<>(OverviewInteractionState::new);
private static final int MSG_SET_PROXY = 200;
private static final int MSG_SET_BACK_BUTTON_ALPHA = 201;
private final Context mContext;
private final Handler mUiHandler;
private final Handler mBgHandler;
// These are updated on the background thread
private ISystemUiProxy mISystemUiProxy;
private float mBackButtonAlpha = 1;
private int mSystemUiStateFlags;
private OverviewInteractionState(Context context) {
mContext = context;
// Data posted to the uihandler will be sent to the bghandler. Data is sent to uihandler
// because of its high send frequency and data may be very different than the previous value
// For example, send back alpha on uihandler to avoid flickering when setting its visibility
mUiHandler = new Handler(this::handleUiMessage);
mBgHandler = new Handler(UiThreadHelper.getBackgroundLooper(), this::handleBgMessage);
onNavigationModeChanged(SysUINavigationMode.INSTANCE.get(context)
.addModeChangeListener(this::onNavigationModeChanged));
}
public float getBackButtonAlpha() {
return mBackButtonAlpha;
}
public void setBackButtonAlpha(float alpha, boolean animate) {
if (!modeSupportsGestures()) {
alpha = 1;
}
mUiHandler.removeMessages(MSG_SET_BACK_BUTTON_ALPHA);
mUiHandler.obtainMessage(MSG_SET_BACK_BUTTON_ALPHA, animate ? 1 : 0, 0, alpha)
.sendToTarget();
}
public void setSystemUiProxy(ISystemUiProxy proxy) {
mBgHandler.obtainMessage(MSG_SET_PROXY, proxy).sendToTarget();
}
public void setSystemUiStateFlags(int stateFlags) {
mSystemUiStateFlags = stateFlags;
}
public int getSystemUiStateFlags() {
return mSystemUiStateFlags;
}
private boolean handleUiMessage(Message msg) {
if (msg.what == MSG_SET_BACK_BUTTON_ALPHA) {
mBackButtonAlpha = (float) msg.obj;
}
mBgHandler.obtainMessage(msg.what, msg.arg1, msg.arg2, msg.obj).sendToTarget();
return true;
}
private boolean handleBgMessage(Message msg) {
switch (msg.what) {
case MSG_SET_PROXY:
mISystemUiProxy = (ISystemUiProxy) msg.obj;
break;
case MSG_SET_BACK_BUTTON_ALPHA:
applyBackButtonAlpha((float) msg.obj, msg.arg1 == 1);
return true;
}
return true;
}
@WorkerThread
private void applyBackButtonAlpha(float alpha, boolean animate) {
if (mISystemUiProxy == null) {
return;
}
try {
mISystemUiProxy.setBackButtonAlpha(alpha, animate);
} catch (RemoteException e) {
Log.w(TAG, "Unable to update overview back button alpha", e);
}
}
private void onNavigationModeChanged(SysUINavigationMode.Mode mode) {
resetHomeBounceSeenOnQuickstepEnabledFirstTime();
}
private void resetHomeBounceSeenOnQuickstepEnabledFirstTime() {
if (modeSupportsGestures() && !Utilities.getPrefs(mContext).getBoolean(
HAS_ENABLED_QUICKSTEP_ONCE, true)) {
Utilities.getPrefs(mContext).edit()
.putBoolean(HAS_ENABLED_QUICKSTEP_ONCE, true)
.putBoolean(DiscoveryBounce.HOME_BOUNCE_SEEN, false)
.apply();
}
}
private boolean modeSupportsGestures() {
return SysUINavigationMode.getMode(mContext).hasGestures;
}
}

View File

@@ -1,67 +0,0 @@
/*
* Copyright (C) 2018 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;
import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.UserManager;
import android.util.Log;
import com.android.uiuios.BuildConfig;
import com.android.uiuios.MainProcessInitializer;
import com.android.uiuios.Utilities;
import com.android.systemui.shared.system.ThreadedRendererCompat;
@SuppressWarnings("unused")
public class QuickstepProcessInitializer extends MainProcessInitializer {
private static final String TAG = "QuickstepProcessInitializer";
private static final int HEAP_LIMIT_MB = 250;
public QuickstepProcessInitializer(Context context) { }
@Override
protected void init(Context context) {
if (Utilities.IS_DEBUG_DEVICE) {
try {
// Trigger a heap dump if the PSS reaches beyond the target heap limit
final ActivityManager am = context.getSystemService(ActivityManager.class);
am.setWatchHeapLimit(HEAP_LIMIT_MB * 1024 * 1024);
} catch (SecurityException e) {
// Do nothing
}
}
// Workaround for b/120550382, an external app can cause the launcher process to start for
// a work profile user which we do not support. Disable the application immediately when we
// detect this to be the case.
UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
if (um.isManagedProfile()) {
PackageManager pm = context.getPackageManager();
pm.setApplicationEnabledSetting(context.getPackageName(),
PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 0 /* flags */);
Log.w(TAG, "Disabling " + BuildConfig.APPLICATION_ID
+ ", unable to run in a managed profile");
return;
}
super.init(context);
// Elevate GPU priority for Quickstep and Remote animations.
ThreadedRendererCompat.setContextPriority(ThreadedRendererCompat.EGL_CONTEXT_PRIORITY_HIGH_IMG);
}
}

View File

@@ -1,36 +0,0 @@
package com.android.quickstep;
import android.content.Context;
import android.os.Bundle;
import com.android.uiuios.testing.TestInformationHandler;
import com.android.uiuios.testing.TestProtocol;
import com.android.uiuios.uioverrides.states.OverviewState;
import com.android.quickstep.util.LayoutUtils;
public class QuickstepTestInformationHandler extends TestInformationHandler {
public QuickstepTestInformationHandler(Context context) { }
@Override
public Bundle call(String method) {
final Bundle response = new Bundle();
switch (method) {
case TestProtocol.REQUEST_HOME_TO_OVERVIEW_SWIPE_HEIGHT: {
final float swipeHeight =
OverviewState.getDefaultSwipeHeight(mDeviceProfile);
response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, (int) swipeHeight);
return response;
}
case TestProtocol.REQUEST_BACKGROUND_TO_OVERVIEW_SWIPE_HEIGHT: {
final float swipeHeight =
LayoutUtils.getShelfTrackingDistance(mContext, mDeviceProfile);
response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, (int) swipeHeight);
return response;
}
}
return super.call(method);
}
}

View File

@@ -1,196 +0,0 @@
/*
* Copyright (C) 2014 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;
import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.content.Context;
import android.os.Build;
import android.os.Process;
import android.util.SparseBooleanArray;
import com.android.uiuios.MainThreadExecutor;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.BackgroundExecutor;
import com.android.systemui.shared.system.KeyguardManagerCompat;
import com.android.systemui.shared.system.RecentTaskInfoCompat;
import com.android.systemui.shared.system.TaskDescriptionCompat;
import com.android.systemui.shared.system.TaskStackChangeListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
/**
* Manages the recent task list from the system, caching it as necessary.
*/
@TargetApi(Build.VERSION_CODES.P)
public class RecentTasksList extends TaskStackChangeListener {
private final KeyguardManagerCompat mKeyguardManager;
private final MainThreadExecutor mMainThreadExecutor;
private final BackgroundExecutor mBgThreadExecutor;
// The list change id, increments as the task list changes in the system
private int mChangeId;
// The last change id when the list was last loaded completely, must be <= the list change id
private int mLastLoadedId;
// The last change id was loaded with keysOnly = true
private boolean mLastLoadHadKeysOnly;
ArrayList<Task> mTasks = new ArrayList<>();
public RecentTasksList(Context context) {
mMainThreadExecutor = new MainThreadExecutor();
mBgThreadExecutor = BackgroundExecutor.get();
mKeyguardManager = new KeyguardManagerCompat(context);
mChangeId = 1;
ActivityManagerWrapper.getInstance().registerTaskStackListener(this);
}
/**
* Fetches the task keys skipping any local cache.
*/
public void getTaskKeys(int numTasks, Consumer<ArrayList<Task>> callback) {
// Kick off task loading in the background
mBgThreadExecutor.submit(() -> {
ArrayList<Task> tasks = loadTasksInBackground(numTasks, true /* loadKeysOnly */);
mMainThreadExecutor.execute(() -> callback.accept(tasks));
});
}
/**
* Asynchronously fetches the list of recent tasks, reusing cached list if available.
*
* @param loadKeysOnly Whether to load other associated task data, or just the key
* @param callback The callback to receive the list of recent tasks
* @return The change id of the current task list
*/
public synchronized int getTasks(boolean loadKeysOnly, Consumer<ArrayList<Task>> callback) {
final int requestLoadId = mChangeId;
Runnable resultCallback = callback == null
? () -> { }
: () -> callback.accept(copyOf(mTasks));
if (mLastLoadedId == mChangeId && (!mLastLoadHadKeysOnly || loadKeysOnly)) {
// The list is up to date, callback with the same list
mMainThreadExecutor.execute(resultCallback);
return requestLoadId;
}
// Kick off task loading in the background
mBgThreadExecutor.submit(() -> {
ArrayList<Task> tasks = loadTasksInBackground(Integer.MAX_VALUE, loadKeysOnly);
mMainThreadExecutor.execute(() -> {
mTasks = tasks;
mLastLoadedId = requestLoadId;
mLastLoadHadKeysOnly = loadKeysOnly;
resultCallback.run();
});
});
return requestLoadId;
}
/**
* @return Whether the provided {@param changeId} is the latest recent tasks list id.
*/
public synchronized boolean isTaskListValid(int changeId) {
return mChangeId == changeId;
}
@Override
public synchronized void onTaskStackChanged() {
mChangeId++;
}
@Override
public void onTaskRemoved(int taskId) {
for (int i = mTasks.size() - 1; i >= 0; i--) {
if (mTasks.get(i).key.id == taskId) {
mTasks.remove(i);
return;
}
}
}
@Override
public synchronized void onActivityPinned(String packageName, int userId, int taskId,
int stackId) {
mChangeId++;
}
@Override
public synchronized void onActivityUnpinned() {
mChangeId++;
}
/**
* Loads and creates a list of all the recent tasks.
*/
private ArrayList<Task> loadTasksInBackground(int numTasks,
boolean loadKeysOnly) {
int currentUserId = Process.myUserHandle().getIdentifier();
ArrayList<Task> allTasks = new ArrayList<>();
List<ActivityManager.RecentTaskInfo> rawTasks =
ActivityManagerWrapper.getInstance().getRecentTasks(numTasks, currentUserId);
// The raw tasks are given in most-recent to least-recent order, we need to reverse it
Collections.reverse(rawTasks);
SparseBooleanArray tmpLockedUsers = new SparseBooleanArray() {
@Override
public boolean get(int key) {
if (indexOfKey(key) < 0) {
// Fill the cached locked state as we fetch
put(key, mKeyguardManager.isDeviceLocked(key));
}
return super.get(key);
}
};
int taskCount = rawTasks.size();
for (int i = 0; i < taskCount; i++) {
ActivityManager.RecentTaskInfo rawTask = rawTasks.get(i);
RecentTaskInfoCompat t = new RecentTaskInfoCompat(rawTask);
Task.TaskKey taskKey = new Task.TaskKey(rawTask);
Task task;
if (!loadKeysOnly) {
ActivityManager.TaskDescription rawTd = t.getTaskDescription();
TaskDescriptionCompat td = new TaskDescriptionCompat(rawTd);
boolean isLocked = tmpLockedUsers.get(t.getUserId());
task = new Task(taskKey, td.getPrimaryColor(), td.getBackgroundColor(),
t.supportsSplitScreenMultiWindow(), isLocked, rawTd, t.getTopActivity());
} else {
task = new Task(taskKey);
}
allTasks.add(task);
}
return allTasks;
}
private ArrayList<Task> copyOf(ArrayList<Task> tasks) {
ArrayList<Task> newTasks = new ArrayList<>();
for (int i = 0; i < tasks.size(); i++) {
Task t = tasks.get(i);
newTasks.add(new Task(t.key, t.colorPrimary, t.colorBackground, t.isDockable,
t.isLocked, t.taskDescription, t.topActivity));
}
return newTasks;
}
}

View File

@@ -1,133 +0,0 @@
/*
* Copyright (C) 2018 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;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import com.android.uiuios.MainThreadExecutor;
import com.android.quickstep.ActivityControlHelper.ActivityInitListener;
import com.android.quickstep.util.RemoteAnimationProvider;
import java.lang.ref.WeakReference;
import java.util.function.BiPredicate;
/**
* Utility class to track create/destroy for some {@link BaseRecentsActivity}.
*/
@TargetApi(Build.VERSION_CODES.P)
public class RecentsActivityTracker<T extends BaseRecentsActivity> implements ActivityInitListener {
private static WeakReference<BaseRecentsActivity> sCurrentActivity =
new WeakReference<>(null);
private static final Scheduler sScheduler = new Scheduler();
private final BiPredicate<T, Boolean> mOnInitListener;
public RecentsActivityTracker(BiPredicate<T, Boolean> onInitListener) {
mOnInitListener = onInitListener;
}
@Override
public void register() {
sScheduler.schedule(this);
}
@Override
public void unregister() {
sScheduler.clearReference(this);
}
private boolean init(T activity, boolean visible) {
return mOnInitListener.test(activity, visible);
}
public static <T extends BaseRecentsActivity> T getCurrentActivity() {
return (T) sCurrentActivity.get();
}
@Override
public void registerAndStartActivity(Intent intent, RemoteAnimationProvider animProvider,
Context context, Handler handler, long duration) {
register();
Bundle options = animProvider.toActivityOptions(handler, duration).toBundle();
context.startActivity(intent, options);
}
public static void onRecentsActivityCreate(BaseRecentsActivity activity) {
sCurrentActivity = new WeakReference<>(activity);
sScheduler.initIfPending(activity, false);
}
public static void onRecentsActivityNewIntent(BaseRecentsActivity activity) {
sScheduler.initIfPending(activity, activity.isStarted());
}
public static void onRecentsActivityDestroy(BaseRecentsActivity activity) {
if (sCurrentActivity.get() == activity) {
sCurrentActivity.clear();
}
}
private static class Scheduler implements Runnable {
private WeakReference<RecentsActivityTracker> mPendingTracker = new WeakReference<>(null);
private MainThreadExecutor mMainThreadExecutor;
public synchronized void schedule(RecentsActivityTracker tracker) {
mPendingTracker = new WeakReference<>(tracker);
if (mMainThreadExecutor == null) {
mMainThreadExecutor = new MainThreadExecutor();
}
mMainThreadExecutor.execute(this);
}
@Override
public void run() {
BaseRecentsActivity activity = sCurrentActivity.get();
if (activity != null) {
initIfPending(activity, activity.isStarted());
}
}
public synchronized boolean initIfPending(BaseRecentsActivity activity,
boolean alreadyOnHome) {
RecentsActivityTracker tracker = mPendingTracker.get();
if (tracker != null) {
if (!tracker.init(activity, alreadyOnHome)) {
mPendingTracker.clear();
}
return true;
}
return false;
}
public synchronized boolean clearReference(RecentsActivityTracker tracker) {
if (mPendingTracker.get() == tracker) {
mPendingTracker.clear();
return true;
}
return false;
}
}
}

View File

@@ -1,219 +0,0 @@
/*
* Copyright (C) 2018 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;
import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SUPPORTS_WINDOW_CORNERS;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_WINDOW_CORNER_RADIUS;
import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.content.ComponentCallbacks2;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.os.HandlerThread;
import android.os.Process;
import android.os.RemoteException;
import android.util.Log;
import com.android.uiuios.util.MainThreadInitializedObject;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.TaskStackChangeListener;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
/**
* Singleton class to load and manage recents model.
*/
@TargetApi(Build.VERSION_CODES.O)
public class RecentsModel extends TaskStackChangeListener {
private static final String TAG = "RecentsModel";
// We do not need any synchronization for this variable as its only written on UI thread.
public static final MainThreadInitializedObject<RecentsModel> INSTANCE =
new MainThreadInitializedObject<>(c -> new RecentsModel(c));
private final List<TaskThumbnailChangeListener> mThumbnailChangeListeners = new ArrayList<>();
private final Context mContext;
private ISystemUiProxy mSystemUiProxy;
private final RecentTasksList mTaskList;
private final TaskIconCache mIconCache;
private final TaskThumbnailCache mThumbnailCache;
private RecentsModel(Context context) {
mContext = context;
HandlerThread loaderThread = new HandlerThread("TaskThumbnailIconCache",
Process.THREAD_PRIORITY_BACKGROUND);
loaderThread.start();
mTaskList = new RecentTasksList(context);
mIconCache = new TaskIconCache(context, loaderThread.getLooper());
mThumbnailCache = new TaskThumbnailCache(context, loaderThread.getLooper());
ActivityManagerWrapper.getInstance().registerTaskStackListener(this);
}
public TaskIconCache getIconCache() {
return mIconCache;
}
public TaskThumbnailCache getThumbnailCache() {
return mThumbnailCache;
}
/**
* Fetches the list of recent tasks.
*
* @param callback The callback to receive the task plan once its complete or null. This is
* always called on the UI thread.
* @return the request id associated with this call.
*/
public int getTasks(Consumer<ArrayList<Task>> callback) {
return mTaskList.getTasks(false /* loadKeysOnly */, callback);
}
/**
* @return The task id of the running task, or -1 if there is no current running task.
*/
public static int getRunningTaskId() {
ActivityManager.RunningTaskInfo runningTask =
ActivityManagerWrapper.getInstance().getRunningTask();
return runningTask != null ? runningTask.id : -1;
}
/**
* @return Whether the provided {@param changeId} is the latest recent tasks list id.
*/
public boolean isTaskListValid(int changeId) {
return mTaskList.isTaskListValid(changeId);
}
/**
* Finds and returns the task key associated with the given task id.
*
* @param callback The callback to receive the task key if it is found or null. This is always
* called on the UI thread.
*/
public void findTaskWithId(int taskId, Consumer<Task.TaskKey> callback) {
mTaskList.getTasks(true /* loadKeysOnly */, (tasks) -> {
for (Task task : tasks) {
if (task.key.id == taskId) {
callback.accept(task.key);
return;
}
}
callback.accept(null);
});
}
@Override
public void onTaskStackChangedBackground() {
if (!mThumbnailCache.isPreloadingEnabled()) {
// Skip if we aren't preloading
return;
}
int currentUserId = Process.myUserHandle().getIdentifier();
if (!checkCurrentOrManagedUserId(currentUserId, mContext)) {
// Skip if we are not the current user
return;
}
// Keep the cache up to date with the latest thumbnails
int runningTaskId = RecentsModel.getRunningTaskId();
mTaskList.getTaskKeys(mThumbnailCache.getCacheSize(), tasks -> {
for (Task task : tasks) {
if (task.key.id == runningTaskId) {
// Skip the running task, it's not going to have an up-to-date snapshot by the
// time the user next enters overview
continue;
}
mThumbnailCache.updateThumbnailInCache(task);
}
});
}
@Override
public void onTaskSnapshotChanged(int taskId, ThumbnailData snapshot) {
mThumbnailCache.updateTaskSnapShot(taskId, snapshot);
for (int i = mThumbnailChangeListeners.size() - 1; i >= 0; i--) {
Task task = mThumbnailChangeListeners.get(i).onTaskThumbnailChanged(taskId, snapshot);
if (task != null) {
task.thumbnail = snapshot;
}
}
}
@Override
public void onTaskRemoved(int taskId) {
Task.TaskKey dummyKey = new Task.TaskKey(taskId, 0, null, null, 0, 0);
mThumbnailCache.remove(dummyKey);
}
public void setSystemUiProxy(ISystemUiProxy systemUiProxy) {
mSystemUiProxy = systemUiProxy;
}
public ISystemUiProxy getSystemUiProxy() {
return mSystemUiProxy;
}
public void onTrimMemory(int level) {
if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
mThumbnailCache.getHighResLoadingState().setVisible(false);
}
if (level == ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL) {
// Clear everything once we reach a low-mem situation
mThumbnailCache.clear();
mIconCache.clear();
}
}
public void onOverviewShown(boolean fromHome, String tag) {
if (mSystemUiProxy == null) {
return;
}
try {
mSystemUiProxy.onOverviewShown(fromHome);
} catch (RemoteException e) {
Log.w(tag,
"Failed to notify SysUI of overview shown from " + (fromHome ? "home" : "app")
+ ": ", e);
}
}
public void addThumbnailChangeListener(TaskThumbnailChangeListener listener) {
mThumbnailChangeListeners.add(listener);
}
public void removeThumbnailChangeListener(TaskThumbnailChangeListener listener) {
mThumbnailChangeListeners.remove(listener);
}
public interface TaskThumbnailChangeListener {
Task onTaskThumbnailChanged(int taskId, ThumbnailData thumbnailData);
}
}

View File

@@ -1,33 +0,0 @@
/*
* Copyright (C) 2018 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;
import android.os.RemoteException;
import android.util.Log;
@FunctionalInterface
public interface RemoteRunnable {
void run() throws RemoteException;
static void executeSafely(RemoteRunnable r) {
try {
r.run();
} catch (final RemoteException e) {
Log.e("RemoteRunnable", "Error calling remote method", e);
}
}
}

View File

@@ -1,129 +0,0 @@
/*
* Copyright (C) 2019 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;
import static com.android.uiuios.util.PackageManagerHelper.getPackageFilter;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.util.Log;
import com.android.uiuios.util.MainThreadInitializedObject;
import java.util.ArrayList;
import java.util.List;
/**
* Observer for the resource config that specifies the navigation bar mode.
*/
public class SysUINavigationMode {
public enum Mode {
THREE_BUTTONS(false, 0),
TWO_BUTTONS(true, 1),
NO_BUTTON(true, 2);
public final boolean hasGestures;
public final int resValue;
Mode(boolean hasGestures, int resValue) {
this.hasGestures = hasGestures;
this.resValue = resValue;
}
}
public static Mode getMode(Context context) {
return INSTANCE.get(context).getMode();
}
public static MainThreadInitializedObject<SysUINavigationMode> INSTANCE =
new MainThreadInitializedObject<>(SysUINavigationMode::new);
private static final String TAG = "SysUINavigationMode";
private final String ACTION_OVERLAY_CHANGED = "android.intent.action.OVERLAY_CHANGED";
private static final String NAV_BAR_INTERACTION_MODE_RES_NAME =
"config_navBarInteractionMode";
private final Context mContext;
private Mode mMode;
private final List<NavigationModeChangeListener> mChangeListeners = new ArrayList<>();
public SysUINavigationMode(Context context) {
mContext = context;
initializeMode();
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Mode oldMode = mMode;
initializeMode();
if (mMode != oldMode) {
dispatchModeChange();
}
}
}, getPackageFilter("android", ACTION_OVERLAY_CHANGED));
}
private void initializeMode() {
int modeInt = getSystemIntegerRes(mContext, NAV_BAR_INTERACTION_MODE_RES_NAME);
for(Mode m : Mode.values()) {
if (m.resValue == modeInt) {
mMode = m;
}
}
}
private void dispatchModeChange() {
for (NavigationModeChangeListener listener : mChangeListeners) {
listener.onNavigationModeChanged(mMode);
}
}
public Mode addModeChangeListener(NavigationModeChangeListener listener) {
mChangeListeners.add(listener);
return mMode;
}
public void removeModeChangeListener(NavigationModeChangeListener listener) {
mChangeListeners.remove(listener);
}
public Mode getMode() {
return mMode;
}
private static int getSystemIntegerRes(Context context, String resName) {
Resources res = context.getResources();
int resId = res.getIdentifier(resName, "integer", "android");
if (resId != 0) {
return res.getInteger(resId);
} else {
Log.e(TAG, "Failed to get system resource ID. Incompatible framework version?");
return -1;
}
}
public interface NavigationModeChangeListener {
void onNavigationModeChanged(Mode newMode);
}
}

View File

@@ -1,157 +0,0 @@
/*
* Copyright (C) 2018 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;
import static com.android.uiuios.uioverrides.RecentsUiFactory.GO_LOW_RAM_RECENTS_ENABLED;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Looper;
import android.util.LruCache;
import android.view.accessibility.AccessibilityManager;
import com.android.uiuios.MainThreadExecutor;
import com.android.uiuios.R;
import com.android.uiuios.Utilities;
import com.android.uiuios.icons.cache.HandlerRunnable;
import com.android.uiuios.uioverrides.RecentsUiFactory;
import com.android.uiuios.util.Preconditions;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.TaskKeyLruCache;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import java.util.function.Consumer;
/**
* Manages the caching of task icons and related data.
* TODO: This class should later be merged into IconCache.
*/
public class TaskIconCache {
private final Handler mBackgroundHandler;
private final MainThreadExecutor mMainThreadExecutor;
private final AccessibilityManager mAccessibilityManager;
private final NormalizedIconLoader mIconLoader;
private final TaskKeyLruCache<Drawable> mIconCache;
private final TaskKeyLruCache<String> mContentDescriptionCache;
private final LruCache<ComponentName, ActivityInfo> mActivityInfoCache;
private TaskKeyLruCache.EvictionCallback mClearActivityInfoOnEviction =
new TaskKeyLruCache.EvictionCallback() {
@Override
public void onEntryEvicted(Task.TaskKey key) {
if (key != null) {
mActivityInfoCache.remove(key.getComponent());
}
}
};
public TaskIconCache(Context context, Looper backgroundLooper) {
mBackgroundHandler = new Handler(backgroundLooper);
mMainThreadExecutor = new MainThreadExecutor();
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
Resources res = context.getResources();
int cacheSize = res.getInteger(R.integer.recentsIconCacheSize);
mIconCache = new TaskKeyLruCache<>(cacheSize, mClearActivityInfoOnEviction);
mContentDescriptionCache = new TaskKeyLruCache<>(cacheSize, mClearActivityInfoOnEviction);
mActivityInfoCache = new LruCache<>(cacheSize);
mIconLoader = new NormalizedIconLoader(context, mIconCache, mActivityInfoCache,
true /* disableColorExtraction */);
}
/**
* Asynchronously fetches the icon and other task data.
*
* @param task The task to fetch the data for
* @param callback The callback to receive the task after its data has been populated.
* @return A cancelable handle to the request
*/
public IconLoadRequest updateIconInBackground(Task task, Consumer<Task> callback) {
Preconditions.assertUIThread();
if (task.icon != null) {
// Nothing to load, the icon is already loaded
callback.accept(task);
return null;
}
IconLoadRequest request = new IconLoadRequest(mBackgroundHandler) {
@Override
public void run() {
Drawable icon = mIconLoader.getIcon(task);
String contentDescription = loadContentDescriptionInBackground(task);
if (isCanceled()) {
// We don't call back to the provided callback in this case
return;
}
mMainThreadExecutor.execute(() -> {
task.icon = icon;
task.titleDescription = contentDescription;
callback.accept(task);
onEnd();
});
}
};
Utilities.postAsyncCallback(mBackgroundHandler, request);
return request;
}
public void clear() {
mIconCache.evictAll();
mContentDescriptionCache.evictAll();
}
/**
* Loads the content description for the given {@param task}.
*/
private String loadContentDescriptionInBackground(Task task) {
// Return the cached content description if it exists
String label = mContentDescriptionCache.getAndInvalidateIfModified(task.key);
if (label != null) {
return label;
}
// Skip loading content descriptions if accessibility is disabled unless low RAM recents
// is enabled.
if (!GO_LOW_RAM_RECENTS_ENABLED && !mAccessibilityManager.isEnabled()) {
return "";
}
// Skip loading the content description if the activity no longer exists
ActivityInfo activityInfo = mIconLoader.getAndUpdateActivityInfo(task.key);
if (activityInfo == null) {
return "";
}
// Load the label otherwise
label = ActivityManagerWrapper.getInstance().getBadgedContentDescription(activityInfo,
task.key.userId, task.taskDescription);
mContentDescriptionCache.put(task.key, label);
return label;
}
public static abstract class IconLoadRequest extends HandlerRunnable {
IconLoadRequest(Handler handler) {
super(handler, null);
}
}
}

View File

@@ -1,242 +0,0 @@
/*
* Copyright (C) 2018 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;
import android.app.ActivityManager;
import android.content.Context;
import android.content.res.Resources;
import android.os.Handler;
import android.os.Looper;
import com.android.uiuios.MainThreadExecutor;
import com.android.uiuios.R;
import com.android.uiuios.Utilities;
import com.android.uiuios.icons.cache.HandlerRunnable;
import com.android.uiuios.util.Preconditions;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.Task.TaskKey;
import com.android.systemui.shared.recents.model.TaskKeyLruCache;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import java.util.ArrayList;
import java.util.function.Consumer;
public class TaskThumbnailCache {
private final Handler mBackgroundHandler;
private final MainThreadExecutor mMainThreadExecutor;
private final int mCacheSize;
private final ThumbnailCache mCache;
private final HighResLoadingState mHighResLoadingState;
public static class HighResLoadingState {
private boolean mIsLowRamDevice;
private boolean mVisible;
private boolean mFlingingFast;
private boolean mHighResLoadingEnabled;
private ArrayList<HighResLoadingStateChangedCallback> mCallbacks = new ArrayList<>();
public interface HighResLoadingStateChangedCallback {
void onHighResLoadingStateChanged(boolean enabled);
}
private HighResLoadingState(Context context) {
ActivityManager activityManager =
(ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
mIsLowRamDevice = activityManager.isLowRamDevice();
}
public void addCallback(HighResLoadingStateChangedCallback callback) {
mCallbacks.add(callback);
}
public void removeCallback(HighResLoadingStateChangedCallback callback) {
mCallbacks.remove(callback);
}
public void setVisible(boolean visible) {
mVisible = visible;
updateState();
}
public void setFlingingFast(boolean flingingFast) {
mFlingingFast = flingingFast;
updateState();
}
public boolean isEnabled() {
return mHighResLoadingEnabled;
}
private void updateState() {
boolean prevState = mHighResLoadingEnabled;
mHighResLoadingEnabled = !mIsLowRamDevice && mVisible && !mFlingingFast;
if (prevState != mHighResLoadingEnabled) {
for (int i = mCallbacks.size() - 1; i >= 0; i--) {
mCallbacks.get(i).onHighResLoadingStateChanged(mHighResLoadingEnabled);
}
}
}
}
public TaskThumbnailCache(Context context, Looper backgroundLooper) {
mBackgroundHandler = new Handler(backgroundLooper);
mMainThreadExecutor = new MainThreadExecutor();
mHighResLoadingState = new HighResLoadingState(context);
Resources res = context.getResources();
mCacheSize = res.getInteger(R.integer.recentsThumbnailCacheSize);
mCache = new ThumbnailCache(mCacheSize);
}
/**
* Synchronously fetches the thumbnail for the given {@param task} and puts it in the cache.
*/
public void updateThumbnailInCache(Task task) {
Preconditions.assertUIThread();
// Fetch the thumbnail for this task and put it in the cache
if (task.thumbnail == null) {
updateThumbnailInBackground(task.key, true /* reducedResolution */,
t -> task.thumbnail = t);
}
}
/**
* Synchronously updates the thumbnail in the cache if it is already there.
*/
public void updateTaskSnapShot(int taskId, ThumbnailData thumbnail) {
Preconditions.assertUIThread();
mCache.updateIfAlreadyInCache(taskId, thumbnail);
}
/**
* Asynchronously fetches the icon and other task data for the given {@param task}.
*
* @param callback The callback to receive the task after its data has been populated.
* @return A cancelable handle to the request
*/
public ThumbnailLoadRequest updateThumbnailInBackground(
Task task, Consumer<ThumbnailData> callback) {
Preconditions.assertUIThread();
boolean reducedResolution = !mHighResLoadingState.isEnabled();
if (task.thumbnail != null && (!task.thumbnail.reducedResolution || reducedResolution)) {
// Nothing to load, the thumbnail is already high-resolution or matches what the
// request, so just callback
callback.accept(task.thumbnail);
return null;
}
return updateThumbnailInBackground(task.key, !mHighResLoadingState.isEnabled(), t -> {
task.thumbnail = t;
callback.accept(t);
});
}
private ThumbnailLoadRequest updateThumbnailInBackground(TaskKey key, boolean reducedResolution,
Consumer<ThumbnailData> callback) {
Preconditions.assertUIThread();
ThumbnailData cachedThumbnail = mCache.getAndInvalidateIfModified(key);
if (cachedThumbnail != null && (!cachedThumbnail.reducedResolution || reducedResolution)) {
// Already cached, lets use that thumbnail
callback.accept(cachedThumbnail);
return null;
}
ThumbnailLoadRequest request = new ThumbnailLoadRequest(mBackgroundHandler,
reducedResolution) {
@Override
public void run() {
ThumbnailData thumbnail = ActivityManagerWrapper.getInstance().getTaskThumbnail(
key.id, reducedResolution);
if (isCanceled()) {
// We don't call back to the provided callback in this case
return;
}
mMainThreadExecutor.execute(() -> {
mCache.put(key, thumbnail);
callback.accept(thumbnail);
onEnd();
});
}
};
Utilities.postAsyncCallback(mBackgroundHandler, request);
return request;
}
/**
* Clears the cache.
*/
public void clear() {
mCache.evictAll();
}
/**
* Removes the cached thumbnail for the given task.
*/
public void remove(Task.TaskKey key) {
mCache.remove(key);
}
/**
* @return The cache size.
*/
public int getCacheSize() {
return mCacheSize;
}
/**
* @return The mutable high-res loading state.
*/
public HighResLoadingState getHighResLoadingState() {
return mHighResLoadingState;
}
/**
* @return Whether to enable background preloading of task thumbnails.
*/
public boolean isPreloadingEnabled() {
return !mHighResLoadingState.mIsLowRamDevice && mHighResLoadingState.mVisible;
}
public static abstract class ThumbnailLoadRequest extends HandlerRunnable {
public final boolean reducedResolution;
ThumbnailLoadRequest(Handler handler, boolean reducedResolution) {
super(handler, null);
this.reducedResolution = reducedResolution;
}
}
private static class ThumbnailCache extends TaskKeyLruCache<ThumbnailData> {
public ThumbnailCache(int cacheSize) {
super(cacheSize);
}
/**
* Updates the cache entry if it is already present in the cache
*/
public void updateIfAlreadyInCache(int taskId, ThumbnailData thumbnailData) {
ThumbnailData oldData = getCacheEntry(taskId);
if (oldData != null) {
putCacheEntry(taskId, thumbnailData);
}
}
}
}

View File

@@ -1,90 +0,0 @@
/*
* Copyright (C) 2018 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;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.UserHandle;
import android.util.Log;
import com.android.uiuios.compat.LauncherAppsCompat;
import com.android.uiuios.compat.UserManagerCompat;
import com.android.uiuios.util.ComponentKey;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.util.List;
/**
* Contains helpful methods for retrieving data from {@link Task}s.
*/
public final class TaskUtils {
private static final String TAG = "TaskUtils";
private TaskUtils() {}
/**
* TODO: remove this once we switch to getting the icon and label from IconCache.
*/
public static CharSequence getTitle(Context context, Task task) {
LauncherAppsCompat launcherAppsCompat = LauncherAppsCompat.getInstance(context);
PackageManager packageManager = context.getPackageManager();
UserHandle user = UserHandle.of(task.key.userId);
ApplicationInfo applicationInfo = launcherAppsCompat.getApplicationInfo(
task.getTopComponent().getPackageName(), 0, user);
if (applicationInfo == null) {
Log.e(TAG, "Failed to get title for task " + task);
return "";
}
return packageManager.getUserBadgedLabel(
applicationInfo.loadLabel(packageManager), user);
}
public static ComponentKey getLaunchComponentKeyForTask(Task.TaskKey taskKey) {
final ComponentName cn = taskKey.sourceComponent != null
? taskKey.sourceComponent
: taskKey.getComponent();
return new ComponentKey(cn, UserHandle.of(taskKey.userId));
}
public static boolean taskIsATargetWithMode(RemoteAnimationTargetCompat[] targets,
int taskId, int mode) {
for (RemoteAnimationTargetCompat target : targets) {
if (target.mode == mode && target.taskId == taskId) {
return true;
}
}
return false;
}
public static boolean checkCurrentOrManagedUserId(int currentUserId, Context context) {
if (currentUserId == UserHandle.myUserId()) {
return true;
}
List<UserHandle> allUsers = UserManagerCompat.getInstance(context).getUserProfiles();
for (int i = allUsers.size() - 1; i >= 0; i--) {
if (currentUserId == allUsers.get(i).getIdentifier()) {
return true;
}
}
return false;
}
}

View File

@@ -1,105 +0,0 @@
/*
* Copyright (C) 2018 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.logging;
import android.content.Context;
import android.content.Intent;
import android.stats.launcher.nano.LauncherExtension;
import android.stats.launcher.nano.LauncherTarget;
import static android.stats.launcher.nano.Launcher.ALLAPPS;
import static android.stats.launcher.nano.Launcher.HOME;
import static android.stats.launcher.nano.Launcher.LAUNCH_APP;
import static android.stats.launcher.nano.Launcher.LAUNCH_TASK;
import static android.stats.launcher.nano.Launcher.BACKGROUND;
import static android.stats.launcher.nano.Launcher.OVERVIEW;
import android.view.View;
import com.android.uiuios.ItemInfo;
import com.android.uiuios.logging.StatsLogManager;
import com.android.uiuios.logging.StatsLogUtils;
import com.android.uiuios.userevent.nano.LauncherLogProto.Target;
import com.android.uiuios.util.ComponentKey;
import com.android.systemui.shared.system.StatsLogCompat;
import com.google.protobuf.nano.MessageNano;
import androidx.annotation.Nullable;
/**
* This method calls the StatsLog hidden method until they are made available public.
*
* To see if the logs are properly sent to statsd, execute following command.
* $ adb root && adb shell statsd
* $ adb shell cmd stats print-logs
* $ adb logcat | grep statsd OR $ adb logcat -b stats
*/
public class StatsLogCompatManager extends StatsLogManager {
private static final int SUPPORTED_TARGET_DEPTH = 2;
public StatsLogCompatManager(Context context) { }
@Override
public void logAppLaunch(View v, Intent intent) {
LauncherExtension ext = new LauncherExtension();
ext.srcTarget = new LauncherTarget[SUPPORTED_TARGET_DEPTH];
int srcState = mStateProvider.getCurrentState();
fillInLauncherExtension(v, ext);
StatsLogCompat.write(LAUNCH_APP, srcState, BACKGROUND /* dstState */,
MessageNano.toByteArray(ext), true);
}
@Override
public void logTaskLaunch(View v, ComponentKey componentKey) {
LauncherExtension ext = new LauncherExtension();
ext.srcTarget = new LauncherTarget[SUPPORTED_TARGET_DEPTH];
int srcState = OVERVIEW;
fillInLauncherExtension(v, ext);
StatsLogCompat.write(LAUNCH_TASK, srcState, BACKGROUND /* dstState */,
MessageNano.toByteArray(ext), true);
}
public static boolean fillInLauncherExtension(View v, LauncherExtension extension) {
StatsLogUtils.LogContainerProvider provider = StatsLogUtils.getLaunchProviderRecursive(v);
if (v == null || !(v.getTag() instanceof ItemInfo) || provider == null) {
return false;
}
ItemInfo itemInfo = (ItemInfo) v.getTag();
Target child = new Target();
Target parent = new Target();
provider.fillInLogContainerData(v, itemInfo, child, parent);
copy(child, extension.srcTarget[0]);
copy(parent, extension.srcTarget[1]);
return true;
}
private static void copy(Target src, LauncherTarget dst) {
// fill in
}
@Override
public void verify() {
if(!(StatsLogUtils.LAUNCHER_STATE_ALLAPPS == ALLAPPS &&
StatsLogUtils.LAUNCHER_STATE_BACKGROUND == BACKGROUND &&
StatsLogUtils.LAUNCHER_STATE_OVERVIEW == OVERVIEW &&
StatsLogUtils.LAUNCHER_STATE_HOME == HOME)) {
throw new IllegalStateException(
"StatsLogUtil constants doesn't match enums in launcher.proto");
}
}
}

View File

@@ -1,86 +0,0 @@
/*
* Copyright (C) 2018 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.logging;
import android.content.Context;
import android.util.Log;
import static com.android.uiuios.logging.LoggerUtils.newLauncherEvent;
import static com.android.uiuios.userevent.nano.LauncherLogProto.ControlType.CANCEL_TARGET;
import static com.android.systemui.shared.system.LauncherEventUtil.VISIBLE;
import static com.android.systemui.shared.system.LauncherEventUtil.DISMISS;
import static com.android.systemui.shared.system.LauncherEventUtil.RECENTS_QUICK_SCRUB_ONBOARDING_TIP;
import static com.android.systemui.shared.system.LauncherEventUtil.RECENTS_SWIPE_UP_ONBOARDING_TIP;
import com.android.uiuios.logging.UserEventDispatcher;
import com.android.uiuios.userevent.nano.LauncherLogProto;
import com.android.systemui.shared.system.MetricsLoggerCompat;
/**
* This class handles AOSP MetricsLogger function calls and logging around
* quickstep interactions.
*/
@SuppressWarnings("unused")
public class UserEventDispatcherExtension extends UserEventDispatcher {
public static final int ALL_APPS_PREDICTION_TIPS = 2;
private static final String TAG = "UserEventDispatcher";
public UserEventDispatcherExtension(Context context) { }
public void logStateChangeAction(int action, int dir, int downX, int downY,
int srcChildTargetType, int srcParentContainerType,
int dstContainerType, int pageIndex) {
new MetricsLoggerCompat().visibility(MetricsLoggerCompat.OVERVIEW_ACTIVITY,
dstContainerType == LauncherLogProto.ContainerType.TASKSWITCHER);
super.logStateChangeAction(action, dir, downX, downY, srcChildTargetType,
srcParentContainerType, dstContainerType, pageIndex);
}
public void logActionTip(int actionType, int viewType) {
LauncherLogProto.Action action = new LauncherLogProto.Action();
LauncherLogProto.Target target = new LauncherLogProto.Target();
switch(actionType) {
case VISIBLE:
action.type = LauncherLogProto.Action.Type.TIP;
target.type = LauncherLogProto.Target.Type.CONTAINER;
target.containerType = LauncherLogProto.ContainerType.TIP;
break;
case DISMISS:
action.type = LauncherLogProto.Action.Type.TOUCH;
action.touch = LauncherLogProto.Action.Touch.TAP;
target.type = LauncherLogProto.Target.Type.CONTROL;
target.controlType = CANCEL_TARGET;
break;
default:
Log.e(TAG, "Unexpected action type = " + actionType);
}
switch(viewType) {
case RECENTS_QUICK_SCRUB_ONBOARDING_TIP:
target.tipType = LauncherLogProto.TipType.QUICK_SCRUB_TEXT;
break;
case RECENTS_SWIPE_UP_ONBOARDING_TIP:
target.tipType = LauncherLogProto.TipType.SWIPE_UP_TEXT;
break;
default:
Log.e(TAG, "Unexpected viewType = " + viewType);
}
LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target);
dispatchUserEvent(event, null);
}
}

View File

@@ -1,63 +0,0 @@
/*
* Copyright (C) 2019 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.util;
import android.os.Binder;
import android.os.IBinder;
import android.os.Looper;
import android.util.Log;
import com.android.uiuios.config.FeatureFlags;
/**
* Utility class to test and check binder calls during development.
*/
public class BinderTracker {
private static final String TAG = "BinderTracker";
public static void start() {
if (!FeatureFlags.IS_DOGFOOD_BUILD) {
Log.wtf(TAG, "Accessing tracker in released code.", new Exception());
return;
}
Binder.setProxyTransactListener(new Tracker());
}
public static void stop() {
if (!FeatureFlags.IS_DOGFOOD_BUILD) {
Log.wtf(TAG, "Accessing tracker in released code.", new Exception());
return;
}
Binder.setProxyTransactListener(null);
}
private static class Tracker implements Binder.ProxyTransactListener {
@Override
public Object onTransactStarted(IBinder iBinder, int code) {
if (Looper.myLooper() == Looper.getMainLooper()) {
Log.e(TAG, "Binder call on ui thread", new Exception());
}
return null;
}
@Override
public void onTransactEnded(Object session) { }
}
}

View File

@@ -1,69 +0,0 @@
/*
* Copyright (C) 2019 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.util;
import static com.android.systemui.shared.system.InputChannelCompat.mergeMotionEvent;
import android.view.MotionEvent;
import java.util.ArrayList;
import java.util.function.Consumer;
/**
* Utility class to dispatch touch events to a different class. It stores the events locally
* until a valid dispatcher is available.
*/
public class CachedEventDispatcher {
private Consumer<MotionEvent> mConsumer;
private ArrayList<MotionEvent> mCache;
private MotionEvent mLastEvent;
public void dispatchEvent(MotionEvent event) {
if (mConsumer != null) {
mConsumer.accept(event);
} else {
if (mLastEvent == null || !mergeMotionEvent(event, mLastEvent)) {
// Queue event.
if (mCache == null) {
mCache = new ArrayList<>();
}
mLastEvent = MotionEvent.obtain(event);
mCache.add(mLastEvent);
}
}
}
public void setConsumer(Consumer<MotionEvent> consumer) {
if (consumer == null) {
return;
}
mConsumer = consumer;
int cacheCount = mCache == null ? 0 : mCache.size();
for (int i = 0; i < cacheCount; i++) {
MotionEvent ev = mCache.get(i);
mConsumer.accept(ev);
ev.recycle();
}
mCache = null;
mLastEvent = null;
}
public boolean hasConsumer() {
return mConsumer != null;
}
}

View File

@@ -1,123 +0,0 @@
/*
* Copyright (C) 2018 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.util;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Rect;
import androidx.annotation.AnyThread;
import androidx.annotation.IntDef;
import com.android.uiuios.DeviceProfile;
import com.android.uiuios.R;
import com.android.uiuios.config.FeatureFlags;
import java.lang.annotation.Retention;
public class LayoutUtils {
private static final int MULTI_WINDOW_STRATEGY_HALF_SCREEN = 1;
private static final int MULTI_WINDOW_STRATEGY_DEVICE_PROFILE = 2;
@Retention(SOURCE)
@IntDef({MULTI_WINDOW_STRATEGY_HALF_SCREEN, MULTI_WINDOW_STRATEGY_DEVICE_PROFILE})
private @interface MultiWindowStrategy {}
public static void calculateLauncherTaskSize(Context context, DeviceProfile dp, Rect outRect) {
float extraSpace;
if (dp.isVerticalBarLayout()) {
extraSpace = 0;
} else {
extraSpace = dp.hotseatBarSizePx + dp.verticalDragHandleSizePx;
}
calculateTaskSize(context, dp, extraSpace, MULTI_WINDOW_STRATEGY_HALF_SCREEN, outRect);
}
public static void calculateFallbackTaskSize(Context context, DeviceProfile dp, Rect outRect) {
calculateTaskSize(context, dp, 0, MULTI_WINDOW_STRATEGY_DEVICE_PROFILE, outRect);
}
@AnyThread
public static void calculateTaskSize(Context context, DeviceProfile dp,
float extraVerticalSpace, @MultiWindowStrategy int multiWindowStrategy, Rect outRect) {
float taskWidth, taskHeight, paddingHorz;
Resources res = context.getResources();
Rect insets = dp.getInsets();
if (dp.isMultiWindowMode) {
if (multiWindowStrategy == MULTI_WINDOW_STRATEGY_HALF_SCREEN) {
DeviceProfile fullDp = dp.getFullScreenProfile();
// Use availableWidthPx and availableHeightPx instead of widthPx and heightPx to
// account for system insets
taskWidth = fullDp.availableWidthPx;
taskHeight = fullDp.availableHeightPx;
float halfDividerSize = res.getDimension(R.dimen.multi_window_task_divider_size)
/ 2;
if (fullDp.isLandscape) {
taskWidth = taskWidth / 2 - halfDividerSize;
} else {
taskHeight = taskHeight / 2 - halfDividerSize;
}
} else {
// multiWindowStrategy == MULTI_WINDOW_STRATEGY_DEVICE_PROFILE
taskWidth = dp.widthPx;
taskHeight = dp.heightPx;
}
paddingHorz = res.getDimension(R.dimen.multi_window_task_card_horz_space);
} else {
taskWidth = dp.availableWidthPx;
taskHeight = dp.availableHeightPx;
paddingHorz = res.getDimension(dp.isVerticalBarLayout()
? R.dimen.landscape_task_card_horz_space
: R.dimen.portrait_task_card_horz_space);
}
float topIconMargin = res.getDimension(R.dimen.task_thumbnail_top_margin);
float paddingVert = res.getDimension(R.dimen.task_card_vert_space);
// Note this should be same as dp.availableWidthPx and dp.availableHeightPx unless
// we override the insets ourselves.
int launcherVisibleWidth = dp.widthPx - insets.left - insets.right;
int launcherVisibleHeight = dp.heightPx - insets.top - insets.bottom;
float availableHeight = launcherVisibleHeight
- topIconMargin - extraVerticalSpace - paddingVert;
float availableWidth = launcherVisibleWidth - paddingHorz;
float scale = Math.min(availableWidth / taskWidth, availableHeight / taskHeight);
float outWidth = scale * taskWidth;
float outHeight = scale * taskHeight;
// Center in the visible space
float x = insets.left + (launcherVisibleWidth - outWidth) / 2;
float y = insets.top + Math.max(topIconMargin,
(launcherVisibleHeight - extraVerticalSpace - outHeight) / 2);
outRect.set(Math.round(x), Math.round(y),
Math.round(x) + Math.round(outWidth), Math.round(y) + Math.round(outHeight));
}
public static int getShelfTrackingDistance(Context context, DeviceProfile dp) {
// Track the bottom of the window.
int shelfHeight = dp.hotseatBarSizePx + dp.getInsets().bottom;
int spaceBetweenShelfAndRecents = (int) context.getResources().getDimension(
R.dimen.task_card_vert_space);
return shelfHeight + spaceBetweenShelfAndRecents;
}
}

View File

@@ -1,198 +0,0 @@
/*
* Copyright (C) 2019 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.util;
import android.content.Context;
import android.content.res.Resources;
import android.view.MotionEvent;
import com.android.uiuios.Alarm;
import com.android.uiuios.R;
import com.android.uiuios.compat.AccessibilityManagerCompat;
/**
* Given positions along x- or y-axis, tracks velocity and acceleration and determines when there is
* a pause in motion.
*/
public class MotionPauseDetector {
// The percentage of the previous speed that determines whether this is a rapid deceleration.
// The bigger this number, the easier it is to trigger the first pause.
private static final float RAPID_DECELERATION_FACTOR = 0.6f;
/** If no motion is added for this amount of time, assume the motion has paused. */
private static final long FORCE_PAUSE_TIMEOUT = 300;
/**
* After {@link #makePauseHarderToTrigger()}, must
* move slowly for this long to trigger a pause.
*/
private static final long HARDER_TRIGGER_TIMEOUT = 400;
private final float mSpeedVerySlow;
private final float mSpeedSlow;
private final float mSpeedSomewhatFast;
private final float mSpeedFast;
private final Alarm mForcePauseTimeout;
private final boolean mMakePauseHarderToTrigger;
private final Context mContext;
private Long mPreviousTime = null;
private Float mPreviousPosition = null;
private Float mPreviousVelocity = null;
private Float mFirstPosition = null;
private OnMotionPauseListener mOnMotionPauseListener;
private boolean mIsPaused;
// Bias more for the first pause to make it feel extra responsive.
private boolean mHasEverBeenPaused;
/** @see #setDisallowPause(boolean) */
private boolean mDisallowPause;
// Time at which speed became < mSpeedSlow (only used if mMakePauseHarderToTrigger == true).
private long mSlowStartTime;
public MotionPauseDetector(Context context) {
this(context, false);
}
/**
* @param makePauseHarderToTrigger Used for gestures that require a more explicit pause.
*/
public MotionPauseDetector(Context context, boolean makePauseHarderToTrigger) {
mContext = context;
Resources res = context.getResources();
mSpeedVerySlow = res.getDimension(R.dimen.motion_pause_detector_speed_very_slow);
mSpeedSlow = res.getDimension(R.dimen.motion_pause_detector_speed_slow);
mSpeedSomewhatFast = res.getDimension(R.dimen.motion_pause_detector_speed_somewhat_fast);
mSpeedFast = res.getDimension(R.dimen.motion_pause_detector_speed_fast);
mForcePauseTimeout = new Alarm();
mForcePauseTimeout.setOnAlarmListener(alarm -> updatePaused(true /* isPaused */));
mMakePauseHarderToTrigger = makePauseHarderToTrigger;
}
/**
* Get callbacks for when motion pauses and resumes.
*/
public void setOnMotionPauseListener(OnMotionPauseListener listener) {
mOnMotionPauseListener = listener;
}
/**
* @param disallowPause If true, we will not detect any pauses until this is set to false again.
*/
public void setDisallowPause(boolean disallowPause) {
mDisallowPause = disallowPause;
updatePaused(mIsPaused);
}
/**
* Computes velocity and acceleration to determine whether the motion is paused.
* @param position The x or y component of the motion being tracked.
*
* TODO: Use historical positions as well, e.g. {@link MotionEvent#getHistoricalY(int, int)}.
*/
public void addPosition(float position, long time) {
if (mFirstPosition == null) {
mFirstPosition = position;
}
mForcePauseTimeout.setAlarm(mMakePauseHarderToTrigger
? HARDER_TRIGGER_TIMEOUT
: FORCE_PAUSE_TIMEOUT);
if (mPreviousTime != null && mPreviousPosition != null) {
long changeInTime = Math.max(1, time - mPreviousTime);
float changeInPosition = position - mPreviousPosition;
float velocity = changeInPosition / changeInTime;
if (mPreviousVelocity != null) {
checkMotionPaused(velocity, mPreviousVelocity, time);
}
mPreviousVelocity = velocity;
}
mPreviousTime = time;
mPreviousPosition = position;
}
private void checkMotionPaused(float velocity, float prevVelocity, long time) {
float speed = Math.abs(velocity);
float previousSpeed = Math.abs(prevVelocity);
boolean isPaused;
if (mIsPaused) {
// Continue to be paused until moving at a fast speed.
isPaused = speed < mSpeedFast || previousSpeed < mSpeedFast;
} else {
if (velocity < 0 != prevVelocity < 0) {
// We're just changing directions, not necessarily stopping.
isPaused = false;
} else {
isPaused = speed < mSpeedVerySlow && previousSpeed < mSpeedVerySlow;
if (!isPaused && !mHasEverBeenPaused) {
// We want to be more aggressive about detecting the first pause to ensure it
// feels as responsive as possible; getting two very slow speeds back to back
// takes too long, so also check for a rapid deceleration.
boolean isRapidDeceleration = speed < previousSpeed * RAPID_DECELERATION_FACTOR;
isPaused = isRapidDeceleration && speed < mSpeedSomewhatFast;
}
if (mMakePauseHarderToTrigger) {
if (speed < mSpeedSlow) {
if (mSlowStartTime == 0) {
mSlowStartTime = time;
}
isPaused = time - mSlowStartTime >= HARDER_TRIGGER_TIMEOUT;
} else {
mSlowStartTime = 0;
isPaused = false;
}
}
}
}
updatePaused(isPaused);
}
private void updatePaused(boolean isPaused) {
if (mDisallowPause) {
isPaused = false;
}
if (mIsPaused != isPaused) {
mIsPaused = isPaused;
if (mIsPaused) {
AccessibilityManagerCompat.sendPauseDetectedEventToTest(mContext);
mHasEverBeenPaused = true;
}
if (mOnMotionPauseListener != null) {
mOnMotionPauseListener.onMotionPauseChanged(mIsPaused);
}
}
}
public void clear() {
mPreviousTime = null;
mPreviousPosition = null;
mPreviousVelocity = null;
mFirstPosition = null;
setOnMotionPauseListener(null);
mIsPaused = mHasEverBeenPaused = false;
mSlowStartTime = 0;
mForcePauseTimeout.cancelAlarm();
}
public boolean isPaused() {
return mIsPaused;
}
public interface OnMotionPauseListener {
void onMotionPauseChanged(boolean isPaused);
}
}

View File

@@ -1,68 +0,0 @@
/*
* Copyright (C) 2018 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.util;
import android.animation.ValueAnimator;
import android.view.animation.Interpolator;
import java.util.ArrayList;
/**
* Utility class to update multiple values with different interpolators and durations during
* the same animation.
*/
public abstract class MultiValueUpdateListener implements ValueAnimator.AnimatorUpdateListener {
private final ArrayList<FloatProp> mAllProperties = new ArrayList<>();
@Override
public final void onAnimationUpdate(ValueAnimator animator) {
final float percent = animator.getAnimatedFraction();
final float currentPlayTime = percent * animator.getDuration();
for (int i = mAllProperties.size() - 1; i >= 0; i--) {
FloatProp prop = mAllProperties.get(i);
float time = Math.max(0, currentPlayTime - prop.mDelay);
float newPercent = Math.min(1f, time / prop.mDuration);
newPercent = prop.mInterpolator.getInterpolation(newPercent);
prop.value = prop.mEnd * newPercent + prop.mStart * (1 - newPercent);
}
onUpdate(percent);
}
public abstract void onUpdate(float percent);
public final class FloatProp {
public float value;
private final float mStart;
private final float mEnd;
private final float mDelay;
private final float mDuration;
private final Interpolator mInterpolator;
public FloatProp(float start, float end, float delay, float duration, Interpolator i) {
value = mStart = start;
mEnd = end;
mDelay = delay;
mDuration = duration;
mInterpolator = i;
mAllProperties.add(this);
}
}
}

View File

@@ -1,69 +0,0 @@
/*
* Copyright (C) 2018 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.util;
import android.animation.AnimatorSet;
import android.app.ActivityOptions;
import android.os.Handler;
import com.android.uiuios.LauncherAnimationRunner;
import com.android.systemui.shared.system.ActivityOptionsCompat;
import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.TransactionCompat;
@FunctionalInterface
public interface RemoteAnimationProvider {
static final int Z_BOOST_BASE = 800570000;
AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] targets);
default ActivityOptions toActivityOptions(Handler handler, long duration) {
LauncherAnimationRunner runner = new LauncherAnimationRunner(handler,
false /* startAtFrontOfQueue */) {
@Override
public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats,
AnimationResult result) {
result.setAnimation(createWindowAnimation(targetCompats));
}
};
return ActivityOptionsCompat.makeRemoteAnimation(
new RemoteAnimationAdapterCompat(runner, duration, 0));
}
/**
* Prepares the given {@param targets} for a remote animation, and should be called with the
* transaction from the first frame of animation.
*
* @param boostModeTargets The mode indicating which targets to boost in z-order above other
* targets.
*/
static void prepareTargetsForFirstFrame(RemoteAnimationTargetCompat[] targets,
TransactionCompat t, int boostModeTargets) {
for (RemoteAnimationTargetCompat target : targets) {
t.setLayer(target.leash, getLayer(target, boostModeTargets));
t.show(target.leash);
}
}
static int getLayer(RemoteAnimationTargetCompat target, int boostModeTarget) {
return target.mode == boostModeTarget
? Z_BOOST_BASE + target.prefixOrderIndex
: target.prefixOrderIndex;
}
}

View File

@@ -1,90 +0,0 @@
/*
* Copyright (C) 2018 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.util;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Queue;
/**
* Holds a collection of RemoteAnimationTargets, filtered by different properties.
*/
public class RemoteAnimationTargetSet {
private final Queue<SyncRtSurfaceTransactionApplierCompat> mDependentTransactionAppliers =
new ArrayDeque<>(1);
public final RemoteAnimationTargetCompat[] unfilteredApps;
public final RemoteAnimationTargetCompat[] apps;
public final int targetMode;
public final boolean hasRecents;
public RemoteAnimationTargetSet(RemoteAnimationTargetCompat[] apps, int targetMode) {
ArrayList<RemoteAnimationTargetCompat> filteredApps = new ArrayList<>();
boolean hasRecents = false;
if (apps != null) {
for (RemoteAnimationTargetCompat target : apps) {
if (target.mode == targetMode) {
filteredApps.add(target);
}
hasRecents |= target.activityType ==
RemoteAnimationTargetCompat.ACTIVITY_TYPE_RECENTS;
}
}
this.unfilteredApps = apps;
this.apps = filteredApps.toArray(new RemoteAnimationTargetCompat[filteredApps.size()]);
this.targetMode = targetMode;
this.hasRecents = hasRecents;
}
public RemoteAnimationTargetCompat findTask(int taskId) {
for (RemoteAnimationTargetCompat target : apps) {
if (target.taskId == taskId) {
return target;
}
}
return null;
}
public boolean isAnimatingHome() {
for (RemoteAnimationTargetCompat target : apps) {
if (target.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
return true;
}
}
return false;
}
public void addDependentTransactionApplier(SyncRtSurfaceTransactionApplierCompat delay) {
mDependentTransactionAppliers.add(delay);
}
public void release() {
SyncRtSurfaceTransactionApplierCompat applier = mDependentTransactionAppliers.poll();
if (applier == null) {
for (RemoteAnimationTargetCompat target : unfilteredApps) {
target.release();
}
} else {
applier.addAfterApplyCallback(this::release);
}
}
}

View File

@@ -1,53 +0,0 @@
/*
* Copyright (C) 2018 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.util;
import static com.android.quickstep.util.RemoteAnimationProvider.prepareTargetsForFirstFrame;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.TransactionCompat;
/**
* Animation listener which fades out the closing targets
*/
public class RemoteFadeOutAnimationListener implements AnimatorUpdateListener {
private final RemoteAnimationTargetSet mTarget;
private boolean mFirstFrame = true;
public RemoteFadeOutAnimationListener(RemoteAnimationTargetCompat[] targets) {
mTarget = new RemoteAnimationTargetSet(targets, MODE_CLOSING);
}
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
TransactionCompat t = new TransactionCompat();
if (mFirstFrame) {
prepareTargetsForFirstFrame(mTarget.unfilteredApps, t, MODE_CLOSING);
mFirstFrame = false;
}
float alpha = 1 - valueAnimator.getAnimatedFraction();
for (RemoteAnimationTargetCompat app : mTarget.apps) {
t.setAlpha(app.leash, alpha);
}
t.apply();
}
}

View File

@@ -1,263 +0,0 @@
/*
* Copyright (C) 2018 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.views;
import static com.android.uiuios.LauncherState.ALL_APPS_HEADER_EXTRA;
import static com.android.uiuios.LauncherState.BACKGROUND_APP;
import static com.android.uiuios.LauncherState.OVERVIEW;
import static com.android.uiuios.anim.Interpolators.ACCEL;
import static com.android.uiuios.anim.Interpolators.ACCEL_2;
import static com.android.uiuios.anim.Interpolators.LINEAR;
import static com.android.uiuios.icons.GraphicsUtils.setColorAlphaBound;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Path.Direction;
import android.graphics.Path.Op;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.animation.Interpolator;
import com.android.uiuios.DeviceProfile;
import com.android.uiuios.R;
import com.android.uiuios.Utilities;
import com.android.uiuios.anim.Interpolators;
import com.android.uiuios.uioverrides.states.OverviewState;
import com.android.uiuios.util.Themes;
import com.android.uiuios.views.ScrimView;
import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
/**
* Scrim used for all-apps and shelf in Overview
* In transposed layout, it behaves as a simple color scrim.
* In portrait layout, it draws a rounded rect such that
* From normal state to overview state, the shelf just fades in and does not move
* From overview state to all-apps state the shelf moves up and fades in to cover the screen
*/
public class ShelfScrimView extends ScrimView implements NavigationModeChangeListener {
// If the progress is more than this, shelf follows the finger, otherwise it moves faster to
// cover the whole screen
private static final float SCRIM_CATCHUP_THRESHOLD = 0.2f;
// Temporarily needed until android.R.attr.bottomDialogCornerRadius becomes public
private static final float BOTTOM_CORNER_RADIUS_RATIO = 2f;
// In transposed layout, we simply draw a flat color.
private boolean mDrawingFlatColor;
// For shelf mode
private final int mEndAlpha;
private final float mRadius;
private final int mMaxScrimAlpha;
private final Paint mPaint;
// Mid point where the alpha changes
private int mMidAlpha;
private float mMidProgress;
private Interpolator mBeforeMidProgressColorInterpolator = ACCEL;
private Interpolator mAfterMidProgressColorInterpolator = ACCEL;
private float mShiftRange;
private final float mShelfOffset;
private float mTopOffset;
private float mShelfTop;
private float mShelfTopAtThreshold;
private int mShelfColor;
private int mRemainingScreenColor;
private final Path mTempPath = new Path();
private final Path mRemainingScreenPath = new Path();
private boolean mRemainingScreenPathValid = false;
private Mode mSysUINavigationMode;
public ShelfScrimView(Context context, AttributeSet attrs) {
super(context, attrs);
mMaxScrimAlpha = Math.round(OVERVIEW.getWorkspaceScrimAlpha(mLauncher) * 255);
mEndAlpha = Color.alpha(mEndScrim);
mRadius = BOTTOM_CORNER_RADIUS_RATIO * Themes.getDialogCornerRadius(context);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mShelfOffset = context.getResources().getDimension(R.dimen.shelf_surface_offset);
// Just assume the easiest UI for now, until we have the proper layout information.
mDrawingFlatColor = true;
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mRemainingScreenPathValid = false;
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
onNavigationModeChanged(SysUINavigationMode.INSTANCE.get(getContext())
.addModeChangeListener(this));
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
SysUINavigationMode.INSTANCE.get(getContext()).removeModeChangeListener(this);
}
@Override
public void onNavigationModeChanged(Mode newMode) {
mSysUINavigationMode = newMode;
// Note that these interpolators are inverted because progress goes 1 to 0.
if (mSysUINavigationMode == Mode.NO_BUTTON) {
// Show the shelf more quickly before reaching overview progress.
mBeforeMidProgressColorInterpolator = ACCEL_2;
mAfterMidProgressColorInterpolator = ACCEL;
} else {
mBeforeMidProgressColorInterpolator = ACCEL;
mAfterMidProgressColorInterpolator = Interpolators.clampToProgress(ACCEL, 0.5f, 1f);
}
}
@Override
public void reInitUi() {
DeviceProfile dp = mLauncher.getDeviceProfile();
mDrawingFlatColor = dp.isVerticalBarLayout();
if (!mDrawingFlatColor) {
mRemainingScreenPathValid = false;
mShiftRange = mLauncher.getAllAppsController().getShiftRange();
if ((OVERVIEW.getVisibleElements(mLauncher) & ALL_APPS_HEADER_EXTRA) == 0) {
mMidProgress = 1;
mMidAlpha = 0;
} else {
mMidAlpha = Themes.getAttrInteger(getContext(), R.attr.allAppsInterimScrimAlpha);
Rect hotseatPadding = dp.getHotseatLayoutPadding();
int hotseatSize = dp.hotseatBarSizePx + dp.getInsets().bottom
- hotseatPadding.bottom - hotseatPadding.top;
float arrowTop = Math.min(hotseatSize, OverviewState.getDefaultSwipeHeight(dp));
mMidProgress = 1 - (arrowTop / mShiftRange);
}
mTopOffset = dp.getInsets().top - mShelfOffset;
mShelfTopAtThreshold = mShiftRange * SCRIM_CATCHUP_THRESHOLD + mTopOffset;
}
updateColors();
updateDragHandleAlpha();
invalidate();
}
@Override
public void updateColors() {
super.updateColors();
if (mDrawingFlatColor) {
mDragHandleOffset = 0;
return;
}
mDragHandleOffset = mShelfOffset - mDragHandleSize;
if (mProgress >= SCRIM_CATCHUP_THRESHOLD) {
mShelfTop = mShiftRange * mProgress + mTopOffset;
} else {
mShelfTop = Utilities.mapRange(mProgress / SCRIM_CATCHUP_THRESHOLD, -mRadius,
mShelfTopAtThreshold);
}
if (mProgress >= 1) {
mRemainingScreenColor = 0;
mShelfColor = 0;
if (mSysUINavigationMode == Mode.NO_BUTTON
&& mLauncher.getStateManager().getState() == BACKGROUND_APP) {
// Show the shelf background when peeking during swipe up.
mShelfColor = setColorAlphaBound(mEndScrim, mMidAlpha);
}
} else if (mProgress >= mMidProgress) {
mRemainingScreenColor = 0;
int alpha = Math.round(Utilities.mapToRange(
mProgress, mMidProgress, 1, mMidAlpha, 0, mBeforeMidProgressColorInterpolator));
mShelfColor = setColorAlphaBound(mEndScrim, alpha);
} else {
mDragHandleOffset += mShiftRange * (mMidProgress - mProgress);
// Note that these ranges and interpolators are inverted because progress goes 1 to 0.
int alpha = Math.round(
Utilities.mapToRange(mProgress, (float) 0, mMidProgress, (float) mEndAlpha,
(float) mMidAlpha, mAfterMidProgressColorInterpolator));
mShelfColor = setColorAlphaBound(mEndScrim, alpha);
int remainingScrimAlpha = Math.round(
Utilities.mapToRange(mProgress, (float) 0, mMidProgress, mMaxScrimAlpha,
(float) 0, LINEAR));
mRemainingScreenColor = setColorAlphaBound(mScrimColor, remainingScrimAlpha);
}
}
@Override
protected void onDraw(Canvas canvas) {
drawBackground(canvas);
drawDragHandle(canvas);
}
private void drawBackground(Canvas canvas) {
if (mDrawingFlatColor) {
if (mCurrentFlatColor != 0) {
canvas.drawColor(mCurrentFlatColor);
}
return;
}
if (Color.alpha(mShelfColor) == 0) {
return;
} else if (mProgress <= 0) {
canvas.drawColor(mShelfColor);
return;
}
int height = getHeight();
int width = getWidth();
// Draw the scrim over the remaining screen if needed.
if (mRemainingScreenColor != 0) {
if (!mRemainingScreenPathValid) {
mTempPath.reset();
// Using a arbitrary '+10' in the bottom to avoid any left-overs at the
// corners due to rounding issues.
mTempPath.addRoundRect(0, height - mRadius, width, height + mRadius + 10,
mRadius, mRadius, Direction.CW);
mRemainingScreenPath.reset();
mRemainingScreenPath.addRect(0, 0, width, height, Direction.CW);
mRemainingScreenPath.op(mTempPath, Op.DIFFERENCE);
}
float offset = height - mRadius - mShelfTop;
canvas.translate(0, -offset);
mPaint.setColor(mRemainingScreenColor);
canvas.drawPath(mRemainingScreenPath, mPaint);
canvas.translate(0, offset);
}
mPaint.setColor(mShelfColor);
canvas.drawRoundRect(0, mShelfTop, width, height + mRadius, mRadius, mRadius, mPaint);
}
}